Makalele
Makalele

Reputation: 7551

UIScrollView's contentOffset gets truncated when adding small number

I have swift 2.0 ios app with storyboard. I have a tableViewController and in each row I have scrollView (with user scrolling disabled). Inside this scrollView I have an image, which fits scroll's size and it's a bit taller. Thing is I want to achieve "floating" effect on this image, when tableView is scrolled. Here's a code:

    override func scrollViewDidScroll(scrollView: UIScrollView) {
        let cells = tableView.visibleCells

        let offsetY = scrollView.contentOffset.y

        var diff:CGFloat = (offsetY - lastY) / 10

        for cell in cells {
            let indexPath = tableView.indexPathForCell(cell)!

            let scroll = cell.viewWithTag(102) as! UIScrollView
            let oldY = scroll.contentOffset.y
            let newY = oldY + diff
            let height = scroll.contentSize.height
            if(newY >= 0 && newY + cellHeight < height){
                scroll.contentOffset = CGPointMake(scroll.contentOffset.x, newY)
            }

            if(indexPath.row == 0){
                print("\(oldY) + \(diff) = \(newY)")
            }
        }

        lastY = offsetY
    }

What I'm doing here is calculating difference of tableView scroll position (variable "diff"), get all visible cells and scroll scrollView's by that difference (divided by 10 and in opposite direction). When I scroll fast it's somehow working, but when I scroll very slowly nothing is moving.

When I'm slowly scrolling I'm getting output like this (last print):

15.5 + 0.05 = 15.55
15.5 + 0.05 = 15.55
15.5 + 0.05 = 15.55
15.5 + 0.05 = 15.55
15.5 + 0.05 = 15.55
15.5 + 0.05 = 15.55
15.5 + 0.05 = 15.55

When diff is too small scrollView is somehow truncating (or rounding) a result.

When I'm scrolling faster:

23.0 + 0.25 = 23.25
23.5 + 0.25 = 23.75
24.0 + 0.15 = 24.15
24.0 + 0.25 = 24.25
24.5 + 0.25 = 24.75
25.0 + 0.25 = 25.25
25.5 + 0.25 = 25.75

Here you can see it's suming up, but some results does not match (sum of line x should be first number in the next line).

It's not an issue with CGFloat as simple example shows:

var sum:CGFloat = 0

for(var i = 0; i < 10; i++){
   let number:CGFloat = 0.05
   sum += number
   print(sum)
}

Results in:

0.05
0.1
0.15
0.2
0.25
0.3
0.35
0.4
0.45
0.5

How can I fix it? Maybe I should use a different approach to get similar effect?

Upvotes: 1

Views: 686

Answers (2)

Lauri Nurmi
Lauri Nurmi

Reputation: 591

Apparently one can also override the contentOffset property in the custom subclass like this:

var unrounded: CGPoint?
override var contentOffset: CGPoint {
    get {
        return unrounded ?? .zero
    }
    set {
        unrounded = newValue
        super.contentOffset = newValue
    }
}

This has the advantage that it also works when calling setContentOffset(_:animated:), not only when assigning to contentOffset.

Upvotes: 0

glyuck
glyuck

Reputation: 3397

UIScrollView.contentOffset is rounded to 0.5 on retina displays (0.33 on iPhone 6+) when set.

I suggest creating custom UITableViewCell subclass (if you don't have it yet) and create custom variable inside it:

class MyCell: UITableViewCell {
    var unroundedContentOffsetY: CGFloat {
        didSet {
            scroll.contentOffset = CGPoint(x: scroll.contentOffset.x, unroundedContentOffsetY)
        }
    }
}

Then make calculations on cell.unroundedContentOffsetY variable instead of cell.scroll.contentOffset

Upvotes: 5

Related Questions