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

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

问题描述

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

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)

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

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.

这很好用:

[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.

明显的决定:

[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)

问题是:如何为内容偏移制作自定义动画(实际上我需要自定义持续时间、结束动作和 BeginFromCurrentState 选项)WITHOUT动画所有代码, 连接到 scrollViewDidScroll 事件?

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:感谢 Andrew 的回答(第一部分)我解决了 scrollViewDidScroll 中的动画问题:

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

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

但是 scrollViewDidScroll 必须(为了我的目的)像在这种情况下一样执行每一帧动画

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?

推荐答案

您是否尝试过相同的方法,但在 scrollViewDidScroll 中禁用了动画?

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

在 iOS 7 上,您可以尝试将代码包装在 scrollViewDidScroll

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

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

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

on previous iOS versions, you could try:

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

更新:

不幸的是,这就是您遇到整个事情的艰难部分的地方.setContentOffset: 只调用一次委托,它等价于 setContentOffset:animated:NO,再次调用它一次.

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 调用委托,因为动画改变了滚动视图的边界,而你想要它,但你不想要提供的动画,所以唯一的方法是解决这个问题,我可以想出的就是逐渐改变scrollview的contentOffset,这样动画系统就不会像现在这样直接跳转到最终值了.

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.

为此,您可以查看关键帧动画,例如 iOS 7:

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.

在以前的 iOS 版本中,您必须使用 CoreAnimation 来制作关键帧动画,但基本上是一样的,只是语法略有不同.

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.

方法二:您可以尝试使用在动画开始时启动的计时器轮询滚动视图的presentationLayer 以获取任何更改,因为不幸的是presentationLayer 的属性不是KVO 可观察的.或者,您可以在图层的子类中使用 needsDisplayForKey 以在边界更改时获得通知,但这需要进行一些设置,并且会导致重绘,这可能会影响性能.

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.

方法三:当动画为 YES 尝试拦截在滚动视图上设置的动画并更改其参数时,将准确剖析 scrollView 发生的情况,但是由于 Apple 的更改和最棘手的方法,这将是最笨拙、易破解的,我赢了不进去.

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天全站免登陆