Iphone 如何使UIScrollView捕捉到图标(如应用商店:功能)
我想要得到的是与此滚动视图相同的行为: 我知道这是使用HTML而不是本机API,但我正在尝试将其作为UIKit组件实现 现在,请注意我正在寻找的行为:Iphone 如何使UIScrollView捕捉到图标(如应用商店:功能),iphone,ios,xcode,cocoa,uiscrollview,Iphone,Ios,Xcode,Cocoa,Uiscrollview,我想要得到的是与此滚动视图相同的行为: 我知道这是使用HTML而不是本机API,但我正在尝试将其作为UIKit组件实现 现在,请注意我正在寻找的行为: 请注意,它是一个分页滚动视图,但“页面大小”小于视图的宽度 当您从左向右滚动时,每页都会“捕捉”到最左边的项目 从右端向左滚动时,它会“捕捉”到最右边的项目 同一页,但现在从右向左: 我所尝试的: 我尝试过使滚动视图比它的超级视图小,并覆盖hitTest,这让我有了从左到右的行为 我已经尝试过实现ScrollViewWillendDra
- 请注意,它是一个分页滚动视图,但“页面大小”小于视图的宽度
- 当您从左向右滚动时,每页都会“捕捉”到最左边的项目
- 从右端向左滚动时,它会“捕捉”到最右边的项目
- 我尝试过使滚动视图比它的超级视图小,并覆盖hitTest,这让我有了从左到右的行为
- 我已经尝试过实现ScrollViewWillendDraging:withVelocity:targetContentOffset:并设置我想要的targetContentOffset,但由于我无法更改速度,它只是滚动得太慢或太快
- 我尝试过实现ScrollViewDiEndDecelling:然后将动画设置为正确的偏移量,但scroll视图先停止,然后移动,看起来不自然李>
- 我尝试过实现ScrollViewDiEndDraging:WillDecreate:并将动画设置为正确的偏移量,但滚动视图“跳跃”并且动画设置不正确
- (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;
}
此解决方案的唯一问题是滑动滚动视图不会产生“反弹”效果。它感觉“卡住了” 您可以启用
paginEnabled
,或者结合使用ScrollViewDiEndDraging
和ScrollViewDiEndDraging
使用decelerationRate=UIScrollViewDecelerationRateFast
(这将最大限度地降低滚动到一个位置然后再次设置动画到另一个位置的效果)。这可能不是你想要的,但很接近。通过使用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;
}];
}
设置
scrollView.decelerationRate=UIScrollViewDecelerationRateFast
,再加上实现ScrollViewWillendDraging:withVelocity:targetContentOffset:
,似乎对我使用集合视图很有效
首先,我给自己一些实例变量:
@implementation ViewController {
NSString *cellClassName;
CGFloat baseOffset;
CGFloat offsetStep;
}
在viewDidLoad
中,我设置了视图的减速率
:
- (void)viewDidLoad {
[super viewDidLoad];
cellClassName = NSStringFromClass([MyCell class]);
[self.collectionView registerNib:[UINib nibWithNibName:cellClassName bundle:nil] forCellWithReuseIdentifier:cellClassName];
self.collectionView.decelerationRate = UIScrollViewDecelerationRateFast;
}
我需要offsetStep
为符合视图屏幕边界的整数个项目的大小。我在viewdilayoutsubviews
中计算它:
- (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偏移量。我在视图中初始化它,显示:
:
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
baseOffset = self.collectionView.contentOffset.x;
}
然后我需要强制视图以offsetStep
的步骤滚动。我在ScrollViewWillendDraging:withVelocity:targetContentOffset:
中这样做。根据速度
,我通过offsetStep
增加或减少baseOffset
。但我将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;
}
这对我也适用
你可以找到我的测试应用程序。简单的解决方案,像应用商店一样工作,有速度也有速度<代码>kCellBaseWidth-单元格的宽度
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView
withVelocity:(CGPoint)velocity
targetContentOffset:(inout CGPoint *)targetContentOffset
{
NSInteger index = lrint(targetContentOffset->x/kCellBaseWidth);
targetContentOffset->x = index * kCellBaseWidth;
}
叫斯威夫特进来。”Ben的答案是最简单的,正如Ben所提到的,效果非常好。虽然我确实添加了一段来自的关于scrollview结尾的回答。综上所述,这一解决方案似乎非常有效
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
}
func ScrollViewWillendDraging(scrollView:UIScrollView,withVelocity:CGPoint,targetContentOffset:UnsafemeutablePointer){
if((scrollView.contentOffset.x+scrollView.frame.size.width)>=scrollView.contentSize.width){
//不需要快照…我们在滚动视图的末尾
返回
}
let index:CGFloat=CGFloat(lrintf(Float(targetContentOffset.memory.x)/kCellBaseWidth))
targetContentOffset.memory.x=索引*kCellBaseWidth
}
只需将您的
最小行间距
添加到kCellBaseWidth
,瞧 是否有可能使用UITableView/UICollectionView实现这一点?我想要相同的滚动和抓拍效果,但使用表格/集合可以预览下一个/上一个项目,如iTunes中的项目。在一列表/集合中执行类似于4行的操作。对于所有想要修复“卡住”感觉的人:如果abs(velocity.x)大于0.8,则可以添加/减去两个偏移步骤,而不是一个。不要忘记执行此操作的非常简单的方法。(特别是自动布局。只需制作文字滚动视图,即“单位”的大小。并将其制作为页面。就这么简单。如果需要调整“可触摸区域”,请使用超级巧妙的方法。)你成功做到了吗?有没有使用故事板的例子?@Fattie你能详细说明一下吗?我想要像App Store这样的东西。但是当我模仿UI时(下一个元素在右边部分可见)并启用分页,它将分页到下一个可见区域。也就是说,如果第一个视图显示,第二个项目显示为10磅,它将分页到第二个项目的第11磅。感谢所有对此做出贡献的人。提醒一下:如果在FlowLayout中使用“minimumLineSpacing”,则必须考虑该值。将其添加到kCellBaseWidth。
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
}