简体   繁体   中英

UIScrollView contentOffset not updating when changed

Seeing an odd issue surrounding changing a scroll view's content offset property.

I have a CADisplayLink that calls a method every frame. This method calculates how much to adjust the content offset by to produce an auto scroll type effect.

@objc private func tick() {

    let fps = 1 / (displayLink.targetTimestamp - displayLink.timestamp)

    let relativeAutoscrollAmount = autoscrollAmount / CGFloat(fps)

    scrollView.contentOffset.x += relativeAutoscrollAmount
}

autoscrollAmount is a CGFloat property that represents how many pixels to move each second. On a 60Hz screen like an iPhone, this would mean a shift of 5/60 per invocation of that method, if this property is 5. However, the content offset never actually changes! Either visually or in memory, I can break and inspect it at any time and it's always 0!

Note that if I adjust it by 1 or greater each time, it works just fine. The animation is far to quick doing this, though.

Any thoughts?

EDIT: Obviously you can't actually adjust by less than a pixel at a time, but when I was doing this previously with a constraint constant, the system just calculated how to deal with this. (I assume by only moving every few ticks).

I believe I have the answer, or at the very least, an explanation based on a theory backed by some pretty good evidence. Here we go...

In the question, I provided an example of 5/60, where 5 is the amount of pixels to move per second, and 60 is the refresh rate of my screen. This comes out at approximately 0.083, which, as I said, caused no updates to contentOffset to take place.

At this point, I assumed that the minimum value was 1 (as you can't make changes to half a pixel) but this is in fact not the case. I began experimenting with different decimal values, in the hope of finding the threshold at which the updates to contentOffset stop taking place.

After a lot of trial and error, I found that value. It is 0.167 . In my head, this had absolutely no significance whatsoever; but there obviously had to be something so I set about manipulating it in various ways to try to observe a pattern of some kind.

It soon became clear that 0.167 * 6 == 1 , which although an interesting observation, again seemed to have little significance. That is until you note that the refresh rate of the display on my iPhone X that I was testing with is 60Hz , 10 times 6 . At this point, I'm still stabbing blindly in the dark but this was at least a lead that I could explore a bit.

From this, I speculated that the system evaluates changes in layer's positions either every 6ms, or, perhaps more realistically, 10 times per display cycle. This supports the behaviour I am seeing in so far as if the movement value passes is too small (IE it cannot be represented in this 10 times per display cycle theorm), it is simply ignored.

This is quite a bold speculation so I decided to see if I could gather evidence to support the theory. I fired up my iPad pro which has a 120Hz display (as opposed to the 60Hz display on my iPhone X) to see if there was a trend. Sure enough, there was. The minimum value to see movement was now half what it was on the 60Hz screen . Given the greater refresh rate (double, in fact), and the original assumption of 10 updates per screen cycle, I am now seeing 20 updates per screen cycle, every 6ms , as before. There's definitely a relationship here.

Now I'd like to stress that this is all purely speculation, but at least I can sleep tonight having a good idea as to why this is happening! I'd love to hear other's thoughts, too.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM