循环滚动UIScrollView,但保持减速状态

4
我已经设置了一个无限滚动视图,当它到达0内容偏移时,我将其设置为最大内容偏移,反之亦然。
例如。
[scrollView setContentOffset:CGPointMake(0,0) animated:NO];

这个方法可以工作,但是会停止UIScrollView的减速。

有没有一种方法可以做到这一点,同时保持UIScrollView的减速?

我尝试过这个...

float declerationRate = scrollView.decelerationRate;
[scrollView setContentOffset:CGPointMake(scrollView.frame.size.width, 0) animated:NO];
scrollView.decelerationRate = declerationRate;

但是它没有起作用。

编辑

我刚刚意识到,decelerationRate是设置决定减速有多慢/快的参数。

我需要从scrollView中获取当前速度。


当你将内容偏移量设置回顶部(0)时,滚动不再移动是很正常的。重新设置减速率也无法帮助scrollview知道应该往哪个方向滚动。 - Daniel
有没有办法在移动物体之前获取其速度,然后再次应用它?我知道这是正常行为,但我不想要正常行为。 - Fogmeister
更新我的答案,你可以使用KVO来计算速度,以便从顶部继续你的动画。 - Daniel
4个回答

3

我需要稍微调整一下这个想法。

结果尝试设置UIScrollView的速度是困难的......非常困难。

所以,无论如何,我对它进行了微调。

其实,这是回答别人的SO问题后的一个小项目,我想自己解决它。

我想创建一个旋转器应用程序,可以轻扫以使箭头旋转并减速到一个点。

我的做法是设置一个UIImageView,箭头指向上方。

然后覆盖UIImageView的是一个UIScrollView。

然后在代码中......

@interface MyViewController () <UIScrollViewDelegate>

@property (nonatomic, weak) IBOutlet UIScrollView *scrollView;
@property (nonatomic, weak) IBOutlet UIImageView *arrowView;

@end

@implementation MyViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.

    //make the content size really big so that the targetOffset of the deceleration will never be met.
    self.scrollView.contentSize = CGSizeMake(self.scrollView.frame.size.width * 100, self.scrollView.frame.size.height);
    //set the contentOffset of the scroll view to a point in the center of the contentSize.
    [self.scrollView setContentOffset:CGPointMake(self.scrollView.frame.size.width * 50, 0) animated:NO];
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

- (void)rotateImageView
{
    //Calculate the percentage of one "frame" that is the current offset.

    // each percentage of a "frame" equates to a percentage of 2 PI Rads to rotate
    float minOffset = self.scrollView.frame.size.width * 50;
    float maxOffset = self.scrollView.frame.size.width * 51;

    float offsetDiff = maxOffset - minOffset;

    float currentOffset = self.scrollView.contentOffset.x - minOffset;

    float percentage = currentOffset / offsetDiff;

    self.arrowView.transform = CGAffineTransformMakeRotation(M_PI * 2 * percentage);
}

- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
    //the scrollView moved so update the rotation of the image
    [self rotateImageView];
}

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
    //the scrollview stopped moving.
    //set the content offset back to be in the middle
    //but make sure to keep the percentage (used above) the same
    //this ensures the arrow is pointing in the same direction as it ended decelerating

    float diffOffset = scrollView.contentOffset.x;

    while (diffOffset >= scrollView.frame.size.width) {
        diffOffset -= scrollView.frame.size.width;
    }

    [scrollView setContentOffset:CGPointMake(scrollView.frame.size.width * 50 + diffOffset, 0) animated:NO];
}

@end

这可以实现类似于“幸运轮”的无限循环旋转的效果。

不过,这种方法存在一个缺陷。如果用户不让它停止地不停旋转,那么它只会在任一方向上旋转50次,然后停止。


1
正如我在评论中所说的那样,你现在的方法可以产生预期的结果,想要实现你想要的效果之一是将内容偏移设置为顶部,然后通过使用内容大小高度和减速值,再次动画化内容偏移,可以查看这个答案:https://dev59.com/mlfUa4cB1Zd3GeqPIHFN#6086521 在感觉正确之前,你需要玩弄一些数学,但我认为这是一个合理的解决方法。
减速越低,动画时间越长,动画距离越远。让我知道你的想法。
正如你所说,减速可能不是你唯一需要的东西。因此,你可以尝试在contentOffset上使用KVO来计算半秒钟内的平均速度,也许可以获得速度的概念。

谢谢,我决定改变策略并找到了另一种方法来完成它。它不像我想要的那么优雅,但它可以工作。我的代码在我的回答中。 - Fogmeister

0

我发现如果你调用

[scrollView setContentOffset:CGPointMake(0,0) animated:NO];

在点(0,0)处,它将触发UIScrollView的反弹动作,然后减速将停止。当您调用setContentOffset到内容的边界时,情况也是如此。

因此,您可以调用setContentOffset到点(10,10)或其他位置,以轻松保持减速。


0

这个问题可能有几种不同的解决方法。理想情况下,您不必进行任何类型的计算来模拟剩余的减速物理过程或者干扰UIScrollView的内部操作。这是容易出错的,也不太可能完全匹配每个人都习惯的UIScrollView物理规律。

相反,对于您的滚动视图仅仅是手势驱动器的情况(即,您实际上不需要在其中显示任何内容),我认为最好是只使用一个非常宽的滚动视图。将其初始内容偏移设置为其内容大小的中心。在scrollViewDidScroll(_:)中,计算已遍历屏幕宽度的百分比:

let pageWidth = scrollView.frame.width
let percentage = (scrollView.contentOffset.x % pageWidth) / pageWidth

这将基本上从0.0循环到1.0(向右移动)或从1.0循环到0.0(向左移动)。您可以将这些归一化值转发给其他可以响应它的函数,例如驱动动画。只需构造响应此内容的任何代码,使其在从0.0跳转到1.0或从1.0跳转到0.0时显得无缝即可。

当然,如果您需要循环的内容比正常滚动视图速度更快或更慢,只需使用屏幕宽度的较小或较大部分即可。我只是随意选择了那个。

如果您担心触及可滚动内容的边缘,则当滚动视图完全停止1时,将其内容偏移重置为相同的初始中心值,加上滚动视图停止滚动时剩余的屏幕宽度(或您正在使用的任何内容)。

鉴于上述重置方法,即使对于您托管可见内容的滚动视图,只要更新任何视图框架/模型值以考虑重置滚动偏移量,您也可以有效地实现无限滚动视图。


1为了正确捕获这个事件,需要实现scrollViewDidEndDecelerating(_:)scrollViewDidEndDragging(_:willDecelerate:)方法,在后者中只有在willDecelerate参数为false时才调用你的“完全停止”函数。而在前者中则总是要调用它。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接