如何正确地动画UIScrollView contentOffset [英] How to properly animate UIScrollView contentOffset

查看:1161
本文介绍了如何正确地动画UIScrollView contentOffset的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有UIScrollView子类。它的内容是可重复使用的 - 大约4或5个视图用于显示数百个元素(滚动隐藏对象重复使用,并跳转到另一个位置,当它需要看到它们)



我需要:能够自动将我的滚动视图滚动到任何位置。例如,我的滚动视图显示第4,第5和第6个元素,当我点击一些按钮,它需要滚动到第30个元素。换句话说,我需要UIScrollView的标准行为



这很好:

  [self setContentOffset:CGPointMake(index * elementWidth,0)animated:YES]; 

但我需要一些自定义。例如,更改动画持续时间,添加一些代码以在动画结束时执行。



显而易见的决定



[UIView animateWithDuration:3 delay:0 options:UIViewAnimationOptionBeginFromCurrentState animations:^ {
[self setContentOffset:CGPointMake(index * elementWidth,0)
} completion:^(BOOL finished){
// some code
}];

但我有一些动作连接到滚动事件,所以现在所有的都在动画块和它会导致所有subview的框架动画(感谢几个可重复使用的元素,所有的动画都不是我想要的)



问题是:我可以进行自定义动画(实际上我需要自定义持续时间,动作结束和BeginFromCurrentState选项)内容偏移 WITHOUT 动画所有的代码,连接到 scrollViewDidScroll event?

UPD:
感谢 Andrew的回答(第一部分)我解决了 scrollViewDidScroll 中的动画问题:

   - (void)scrollViewDidScroll:(UIScrollView *)scrollView {
[UIView performWithoutAnimation:^ {
[self refreshTiles]
}];
}

但是 scrollViewDidScroll (为我的目的)执行动画的每一帧,就像在

  [self setContentOffset:CGPointMake(index * elementWidth, 0)animated:YES]; 

但是,现在只在动画开始时执行一次。



我如何解决这个问题?

解决方案

code> scrollViewDidScroll ?



在iOS 7上,您可以尝试在 scrollViewDidScroll in

  [UIView performWithoutAnimation:^ {
// Your code here
} ;

在以前的iOS版本中,您可以尝试:

  [CATransaction begin]; 
[CATransaction setDisableActions:YES];
//你的代码在这里
[CATransaction commit];

更新



不幸的是,你碰到了整个事情的艰难部分。 setContentOffset:只调用一次代理,它等效于 setContentOffset:animated:NO ,它再次调用它一次。



setContentOffset:animated:YES 当动画改变scrollview的边界时调用委托,但是你不想要提供的动画,所以我可以想出的唯一方法是逐渐更改scrollview的 contentOffset ,以便动画系统



为了做到这一点,你可以看看关键帧动画,像这样的iOS 7:

/ p>

  [UIView animateKeyframesWithDuration:duration delay:delay options:options animations:^ {
[UIView addKeyframeWithRelativeStartTime:0.0 relativeDuration:0.5动画:^ {
[self setContentOffset:CGPointMake(floorf(index / 2)* elementWidth,0)];
}];
[UIView addKeyframeWithRelativeStartTime:0.5 relativeDuration:0.5 animations:^ {
[self setContentOffset:CGPointMake(index * elementWidth,0)
}];
} completion:^(BOOL finished){
//完成块
}];

这将给你两个更新,当然你可以使用一些数学和一个循环,



在以前的iOS版本中,你必须删除关键帧动画的CoreAnimation,但是它基本上是一样的不同的语法。



方法2:
您可以尝试通过在动画开始时启动的计时器轮询scrollview的presentationLayer,因为不幸的是presentationLayer的属性不是KVO observable。或者你可以在图层的子类中使用 needsDisplayForKey ,以便在边界发生变化时获得通知,但这需要一些工作来设置,并且会导致重绘,影响性能。



方法3:
当动画是YES的时候会正确解释scrollView的效果try和拦截在scrollview上设置的动画并改变它的参数,但由于这是最黑的,可破坏的,由于苹果的变化和最棘手的方法,我不会去。


I have UIScrollView subclass. Its content is reusable - about 4 or 5 views are used to display hundreds of elements (while scrolling hidden objects reused and jumps to another position when its needed to see them)

What i need: ability to automatically scroll my scroll view to any position. For example my scroll view displays 4th, 5th and 6th element and when I tap some button it needs to scroll to 30th element. In other words I need standard behaviour of UIScrollView.

This works fine:

[self setContentOffset:CGPointMake(index*elementWidth, 0) animated:YES];

but I need some customisation. For example, change animation duration, add some code to perform on end of animation.

Obvious decision:

[UIView animateWithDuration:3 delay:0 options:UIViewAnimationOptionBeginFromCurrentState animations:^{
    [self setContentOffset:CGPointMake(index*elementWidth, 0)];
} completion:^(BOOL finished) {
    //some code
}];

but I have some actions connected to scroll event, and so now all of them are in animation block and it causes all subview's frames to animate too (thanks to few reusable elements all of them animates not how i want)

The question is: How can I make custom animation (in fact I need custom duration, actions on end and BeginFromCurrentState option) for content offset WITHOUT animating all the code, connected to scrollViewDidScroll event?

UPD: Thanks to Andrew's answer(first part) I solved issue with animation inside scrollViewDidScroll:

- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
    [UIView performWithoutAnimation:^{
        [self refreshTiles];
    }];
}

But scrollViewDidScroll must (for my purposes) executes every frame of animation like it was in case of

[self setContentOffset:CGPointMake(index*elementWidth, 0) animated:YES];

However, now it executes only once at start of animation.

How can I solve this?

解决方案

Did you try the same approach, but with disabled animation in scrollViewDidScroll ?

On iOS 7, you could try wrapping your code in scrollViewDidScroll in

[UIView performWithoutAnimation:^{
       //Your code here
    }];

on previous iOS versions, you could try:

  [CATransaction begin];
    [CATransaction setDisableActions:YES];
     //Your code here
    [CATransaction commit];

Update:

Unfortunately that's where you hit the tough part of the whole thing. setContentOffset: calls the delegate just once, it's equivalent to setContentOffset:animated:NO, which again calls it just once.

setContentOffset:animated:YES calls the delegate as the animation changes the bounds of the scrollview and you want that, but you don't want the provided animation, so the only way around this that I can come up with is to gradually change the contentOffset of the scrollview, so that the animation system doesn't just jump to the final value, as is the case at the moment.

To do that you can look at keyframe animations, like so for iOS 7:

[UIView animateKeyframesWithDuration:duration delay:delay options:options animations:^{
    [UIView addKeyframeWithRelativeStartTime:0.0 relativeDuration:0.5 animations:^{
       [self setContentOffset:CGPointMake(floorf(index/2) * elementWidth, 0)];
    }];
    [UIView addKeyframeWithRelativeStartTime:0.5 relativeDuration:0.5 animations:^{
        [self setContentOffset:CGPointMake(index*elementWidth, 0)];
    }];
} completion:^(BOOL finished) {
    //Completion Block
}];

This will get you two updates and of course you could use some math and a loop to add up a lot more of these with the appropriate timings.

On previous iOS versions, you'll have to drop to CoreAnimation for keyframe animations, but it's basically the same thing with a bit different syntax.

Method 2: You can try polling the presentationLayer of the scrollview for any changes with a timer that you start at the beginning of the animation, since unfortunately the presentationLayer's properties aren't KVO observable. Or you can use needsDisplayForKey in a subclass of the layer to get notified when the bounds change, but that'll require some work to set up and it does cause redrawing, which might affect performance.

Method 3: Would be to dissect exactly what happens to the scrollView when animated is YES try and intercept the animation that gets set on the scrollview and change its parameters, but since this would be the most hacky, breakable due to Apple's changes and trickiest method, I won't go into it.

这篇关于如何正确地动画UIScrollView contentOffset的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆