UIScrollViewサブクラスがあります。そのコンテンツは再利用可能です-約4または5つのビューを使用して数百の要素を表示します(非表示オブジェクトをスクロールすると再利用され、必要に応じて別の位置にジャンプします)
必要なもの:スクロールビューを任意の位置に自動的にスクロールする機能。たとえば、スクロールビューに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
}];
しかし、スクロールイベントに接続されているいくつかのアクションがあるため、それらのすべてがアニメーションブロックにあり、すべてのサブビューのフレームもアニメーション化されます(いくつかの再利用可能な要素のおかげで、すべてのアニメーションが希望どおりにアニメーション化されません)
質問は次のとおりです:コンテンツオフセット[〜#〜]なし[〜#〜]scrollViewDidScroll
イベントに接続されたすべてのコードをアニメーション化しますか?
UPD:おかげで Andrewの回答 (前半)scrollViewDidScroll
内のアニメーションに関する問題を解決しました:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
[UIView performWithoutAnimation:^{
[self refreshTiles];
}];
}
しかし、scrollViewDidScroll
は(私の目的では)アニメーションのすべてのフレームを実行する必要があります。
[self setContentOffset:CGPointMake(index*elementWidth, 0) animated:YES];
ただし、アニメーションの開始時に一度だけ実行されるようになりました。
どうすればこれを解決できますか?
同じアプローチを試しましたが、scrollViewDidScroll
のアニメーションが無効になっていますか?
IOS 7では、scrollViewDidScroll
でコードをラップしてみてください
[UIView performWithoutAnimation:^{
//Your code here
}];
以前のiOSバージョンでは、以下を試すことができます。
[CATransaction begin];
[CATransaction setDisableActions:YES];
//Your code here
[CATransaction commit];
更新:
残念ながら、それはあなたが全体の難しい部分にぶつかるところです。 setContentOffset:
はデリゲートを1回だけ呼び出します。これはsetContentOffset:animated:NO
と同じで、もう一度1回だけ呼び出します。
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
}];
これにより、2つの更新が得られます。もちろん、いくつかの計算とループを使用して、これらの多くを適切なタイミングで加算することができます。
以前のiOSバージョンでは、キーフレームアニメーションの場合はCoreAnimationにドロップする必要がありますが、それは基本的に同じで、構文は少し異なります。
方法2:残念ながら、PresentationLayerのプロパティはKVOで監視できないため、アニメーションの最初から開始するタイマーを使用して、スクロールビューのpresentationLayerの変更をポーリングしてみてください。または、レイヤーのサブクラスでneedsDisplayForKey
を使用して、境界が変更されたときに通知を受け取ることもできますが、そのためにはいくつかの設定が必要であり、再描画が発生するため、パフォーマンスに影響する可能性があります。
方法3:アニメーションがYESの場合にscrollViewに何が起こるかを正確に分析することは、scrollviewに設定されるアニメーションをインターセプトしてパラメーターを変更することですが、これはAppleの変更とトリッキーな方法により最もハッキングされ、壊れやすいため、私はそれには入りません。
これを行うための良い方法は、 AnimationEngine ライブラリを使用することです。これは非常に小さなライブラリです。6つのファイルがあり、減衰されたばねの動作が必要な場合はさらに3つあります。
舞台裏ではCADisplayLink
を使用して、フレームごとに1回アニメーションブロックを実行します。使いやすいクリーンなブロックベースの構文と、時間を節約する 補間 と イージング関数 の束が得られます。
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;
}];
これを試して:
UIView.animate(withDuration: 0.6, animations: {
self.view.collectionView.contentOffset = newOffset
self.view.layoutIfNeeded()
}, completion: nil)