UIScrollView:水平分页,垂直滚动?

91

如何强制在分页和滚动都开启的情况下,UIScrollView 在给定时刻只能沿垂直或水平方向移动?

我理解 directionalLockEnabled 属性可以实现这一点,但是对角线滑动仍然会导致视图在两个方向上同时滚动而不是限制在单个轴上。

编辑:更明确地说,我希望允许用户水平或垂直滚动,但不能同时进行。


你想将视图限制为仅水平或垂直滚动,还是希望用户可以水平或垂直滚动,但不能同时进行? - Will Hartung
你能否帮我一个忙,把标题改得更有趣些,更加不平凡?比如说:“UIScrollView:水平分页,竖直滚动?” - Andrey Tarantsov
很不幸,我以前在一台现在无法访问的电脑上以未注册用户的身份发布了这个问题(后来回家时使用相同的用户名注册),这意味着我无法编辑它。这也解释了为什么我在下面进行跟进而不是编辑原始问题。很抱歉这次违反了 Stack 的礼仪! - Nick
21个回答

2
我所做的是创建了一个分页的UIScrollView,它的框架大小与视图屏幕相同,并将内容大小设置为所有内容所需的宽度和高度为1(感谢Tonetel)。然后我创建菜单UIScrollView页面,每个页面的框架设置为每个页面、视图屏幕的大小,并将每个页面的内容大小设置为屏幕的宽度和所需的高度。然后,我只需将每个菜单页面添加到分页视图中即可。请确保在分页滚动视图上启用分页,但在菜单视图上禁用分页(默认情况下应该禁用),这样就可以了。
由于内容高度的限制,分页滚动视图现在水平滚动,但不垂直滚动。每个页面垂直滚动,但由于其内容大小的限制,不能水平滚动。
如果上面的解释还有不清楚的地方,以下是代码:
UIScrollView *newPagingScrollView =  [[UIScrollView alloc] initWithFrame:self.view.bounds]; 
[newPagingScrollView setPagingEnabled:YES];
[newPagingScrollView setShowsVerticalScrollIndicator:NO];
[newPagingScrollView setShowsHorizontalScrollIndicator:NO];
[newPagingScrollView setDelegate:self];
[newPagingScrollView setContentSize:CGSizeMake(self.view.bounds.size.width * NumberOfDetailPages, 1)];
[self.view addSubview:newPagingScrollView];

float pageX = 0;

for (int i = 0; i < NumberOfDetailPages; i++)
{               
    CGRect pageFrame = (CGRect) 
    {
        .origin = CGPointMake(pageX, pagingScrollView.bounds.origin.y), 
        .size = pagingScrollView.bounds.size
    };
    UIScrollView *newPage = [self createNewPageFromIndex:i ToPageFrame:pageFrame]; // newPage.contentSize custom set in here        
    [pagingScrollView addSubview:newPage];

    pageX += pageFrame.size.width;  
}           

2

这是改进的icompot解决方案,相比之前的版本更加稳定。它易于实现,以下是具体步骤:

- (void) scrollViewWillBeginDragging: (UIScrollView *) scrollView
{
    self.oldContentOffset = scrollView.contentOffset;
}

- (void) scrollViewDidScroll: (UIScrollView *) scrollView
{    

    float XOffset = fabsf(self.oldContentOffset.x - scrollView.contentOffset.x );
    float YOffset = fabsf(self.oldContentOffset.y - scrollView.contentOffset.y );

        if (scrollView.contentOffset.x != self.oldContentOffset.x && (XOffset >= YOffset) )
        {

            scrollView.pagingEnabled = YES;
            scrollView.contentOffset = CGPointMake(scrollView.contentOffset.x,
                                                   self.oldContentOffset.y);

        }
        else
        {
            scrollView.pagingEnabled = NO;
               scrollView.contentOffset = CGPointMake( self.oldContentOffset.x,
                                                   scrollView.contentOffset.y);
        }

}


- (void) scrollViewDidEndDecelerating: (UIScrollView *) scrollView
{
    self.oldContentOffset = scrollView.contentOffset;
}

1
我的视图由10个水平视图组成,其中一些视图又包含多个垂直视图,所以你会得到类似这样的结构:

1.0   2.0   3.1    4.1
      2.1   3.1
      2.2
      2.3

在水平和垂直轴上启用分页

视图还具有以下属性:

[self setDelaysContentTouches:NO];
[self setMultipleTouchEnabled:NO];
[self setDirectionalLockEnabled:YES];

现在要防止对角线滚动,请执行以下操作:

-(void)scrollViewDidScroll:(UIScrollView *)scrollView {
    int pageWidth = 768;
    int pageHeight = 1024;
    int subPage =round(self.contentOffset.y / pageHeight);
    if ((int)self.contentOffset.x % pageWidth != 0 && (int)self.contentOffset.y % pageHeight != 0) {
        [self setContentOffset:CGPointMake(self.contentOffset.x, subPage * pageHeight];
    } 
}

对我来说简直太棒了!


1
不明白这对你是如何工作的...你能上传一个示例项目吗? - hfossli

1

需要在touchesEndedtouchesCancelled中将_isHorizontalScroll重置为NO。


1

来自文档:

“默认值为NO,这意味着可以在水平和垂直方向上滚动。如果值为YES并且用户开始沿一个方向(水平或垂直)拖动,则滚动视图禁用另一个方向的滚动。”

我认为重要的部分是“如果...用户开始沿一个方向拖动”。因此,如果他们开始对角线拖动,这种情况不会发生。 虽然这些文档在解释方面并不总是可靠 - 但这似乎符合您所看到的情况。

这实际上似乎很明智。我必须问一下 - 为什么您想随时限制只水平或只垂直?也许UIScrollView不适合您?


1

0

对于那些在Swift中查看@AndreyTarantsov的第二个解决方案,代码如下:

    var positionScroll:CGFloat = 0

    func scrollViewWillBeginDragging(scrollView: UIScrollView) {
        positionScroll = self.theScroll.contentOffset.x
    }

    func scrollViewDidScroll(scrollView: UIScrollView) {
        if self.theScroll.contentOffset.x > self.positionScroll || self.theScroll.contentOffset.x < self.positionScroll{
             self.theScroll.pagingEnabled = true
        }else{
            self.theScroll.pagingEnabled = false
        }
    }

我们在这里做的是在scrollview被拖动之前保持当前位置,然后检查x是否与当前位置的x相比增加或减少。如果是,则将pagingEnabled设置为true;如果不是(y已经增加/减少),则将pagingEnabled设置为false。

希望对新手有所帮助!


0

如果你还没有在3.0 beta中尝试过这个,我建议你试一试。我目前正在使用一个滚动视图内的一系列表视图,它的效果正如预期一样。(好吧,至少滚动是可以的。当我在寻找解决一个完全不同的问题时,发现了这个问题...)


0
这是Benjamin的答案的Swift 5版本。
// add this property to your view controller
var positionScroll: CGPoint = .zero

// make sure to set isDirectionalLockEnabled to true on your scroll view
self.scrollView.isDirectionalLockEnabled = true

// then in scrollview/collectionview/tableview delegate
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
    positionScroll = scrollView.contentOffset
}

func scrollViewDidScroll(_ scrollView: UIScrollView) {
    // The user wants to scroll on the X axis
    if scrollView.contentOffset.x > positionScroll.x || scrollView.contentOffset.x < positionScroll.x {
        // Reset the Y position of the scrollView to what it was before scrolling started
        scrollView.contentOffset = CGPoint(x: scrollView.contentOffset.x, y: positionScroll.y)
        scrollView.isPagingEnabled = true
    } else {
        // The user wants to scroll on the Y axis
        // Reset the X position of the scrollView to what it was before scrolling started
        scrollView.contentOffset = CGPoint(x: positionScroll.x, y: scrollView.contentOffset.y)
        scrollView.isPagingEnabled = false
    }
}

0

我通过实现scrollViewDidBeginDragging(_:), 并查看底层UIPanGestureRecognizer的速度来解决了这个问题。

NB: 使用这种解决方案,每个斜向滑动都将被scrollView忽略。

override func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
    super.scrollViewWillBeginDragging(scrollView)
    let velocity = scrollView.panGestureRecognizer.velocity(in: scrollView)
    if velocity.x != 0 && velocity.y != 0 {
        scrollView.panGestureRecognizer.isEnabled = false
        scrollView.panGestureRecognizer.isEnabled = true
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接