如何强制在其中进行分页和滚动的UIScrollView
仅在给定时刻垂直或水平移动?
我的理解是directionalLockEnabled
属性应该实现这一点,但是对角线滑动仍会导致视图对角滚动而不是将运动限制为单个轴。
编辑:为了更清楚,我想让用户水平或垂直滚动,但不能同时滚动。
答案 0 :(得分:113)
答案 1 :(得分:53)
每次都适合我......
scrollView.contentSize = CGSizeMake(scrollView.frame.size.width * NumberOfPages, 1);
答案 2 :(得分:25)
对于像我这样懒惰的人:
将scrollview.directionalLockEnabled
设为YES
- (void) scrollViewWillBeginDragging: (UIScrollView *) scrollView
{
self.oldContentOffset = scrollView.contentOffset;
}
- (void) scrollViewDidScroll: (UIScrollView *) scrollView
{
if (scrollView.contentOffset.x != self.oldContentOffset.x)
{
scrollView.pagingEnabled = YES;
scrollView.contentOffset = CGPointMake(scrollView.contentOffset.x,
self.oldContentOffset.y);
}
else
{
scrollView.pagingEnabled = NO;
}
}
- (void) scrollViewDidEndDecelerating: (UIScrollView *) scrollView
{
self.oldContentOffset = scrollView.contentOffset;
}
答案 3 :(得分:16)
我使用以下方法来解决这个问题,希望它可以帮到你。
首先在控制器头文件中定义以下变量。
CGPoint startPos;
int scrollDirection;
当您的代理收到 scrollViewWillBeginDragging 消息时,startPos 将保留 contentOffset 值。所以在这种方法中我们这样做;
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView{
startPos = scrollView.contentOffset;
scrollDirection=0;
}
然后我们使用这些值来确定用户在 scrollViewDidScroll 消息中的预期滚动方向。
- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
if (scrollDirection==0){//we need to determine direction
//use the difference between positions to determine the direction.
if (abs(startPos.x-scrollView.contentOffset.x)<abs(startPos.y-scrollView.contentOffset.y)){
NSLog(@"Vertical Scrolling");
scrollDirection=1;
} else {
NSLog(@"Horitonzal Scrolling");
scrollDirection=2;
}
}
//Update scroll position of the scrollview according to detected direction.
if (scrollDirection==1) {
[scrollView setContentOffset:CGPointMake(startPos.x,scrollView.contentOffset.y) animated:NO];
} else if (scrollDirection==2){
[scrollView setContentOffset:CGPointMake(scrollView.contentOffset.x,startPos.y) animated:NO];
}
}
最后,我们必须在用户结束拖动时停止所有更新操作;
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate{
if (decelerate) {
scrollDirection=3;
}
}
答案 4 :(得分:13)
我可能会在这里唠叨,但是当我试图解决同样的问题时,我今天偶然发现了这篇文章。这是我的解决方案,似乎工作得很好。
我希望显示一系列内容,但允许用户滚动到其他4个屏幕中的一个,向上和向下。我有一个大小为320x480的UIScrollView和一个960x1440的contentSize。内容偏移量从320,480开始。在scrollViewDidEndDecelerating:中,我重绘了5个视图(中心视图和它周围的4个视图),并将内容偏移重置为320,480。
这是我的解决方案的主要内容,即scrollViewDidScroll:方法。
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
if (!scrollingVertically && ! scrollingHorizontally)
{
int xDiff = abs(scrollView.contentOffset.x - 320);
int yDiff = abs(scrollView.contentOffset.y - 480);
if (xDiff > yDiff)
{
scrollingHorizontally = YES;
}
else if (xDiff < yDiff)
{
scrollingVertically = YES;
}
}
if (scrollingHorizontally)
{
scrollView.contentOffset = CGPointMake(scrollView.contentOffset.x, 480);
}
else if (scrollingVertically)
{
scrollView.contentOffset = CGPointMake(320, scrollView.contentOffset.y);
}
}
答案 5 :(得分:4)
这就像使子视图高度与滚动视图高度和内容大小高度相同一样简单。然后禁用垂直滚动。
答案 6 :(得分:3)
我也必须解决这个问题,虽然Andrey Tarantsov的回答肯定有很多有用的信息来理解UIScrollViews
如何运作,但我觉得解决方案有点过了-复杂。我的解决方案不涉及任何子类或触摸转发,如下所示:
UIPanGestureRecognizers
,每个方向再创一个。UIScrollViews
&#39;之间创建失败依赖关系使用UIPanGestureRecognizer
UIGestureRecognizer's
选择器,panGestureRecognizer属性和与您的UIScrollView所需滚动方向垂直的方向对应的虚拟-requireGestureRecognizerToFail:
。-gestureRecognizerShouldBegin:
回调中,使用手势&#39; s -translationInView:选择器计算平移的初始方向(您的UIPanGestureRecognizers
不会调用此委托回调直到你的触摸翻译足以注册为平底锅)。允许或禁止您的手势识别器根据计算出的方向开始,这反过来应控制是否允许垂直UIScrollView平移手势识别器开始。-gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:
中,不允许您的虚拟UIPanGestureRecognizers
与滚动一起运行(除非您有一些额外的行为要添加)。答案 7 :(得分:3)
这是我实现的只有正交滚动的分页UIScrollView
。请务必将pagingEnabled
设置为YES
。
PagedOrthoScrollView.h
@interface PagedOrthoScrollView : UIScrollView
@end
PagedOrthoScrollView.m
#import "PagedOrthoScrollView.h"
typedef enum {
PagedOrthoScrollViewLockDirectionNone,
PagedOrthoScrollViewLockDirectionVertical,
PagedOrthoScrollViewLockDirectionHorizontal
} PagedOrthoScrollViewLockDirection;
@interface PagedOrthoScrollView ()
@property(nonatomic, assign) PagedOrthoScrollViewLockDirection dirLock;
@property(nonatomic, assign) CGFloat valueLock;
@end
@implementation PagedOrthoScrollView
@synthesize dirLock;
@synthesize valueLock;
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self == nil) {
return self;
}
self.dirLock = PagedOrthoScrollViewLockDirectionNone;
self.valueLock = 0;
return self;
}
- (void)setBounds:(CGRect)bounds {
int mx, my;
if (self.dirLock == PagedOrthoScrollViewLockDirectionNone) {
// is on even page coordinates, set dir lock and lock value to closest page
mx = abs((int)CGRectGetMinX(bounds) % (int)CGRectGetWidth(self.bounds));
if (mx != 0) {
self.dirLock = PagedOrthoScrollViewLockDirectionHorizontal;
self.valueLock = (round(CGRectGetMinY(bounds) / CGRectGetHeight(self.bounds)) *
CGRectGetHeight(self.bounds));
} else {
self.dirLock = PagedOrthoScrollViewLockDirectionVertical;
self.valueLock = (round(CGRectGetMinX(bounds) / CGRectGetWidth(self.bounds)) *
CGRectGetWidth(self.bounds));
}
// show only possible scroll indicator
self.showsVerticalScrollIndicator = dirLock == PagedOrthoScrollViewLockDirectionVertical;
self.showsHorizontalScrollIndicator = dirLock == PagedOrthoScrollViewLockDirectionHorizontal;
}
if (self.dirLock == PagedOrthoScrollViewLockDirectionHorizontal) {
bounds.origin.y = self.valueLock;
} else {
bounds.origin.x = self.valueLock;
}
mx = abs((int)CGRectGetMinX(bounds) % (int)CGRectGetWidth(self.bounds));
my = abs((int)CGRectGetMinY(bounds) % (int)CGRectGetHeight(self.bounds));
if (mx == 0 && my == 0) {
// is on even page coordinates, reset lock
self.dirLock = PagedOrthoScrollViewLockDirectionNone;
}
[super setBounds:bounds];
}
@end
答案 8 :(得分:2)
为了将来的参考,我建立了Andrey Tanasov的答案,并提出了以下解决方案,该方法运行良好。
首先定义一个变量来存储contentOffset并将其设置为0,0坐标
var positionScroll = CGPointMake(0, 0)
现在在scrollView委托中实现以下内容:
override func scrollViewWillBeginDragging(scrollView: UIScrollView) {
// Note that we are getting a reference to self.scrollView and not the scrollView referenced passed by the method
// For some reason, not doing this fails to get the logic to work
self.positionScroll = (self.scrollView?.contentOffset)!
}
override func scrollViewDidScroll(scrollView: UIScrollView) {
// Check that contentOffset is not nil
// We do this because the scrollViewDidScroll method is called on viewDidLoad
if self.scrollView?.contentOffset != nil {
// The user wants to scroll on the X axis
if self.scrollView?.contentOffset.x > self.positionScroll.x || self.scrollView?.contentOffset.x < self.positionScroll.x {
// Reset the Y position of the scrollView to what it was BEFORE scrolling started
self.scrollView?.contentOffset = CGPointMake((self.scrollView?.contentOffset.x)!, self.positionScroll.y);
self.scrollView?.pagingEnabled = true
}
// The user wants to scroll on the Y axis
else {
// Reset the X position of the scrollView to what it was BEFORE scrolling started
self.scrollView?.contentOffset = CGPointMake(self.positionScroll.x, (self.scrollView?.contentOffset.y)!);
self.collectionView?.pagingEnabled = false
}
}
}
希望这有帮助。
答案 9 :(得分:2)
谢谢Tonetel。我略微修改了你的方法,但这正是我需要防止水平滚动的。
self.scrollView.contentSize = CGSizeMake(self.scrollView.contentSize.width, 1);
答案 10 :(得分: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;
}
答案 11 :(得分:1)
来自文档:
“默认值为NO,这意味着在水平和垂直方向都允许滚动。如果值为YES并且用户开始在一个大致方向上拖动(水平或垂直),滚动视图将禁用滚动另一个方向。“
我认为重要的部分是“如果...用户开始拖动一个大方向”。 因此,如果他们开始沿对角线方向拖动,那么就不会开始。 并不是说这些文档在解释方面总是可靠的 - 但这似乎与你所看到的一致。
这实际上似乎是明智的。我不得不问 - 为什么你想在任何时候只限制水平或垂直?也许UIScrollView不是适合你的工具?
答案 12 :(得分:1)
如果你有一个大的滚动视图...并且你想限制对角线滚动http://chandanshetty01.blogspot.in/2012/07/restricting-diagonal-scrolling-in.html
答案 13 :(得分: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];
}
}
对我来说就像魅力一样!
答案 14 :(得分:1)
需要在_isHorizontalScroll
和touchesEnded
中将touchesCancelled
重置为否。
答案 15 :(得分:0)
如果您还没有尝试在3.0测试版中执行此操作,我建议您试一试。我目前在滚动视图中使用一系列表视图,它按预期工作。 (好吧,无论如何,滚动都是这样。在寻找一个完全不同的问题时找到这个问题...)
答案 16 :(得分:0)
对于那些在@AndreyTarantsov第二个解决方案中寻找Swift的人,在代码中就像这样:
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
希望这对新来的人有用!
答案 17 :(得分:0)
我设法实现了scrollViewDidBeginDragging(_ :)并查看了底层UIPanGestureRecognizer的速度来解决了这个问题。
注意:使用此解决方案,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
}
}
答案 18 :(得分:0)
// 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
}
}
答案 19 :(得分:-1)
如果您使用界面构建器来设计界面 - 这可以非常轻松地完成。
在界面构建器中,只需单击UIScrollView并选择(仅在检查器中)将其限制为仅一种方式滚动的选项。