I have a subclass of UIScrollView that I'm using for images slideshow, with infinite scrolling and circular slideshow.

I used to animate the transition in this way: (Because I wanted the transition to be slower)

[UIView animateWithDuration:1.0 
                      delay:0 options:(UIViewAnimationCurveEaseOut)
                 animations:^{
                         self.scrollView.contentOffset = newOffset;}
                 completion:NULL];

And it worked just fine.

Then I watched the lecture "Advanced Scrolling Techniques" from WWDC 2011, and they recommend to implement infinite scrolling by overriding layoutSubviews.

So I changed my implementation and override layoutSubviews

Once I did that the transition animation stopped working.

If I comment out my custom layoutSubviews - It's working again!

Why??

What can I do to make my own scrolling animation while overriding layoutSubviews?

Thanks!

有帮助吗?

解决方案

OK, I think I found the problem, and a possible fix.

The key to understand the problem is understanding that when you call

[UIView animateWithDuration...]

All the properties that you change inside the animation block, are changed immediately. They do not change as the animation actually executing (visually).

So in my case, I'm animating the contentOffset to my target value. This operation invokes layoutSubviews immediately.

In my implementation of layoutSubviews I'm checking to see if enough movement has been accumulated. If so - I'm rearranging my ImageViews, and set the contentOffset to its initial value. Once I do that there is nothing to animate anymore, because nothing has changed!

  1. Animate contentOffset to X:
    • calls setContentOffset to X:
      • invokes layoutSubview
        • Sets the contentOffset back to Y! (after rearranging the views)
    • Nothing to animate at this point...

Possible Fix

I'm not sure if it is the right way to do it, but its working working. So what we need to do is to ensure that no changes will be made in layoutSubviews while we are in the middle of animation block.

So I created a BOOL instance variable named isAnimating, and set it appropriately in the animation block:

self.isAnimating = YES;
[UIView animateWithDuration:self.transitionDuration 
                      delay:0 
                    options:(UIViewAnimationOptionCurveEaseOut) 
                 animations:^{
    self.contentOffset = newOffset;
}

                 completion:^(BOOL finished) {
        self.isAnimating = NO;
        [self setNeedsLayout];
}];

Now in layoutSubviews we need to take isAnimating into account:

-(void) layoutSubviews
{
    [super layoutSubviews];
    if (self.isAnimating == NO)
    {
        [self rearrangeIfNecessary];
    }
}

And it's working again!

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top