【问题标题】:How to change UIScrollView contentOffset animation speed?如何更改 UIScrollView contentOffset 动画速度?
【发布时间】:2013-02-17 19:58:28
【问题描述】:

有没有一种方法可以改变 contentOffset 动画的速度,而无需创建自己的动画来设置 contentOffset?

我无法为 contentOffset 更改创建自己的动画的原因是,这不会在动画期间定期调用 -scrollViewDidScroll:。

【问题讨论】:

  • 假设从动画块中调用 [scrollView setContentOffset:foo animated:YES] 不起作用(我怀疑它不起作用),您可能可以通过弄乱“私有”API 来访问动画持续时间 - 例如this answer 提供了一种更改减速率的方法。这里是龙......
  • 嗨,请在下面查看我修改后的答案。

标签: ios uiscrollview uikit core-animation


【解决方案1】:

要获取有关滚动状态的周期性信息,您可以分步运行动画。每个步骤都会调用一次委托 (scrollViewDidScroll:)

- (void)scrollTo:(CGPoint)offset completion:(void (^)(BOOL))completion {

    // this presumes an outlet called scrollView.   You could generalize by passing
    // the scroll view, or even more generally, place this in a UIScrollView category
    CGPoint contentOffset = self.scrollView.contentOffset;

    // scrollViewDidScroll delegate will get called 'steps' times
    NSInteger steps = 10;
    CGPoint offsetStep = CGPointMake((offset.x-contentOffset.x)/steps, (offset.y-contentOffset.y)/steps);
    NSMutableArray *offsets = [NSMutableArray array];

    for (int i=0; i<steps; i++) {
        CGFloat stepX = offsetStep.x * (i+1);
        CGFloat stepY = offsetStep.y * (i+1);
        NSValue *nextStep = [NSValue valueWithCGPoint:CGPointMake(contentOffset.x+stepX, contentOffset.y+stepY)];
        [offsets addObject:nextStep];
    }
    [self scrollBySteps:offsets completion:completion];
}

// run several scroll animations back-to-back
- (void)scrollBySteps:(NSMutableArray *)offsets completion:(void (^)(BOOL))completion {

    if (!offsets.count) return completion(YES);

    CGPoint offset = [[offsets objectAtIndex:0] CGPointValue];
    [offsets removeObjectAtIndex:0];

    // total animation time == steps * duration.  naturally, you can fool with both
    // constants.  to keep the rate constant, set duration == steps * k, where k
    // is some constant time per step
    [UIView animateWithDuration:0.1 delay:0.0 options:UIViewAnimationOptionCurveLinear animations:^{
        self.scrollView.contentOffset = offset;
    } completion:^(BOOL finished) {
        [self scrollBySteps:offsets completion:completion];
    }];
}

这样称呼...

CGPoint bottomOffset = CGPointMake(0, self.scrollView.contentSize.height - self.scrollView.bounds.size.height);    
[self scrollTo:bottomOffset completion:^(BOOL finished) {}];

// BONUS completion handler!  you can omit if you don't need it

【讨论】:

    【解决方案2】:

    这是一个有效的简单解决方案。

        [UIView beginAnimations:nil context:nil];
        [UIView setAnimationDuration:0.5];
        [self setContentOffset:<offset> animated:YES];
        [UIView commitAnimations];
    

    希望这会有所帮助。

    布赖恩

    【讨论】:

    • 不起作用。尝试设置不同的持续时间,仍然是 500 毫秒左右。
    【解决方案3】:

    不幸的是,没有干净简单的方法可以做到这一点。 这是一种略显粗暴但有效的方法:

    1) 将CADisplayLink 添加为属性:

    @property (nonatomic, strong) CADisplayLink *displayLink;
    

    2) 动画内容偏移:

    CGFloat duration = 2.0;
    
    // Create CADisplay link and add it to the run loop
    self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(_displayLinkTick)];
    [self.displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
    
    [UIView animateWithDuration:duration animations:^{
        self.scrollView.contentOffset = newContentOffset;       
    } completion:^(BOOL finished) {
        // Cleanup the display link
        [self.displayLink removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
        self.displayLink = nil;
    }];
    

    3) 最后观察presentationLayer上的变化like:

    - (void)_displayLinkTick {
        CALayer *presentationLayer = (CALayer *)self.scrollView.layer.presentationLayer;
        CGPoint contentOffset = presentationLayer.bounds.origin;
        [self _handleContentOffsetChangeWithOffset:contentOffset];
    }
    
    - (void)_handleContentOffsetChangeWithOffset:(CGPoint)offset {
        // handle offset change
    }
    

    【讨论】:

    • 我认为第 3 步是需要牢记的重要一步:UI 的当前状态由presentationLayer 给出。
    • @kamilkocemba 总的来说这是一个很棒的提示!实际上,使用单个动画来驱动其他元素的位置是一种非常有效的方法。谢谢!
    • 它的作品。但是 UIScrollView 上的任何按钮都不会收到触摸事件。
    • 但是……你在_handleContentOffsetChangeWithOffset做什么?
    • @buildsucceeded,与 scrollViewDidScroll: 方法中的代码相同。
    【解决方案4】:

    下面的代码解决了我的问题

    CGPoint leftOffset = CGPointMake(0, 0);
              [UIView animateWithDuration:.5
                                    delay:0
                                  options:UIViewAnimationOptionCurveLinear
                               animations:^{
                                   [self.topBarScrollView setContentOffset:leftOffset animated:NO];
                               } completion:nil];
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-01-02
      • 2011-03-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-03-12
      • 2014-11-23
      相关资源
      最近更新 更多