6

私が取得したいのは、このスクロール ビューと同じ動作です。

App Store 機能のスクロール ビュー (左)

これがネイティブ API ではなく HTML を使用していることはわかっていますが、UIKit コンポーネントとして実装しようとしています。

今、私が探している動作に:

  • これはページ付きスクロール ビューですが、「ページ サイズ」はビューの幅よりも小さいことに注意してください。
  • 左から右にスクロールすると、各ページが一番左の項目に「スナップ」します。
  • 右端から左にスクロールすると、一番右の項目に「スナップ」します。

同じページですが、右から左:

App Store 機能のスクロール ビュー (右)

私が試したこと:

  • スクロール ビューをスーパー ビューよりも小さくして、hitTest をオーバーライドしようとしたところ、左から右への動作になりました。
  • scrollViewWillEndDragging:withVelocity:targetContentOffset: を実装して、必要な targetContentOffset を設定しようとしましたが、速度を変更できないため、スクロールが遅すぎたり速すぎたりします。
  • scrollViewDidEndDecelerating: を実装してから、正しいオフセットにアニメーション化しようとしましたが、スクロール ビューが最初に停止してから移動し、自然に見えません。
  • scrollViewDidEndDragging:willDecelerate: を実装してから、正しいオフセットにアニメーション化しようとしましたが、スクロール ビューが「ジャンプ」して正しくアニメーション化されません。

私はアイデアがありません。

ありがとう!

アップデート:

最終的に Rob Mayoff の方法を使用しましたが、きれいに見えます。ユーザーが指をドラッグ、停止、離したときなど、速度が 0 のときに機能するように変更しました。

- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView 
                     withVelocity:(CGPoint)velocity 
              targetContentOffset:(CGPoint *)targetContentOffset {
    CGFloat maxOffset = scrollView.contentSize.width - scrollView.bounds.size.width;
    CGFloat minOffset = 0;

    if (velocity.x == 0) {
        CGFloat targetX = MAX(minOffset,MIN(maxOffset, targetContentOffset->x));

        CGFloat diff = targetX - baseOffset;

        if (ABS(diff) > offsetStep/2) {
            if (diff > 0) {
                //going left
                baseOffset = MIN(maxOffset, baseOffset + offsetStep);
            } else {
                //going right
                baseOffset = MAX(minOffset, baseOffset - offsetStep);
            }
        }
    } else {
        if (velocity.x > 0) {
            baseOffset = MIN(maxOffset, baseOffset + offsetStep);
        } else {
            baseOffset = MAX(minOffset, baseOffset - offsetStep);
        }
    }

    targetContentOffset->x = baseOffset;
}

このソリューションの唯一の問題は、スクロール ビューをスワイプしても「バウンス」効果が発生しないことです。「くっついた」感じです。

4

4 に答える 4

12

をオンにするか、とを組み合わせてpagingEnabled使用​​できます(これにより、ある場所へのスクロールが遅くなり、別の場所へのアニメーションが再び行われるという影響を最小限に抑えることができます)。それはおそらくあなたが望むものではありませんが、かなり近いものです。そして、それを使用することで、最後のスナップの瞬間的なジャンプを回避できます。decelerationRate = UIScrollViewDecelerationRateFastscrollViewDidEndDraggingscrollViewDidEndDeceleratinganimateWithDuration

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
{
    if (!decelerate)
        [self snapScrollView:scrollView];
}

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
    [self snapScrollView:scrollView];
}

snapScrollView次に、次のような を記述できます。

- (void)snapScrollView:(UIScrollView *)scrollView
{
    CGPoint offset = scrollView.contentOffset;

    if ((offset.x + scrollView.frame.size.width) >= scrollView.contentSize.width)
    {
        // no snap needed ... we're at the end of the scrollview
        return;
    }

    // calculate where you want it to snap to

    offset.x = floorf(offset.x / kIconOffset + 0.5) * kIconOffset;

    // now snap it to there

    [UIView animateWithDuration:0.1
                     animations:^{
                         scrollView.contentOffset = offset;
                     }];
}
于 2013-04-12T00:57:20.733 に答える
6

シンプルなソリューションは、速度の有無にかかわらず、App Store のように機能します。kCellBaseWidth— セルの幅。

- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView
                     withVelocity:(CGPoint)velocity
              targetContentOffset:(inout CGPoint *)targetContentOffset
{
    NSInteger index = lrint(targetContentOffset->x/kCellBaseWidth);
    targetContentOffset->x = index * kCellBaseWidth;
}
于 2014-04-23T11:56:37.547 に答える