Home > OS >  UIScrollView contentOffset value marginally wrong
UIScrollView contentOffset value marginally wrong

Time:10-24

I have a scrollView in my ViewController. The scrollView is placed vertically centred, its width is equal to the width of ViewController and height is equal to 100. To make the scrollView scrollable, I set an arbitrary contentSize to this scrollView, let's say it is (500, 100). This helps my scrollView to scroll horizontally.

My requirement is that I want the scrollView's content to start from horizontal centre of scrollView. Hence, I set contentInset value on it.

let halfWidth = scrollView.bounds.size.width / 2.0
scrollView.contentInset = UIEdgeInsets(top: 0, left: halfWidth, bottom: 0, right: halfWidth)
scrollView.contentOffset = CGPoint(x: -halfWidth, y: 0)

Remember, my scrollView width is equal to ViewController width. Hence, the halfWidth value from above code will be same as view.bounds.width / 2.0.

Where it works

In iPhone 12 Pro, data values look like following:

  • ViewController / Screen / scrollView width = 390
  • halfWidth = 195
  • scrollView.contentOffset.x = -195

All the above values turn out to be absolutely correct as per calculations we have in code.

Where it doesn't work

In iPhone 12 mini, data values look like following:

  • ViewController / Screen / scrollView width = 375
  • halfWidth = 187.5
  • scrollView.contentOffset.x = -187.33333333333334

The value of scrollView.contentOffset.x was expected to be -187.5, but in reality it is -187.33333333333334.

Further observation with all other devices

On observing the pattern, I realised that this marginal error in contentOffset value is appearing on specific devices which meet following two conditions:

  • Condition 1: Device width is an odd number.
  • Condition 2: Device scale factor is odd number.

Example devices to reproduce error - iPhone 12 mini, 11 Pro, X, XS. All these devices have screen width 375 and scale factor 3.

Why does precision matter

My usecase is that I am creating a custom slider using UICollectionView which is responsible to emit the value / fraction of sliding based on the amount of scroll that has taken place. In the question details that I have mentioned above, the scenario is for the case where no scrolling has taken place and hence my expectation of the value / fraction emitted is 0. But, because of this marginal error, the value turns out to be non-zero every time the user tries to scroll it to the beginning.

Is this a known bug? Is there any work around for this?

I have created an example project for quick debugging. You can clone it from here.

CodePudding user response:

The OS is going to place your control on an even pixel boundary.

You are positioning your control in the UI Space. So the system is going to translate the coordinates you provide to pixel space, set the position of the control (on a pixel boundary), but return the position to you in UI Space.

So it takes your offset in UI space and converts it to pixel space:

offset * sceen scale = pixel offset

187.5 * 3 = 562.5 pixels

But pixels are real, physical things. There's no such thing as half pixel. So the system rounds off and the pixel offset becomes 562 pixels.

When you translate that offset back to UI space

pixel offset / screen scale = ui space offset

562 / 3 = 187.3333...

That's why the system gives you back that value.

  • Related