iphone - 如何使 UIScrollView 捕捉到图标(如 App Store : Feature)

标签 iphone ios xcode cocoa uiscrollview

我想要得到的是这个 ScrollView 具有的相同行为:

App Store feature scroll view (left)

我知道这是使用 HTML 而不是 native API,但我正在尝试将其实现为 UIKit 组件。

现在,我正在寻找的行为:

  • 请注意,这是一个分页 ScrollView ,但“页面大小”小于 View 的宽度。
  • 当您从左向右滚动时,每个页面都会“吸附”到最左边的项目。
  • 当您将它从右端滚动到左侧时,它会“捕捉”到最右侧的项目。

相同的页面,但现在从右到左:

App Store feature scroll view (right)

我尝试过的:

  • 我试过使 ScrollView 小于它的 super View 并覆盖 hitTest,这让我产生了从左到右的行为。
  • 我已尝试实现 scrollViewWillEndDragging:withVelocity:targetContentOffset: 并设置我想要的 targetContentOffset,但由于我无法更改速度,它只会滚动得太慢或太快。
  • 我已尝试实现 scrollViewDidEndDecelerating:,然后将动画设置为正确的偏移量,但 ScrollView 先停止然后移动,看​​起来不自然。
  • 我已经尝试实现 scrollViewDidEndDragging:willDecelerate:,然后将动画设置为正确的偏移量,但 ScrollView “跳跃”并且没有正确设置动画。

我没有想法。

谢谢!

更新:

我最终使用了 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;
}

此解决方案的唯一问题是滑动 ScrollView 不会产生“反弹”效果。感觉“卡住了”。

最佳答案

设置 scrollView.decelerationRate = UIScrollViewDecelerationRateFast,结合实现 scrollViewWillEndDragging:withVelocity:targetContentOffset:,似乎对我使用 Collection View 有用。

首先,我给自己一些实例变量:

@implementation ViewController {
    NSString *cellClassName;
    CGFloat baseOffset;
    CGFloat offsetStep;
}

viewDidLoad 中,我设置了 View 的 decelerationRate:

- (void)viewDidLoad {
    [super viewDidLoad];
    cellClassName = NSStringFromClass([MyCell class]);
    [self.collectionView registerNib:[UINib nibWithNibName:cellClassName bundle:nil] forCellWithReuseIdentifier:cellClassName];
    self.collectionView.decelerationRate = UIScrollViewDecelerationRateFast;
}

我需要 offsetStep 是适合 View 屏幕边界的整数项的大小。我在 viewDidLayoutSubviews 中计算它:

- (void)viewDidLayoutSubviews {
    [super viewDidLayoutSubviews];
    UICollectionViewFlowLayout *layout = (UICollectionViewFlowLayout *)self.collectionView.collectionViewLayout;
    CGFloat stepUnit = layout.itemSize.width + layout.minimumLineSpacing;
    offsetStep = stepUnit * floorf(self.collectionView.bounds.size.width / stepUnit);
}

我需要 baseOffset 作为滚动开始前 View 的 X 偏移量。我在 viewDidAppear::

中初始化它
- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    baseOffset = self.collectionView.contentOffset.x;
}

然后我需要强制 View 以 offsetStep 为步长滚动。我在 scrollViewWillEndDragging:withVelocity:targetContentOffset: 中这样做。根据 velocity,我将 baseOffset 增加或减少 offsetStep。但是我将 baseOffset 限制为最小值 0 和最大值 contentSize.width - bounds.size.width

- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset {
    if (velocity.x < 0) {
        baseOffset = MAX(0, baseOffset - offsetStep);
    } else if (velocity.x > 0) {
        baseOffset = MIN(scrollView.contentSize.width - scrollView.bounds.size.width, baseOffset + offsetStep);
    }
    targetContentOffset->x = baseOffset;
}

请注意,我不关心 targetContentOffset->x 是什么。

这具有与最左侧可见项目的左边缘对齐的效果,直到用户一直滚动到最后一个项目。此时它与最右侧可见项的右边缘对齐,直到用户一直滚动到左侧。这似乎与 App Store 应用程序的行为相符。

如果这对您不起作用,您可以尝试将最后一行 (targetContentOffset->x = baseOffset) 替换为:

    dispatch_async(dispatch_get_main_queue(), ^{
        [scrollView setContentOffset:CGPointMake(baseOffset, 0) animated:YES];
    });

这也适用于我。

你可以找到我的测试应用 in this git repository .

关于iphone - 如何使 UIScrollView 捕捉到图标(如 App Store : Feature),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15961099/

相关文章:

iphone - 带有异常肥胖的 UISegmentedControl 的 UIToolbar

ios - UITabBarController 在通话或个人热点期间被按下?

ios - 如何使用带有现有 UIPageViewController 的按钮以编程方式滑动 View

ios - 在/Today-View 不存在应用程序

iphone - 将最后一行移出部分并删除部分时出现奇怪的动画

iphone - 如何在cocos2d中快速绘制背景?

iphone - iPhone 上的 XML 和 SQLite 内存利用率和性能

ios - 我想从 NSData 加载图像但滚动不流畅

iphone - 单击按钮时如何更改 uiwebview 的 url?

xcode - 如何让 Xcode 在构建时始终复制文件(即使它们没有被检测到已修改)?