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.