2014-02-13 58 views
14

我有UIScrollView的子類。其內容是可重複使用的 - 大約4或5次用於顯示數百個元素(滾動時隱藏的對象重用,並跳轉到當其需要看到他們另一個位置)如何正確動畫UIScrollView contentOffset

我需要什麼:能夠自動滾動我將視圖滾動到任何位置。例如,我的滾動視圖顯示第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 
}]; 

但我已經連接了一些行動,以滾動的事件,所以他們現在都是在動畫塊,它會導致所有子視圖的幀動畫太(感謝一些可重複使用元素個個動畫不是我想要的)

的問題是:我怎樣才能讓自定義動畫(其實我需要自定義工期,對內容的最終和BeginFromCurrentState選項)的行動抵消WITH OUT動畫所有代碼,連接到scrollViewDidScroll事件?

UPD: 由於Andrew's answer(第一部分)我解決了問題,動畫裏面scrollViewDidScroll

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

scrollViewDidScroll必須(對我而言)執行動畫的每一幀,就像是在的情況下,

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

但是,現在它只在動畫開始時執行一次。

我該如何解決這個問題?

回答

16

您是否嘗試過相同的方法,但在scrollViewDidScroll中禁用了動畫?

在iOS 7,你可以嘗試包裹在scrollViewDidScroll你的代碼

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

以前的iOS版本,你可以嘗試:

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

更新:

不幸的是你擊中了整個事情的艱難部分。 setContentOffset:只需調用一次代表,它相當於setContentOffset:animated:NO,再次調用它一次。

setContentOffset:animated:YES調用委託作爲動畫改變了滾動視圖的邊界,你想要的,但你不想提供的動畫,所以我可以想出的唯一方法是逐步改變contentOffset ,這樣動畫系統就不會像現在那樣跳到最終值。

要做到這一點,你可以看看關鍵幀動畫,像這樣針對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 
}]; 

這將讓你兩個更新,當然你可以使用一些數學和循環增加了很多更這些與適當的時機。

在之前的iOS版本中,您必須爲關鍵幀動畫放置CoreAnimation,但它基本上是相同的,但語法有點不同。

方法2: 由於不幸的是,presentationLayer的屬性不是KVO可觀察的,您可以嘗試使用在動畫開頭啓動的計時器輪詢scrollview的presentationLayer以進行任何更改。或者您可以在圖層的子類中使用needsDisplayForKey以在邊界更改時得到通知,但這需要一些工作來設置,並且確實會導致重繪,這可能會影響性能。

方法3: 將解析動畫爲YES時scrollView發生的情況嘗試並攔截在scrollview上設置的動畫並更改其參數,但由於這將是最棘手的,易碎的,因爲蘋果的變化和最棘手的方法,我不會進入它。

+0

謝謝!這是如此明顯) 它幫助和現在「scrollViewDidScroll」執行沒有動畫。但是,與「contentOffset」的系統動畫幾次執行(我相信每幀)的情況不同,現在它只執行一次。 你有想法如何解決這個問題嗎? – Lloyd18

+1

在我的回答中添加了對您的評論的答案,因爲評論的評論時間很長。 – Andrew

+0

精彩的回答!非常感謝! – Lloyd18

4

一個很好的方法是使用AnimationEngine庫。這是一個非常小的圖書館:六個文件,如果你想要阻尼彈簧行爲,還有三個文件。

在幕後,它使用CADisplayLink每幀運行一次動畫塊。你可以得到一個簡單易用的基於塊的語法,以及一大堆節省時間的interpolationeasing functions

要動畫contentOffset

startOffset = scrollView.contentOffset; 
endOffset = .. 

// Constant speed looks good... 
const CGFloat kTimelineAnimationSpeed = 300; 
CGFloat timelineAnimationDuration = fabs(deltaToDesiredX)/kTimelineAnimationSpeed; 

[INTUAnimationEngine animateWithDuration:timelineAnimationDuration 
            delay:0 
            easing:INTULinear 
           animations:^(CGFloat progress) { 
            self.videoTimelineView.contentOffset = 
             INTUInterpolateCGPoint(startOffset, endOffset, progress); 
           } 
          completion:^(BOOL finished) { 
            autoscrollEnabled = YES; 
          }]; 
+0

謝謝,這對我來說非常合適! –