我想要得到的是这个 ScrollView 具有的相同行为:
我知道这是使用 HTML 而不是 native API,但我正在尝试将其实现为 UIKit 组件。
现在,我正在寻找的行为:
- 请注意,这是一个分页 ScrollView ,但“页面大小”小于 View 的宽度。
- 当您从左向右滚动时,每个页面都会“吸附”到最左边的项目。
- 当您将它从右端滚动到左侧时,它会“捕捉”到最右侧的项目。
相同的页面,但现在从右到左:
我尝试过的:
- 我试过使 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/