2013-03-03 4 views
4

Есть ли способ изменить скорость анимации contentOffset, не создавая собственную анимацию, которая устанавливает contentOffset?Как изменить скорость анимации UIScrollView?

Причина, по которой я не могу создать свою собственную анимацию для изменения contentOffset, заключается в том, что она не будет вызывать -scrollViewDidScroll: равномерно во время анимации.

+1

Предполагая вызове '[Scrollview setContentOffset: Foo анимированный: YES]' внутри анимации блок не работает (и я подозреваю, что это не делает), вы, вероятно, может получить доступ к продолжительности анимации по баловаться с " частные "API - например [этот ответ] (http://stackoverflow.com/a/11271360/349112) дает возможность изменить скорость торможения. Вот драконы ... –

+0

привет, см. Мой пересмотренный ответ ниже. – danh

ответ

2

Чтобы получить периодическую информацию о состоянии прокрутки, вы можете запустить анимацию пошагово. Делегат будет вызван один раз (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

Я пробовал это, и он очень изменчив! – brenjt

+1

+1 для изменчивых :-) –

-2

Вот простое решение, которое работает.

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

Надеюсь, что это поможет.

Brian

+1

Не работает. Пробовал устанавливать другую продолжительность, а ее еще около 500 мс. – jede

9

К сожалению, нет чистой и простой способ сделать это. Вот немного жестокий, но работая подход:

1) Добавить CADisplayLink как свойство:

@property (nonatomic, strong) CADisplayLink *displayLink; 

2) Animate содержания смещения:

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:

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

- (void)_handleContentOffsetChangeWithOffset:(CGPoint)offset { 
    // handle offset change 
} 
+0

Я думаю, что шаг 3 является важным, чтобы иметь в виду: текущее состояние пользовательского интерфейса задается презентационным слоем. –

+1

@kamilkocemba Это замечательный совет в целом! Это действительно эффективный способ использования одной анимации для управления позициями других элементов. Благодаря! –

+0

Его работы. Но любая кнопка в UIScrollView не получает событие касания. – Asfanur

1

Ниже приведено решение i СГУП

CGPoint leftOffset = CGPointMake(0, 0); 
      [UIView animateWithDuration:.5 
           delay:0 
           options:UIViewAnimationOptionCurveLinear 
          animations:^{ 
           [self.topBarScrollView setContentOffset:leftOffset animated:NO]; 
          } completion:nil]; 
Смежные вопросы