我想要获得的是与此滚动视图相同的行为:
我知道这是使用HTML而不是本机API,但我正在尝试将其作为UIKit组件实现。
现在,我正在寻找的行为:
同一页面,但现在是从右到左:
我尝试了什么:
我没有想法。
谢谢!
更新
我最终使用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;
}
此解决方案的唯一问题是滑动滚动视图不会产生“反弹”效果。感觉“卡住了”。
答案 0 :(得分:14)
设置scrollView.decelerationRate = UIScrollViewDecelerationRateFast
,结合实施scrollViewWillEndDragging:withVelocity:targetContentOffset:
,似乎对我使用集合视图。
首先,我给自己一些实例变量:
@implementation ViewController {
NSString *cellClassName;
CGFloat baseOffset;
CGFloat offsetStep;
}
在viewDidLoad
中,我设置了视图decelerationRate
:
- (void)viewDidLoad {
[super viewDidLoad];
cellClassName = NSStringFromClass([MyCell class]);
[self.collectionView registerNib:[UINib nibWithNibName:cellClassName bundle:nil] forCellWithReuseIdentifier:cellClassName];
self.collectionView.decelerationRate = UIScrollViewDecelerationRateFast;
}
我需要offsetStep
为适合视图屏幕边界的整数项目的大小。我在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
作为视图的X偏移量。我在viewDidAppear:
中初始化它:
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
baseOffset = self.collectionView.contentOffset.x;
}
然后我需要强制视图以offsetStep
的步长滚动。我在scrollViewWillEndDragging:withVelocity:targetContentOffset:
中这样做。根据{{1}},我会velocity
增加或减少baseOffset
。但我将offsetStep
限制在最小值0和最大值baseOffset
。
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;
}
是什么意思。
这具有与最左边的可见项目的左边缘对齐的效果,直到用户一直滚动到最后一个项目。此时,它与最右边的可见项的右边缘对齐,直到用户一直向左滚动。这似乎与App Store应用程序的行为相匹配。
如果这对您不起作用,您可以尝试用此替换最后一行(targetContentOffset->x
):
targetContentOffset->x = baseOffset
这对我也有用。
您可以找到我的测试应用in this git repository。
答案 1 :(得分:12)
您可以启用pagingEnabled
,也可以使用decelerationRate = UIScrollViewDecelerationRateFast
结合scrollViewDidEndDragging
和scrollViewDidEndDecelerating
(这样可以最大限度地减少放慢滚动到一个位置的效果,然后重新制作动画效果到另一个地方)。它可能不是你想要的,但它非常接近。通过使用animateWithDuration
,它可以避免最后一次快速跳跃。
- (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;
}];
}
答案 2 :(得分:5)
简单的解决方案,像App Store一样,有速度或无速度。 kCellBaseWidth
- 细胞宽度。
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView
withVelocity:(CGPoint)velocity
targetContentOffset:(inout CGPoint *)targetContentOffset
{
NSInteger index = lrint(targetContentOffset->x/kCellBaseWidth);
targetContentOffset->x = index * kCellBaseWidth;
}
答案 3 :(得分:2)
为Swift做准备。到目前为止,@avdyushin的答案是最简单的,而且正如Ben所说,它的效果非常好。虽然我确实添加了@Rob关于滚动视图结束的答案。总之,这个解决方案似乎完美无缺。
func scrollViewWillEndDragging(scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
if ((scrollView.contentOffset.x + scrollView.frame.size.width) >= scrollView.contentSize.width) {
// no snap needed ... we're at the end of the scrollview
return
}
let index: CGFloat = CGFloat(lrintf(Float(targetContentOffset.memory.x) / kCellBaseWidth))
targetContentOffset.memory.x = index * kCellBaseWidth
}
只需将minimumLineSpacing
添加到kCellBaseWidth
即可。