寻找在UIScrollView滚动的方向?
我有一个UIScrollView
只允许水平滚动,我想知道用户滚动的方向(左,右)。 我所做的是touchesMoved
UIScrollView
并覆盖touchesMoved
方法:
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { [super touchesMoved:touches withEvent:event]; UITouch *touch = [touches anyObject]; float now = [touch locationInView:self].x; float before = [touch previousLocationInView:self].x; NSLog(@"%f %f", before, now); if (now > before){ right = NO; NSLog(@"LEFT"); } else{ right = YES; NSLog(@"RIGHT"); } }
但是这个方法有时在我移动时根本不会被调用。 你怎么看?
确定方向是相当直接的,但请记住方向可以在手势过程中多次改变。 例如,如果您的滚动视图打开了分页,并且用户滑动到下一页,则初始方向可能是向右的,但是如果打开了反弹,它将短暂地完全没有方向,然后简单地向左走。
要确定方向,您需要使用UIScrollView scrollViewDidScroll
委托。 在这个示例中,我创build了一个名为lastContentOffset
的variables,用于比较当前的内容偏移量和前一个偏移量。 如果它更大,那么scrollView正在向右滚动。 如果它不是那么scrollView左滚动:
// somewhere in the private class extension @property (nonatomic, assign) CGFloat lastContentOffset; // somewhere in the class implementation - (void)scrollViewDidScroll:(UIScrollView *)scrollView { ScrollDirection scrollDirection; if (self.lastContentOffset > scrollView.contentOffset.x) { scrollDirection = ScrollDirectionRight; } else if (self.lastContentOffset < scrollView.contentOffset.x) { scrollDirection = ScrollDirectionLeft; } self.lastContentOffset = scrollView.contentOffset.x; // do whatever you need to with scrollDirection here. }
我正在使用下面的枚举来定义方向。 将第一个值设置为ScrollDirectionNone还有一个额外的好处,就是在初始化variables时将该方向设置为默认值:
typedef NS_ENUM(NSInteger, ScrollDirection) { ScrollDirectionNone, ScrollDirectionRight, ScrollDirectionLeft, ScrollDirectionUp, ScrollDirectionDown, ScrollDirectionCrazy, };
…我想知道用户滚动的方向(左,右)。
在这种情况下,在iOS 5及更高版本上,使用UIScrollViewDelegate
来确定用户的平移手势的方向:
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView { if ([scrollView.panGestureRecognizer translationInView:scrollView.superview].x > 0) { // handle dragging to the right } else { // handle dragging to the left } }
使用scrollViewDidScroll:
是find当前方向的好方法。
如果您想知道用户完成滚动后的方向,请使用以下命令:
@property (nonatomic) CGFloat lastContentOffset; - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView { self.lastContentOffset = scrollView.contentOffset.x; } - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView { if (self.lastContentOffset < scrollView.contentOffset.x) { // moved right } else if (self.lastContentOffset > scrollView.contentOffset.x) { // moved left } else { // didn't move } }
不需要添加额外的variables来跟踪这个。 只需使用像这样的UIScrollView
的panGestureRecognizer
属性。 不幸的是,这只有在速度不是0时才起作用:
CGFloat yVelocity = [scrollView.panGestureRecognizer velocityInView:scrollView].y; if (yVelocity < 0) { NSLog(@"Up"); } else if (yVelocity > 0) { NSLog(@"Down"); } else { NSLog(@"Can't determine direction as velocity is 0"); }
您可以使用x和y组件的组合来检测上,下,左和右。
解决scheme
func scrollViewDidScroll(scrollView: UIScrollView) { if(scrollView.panGestureRecognizer.translationInView(scrollView.superview).y > 0) { print("up") } else { print("down") } }
在iOS8 Swift中我使用了这个方法:
override func scrollViewDidScroll(scrollView: UIScrollView){ var frame: CGRect = self.photoButton.frame var currentLocation = scrollView.contentOffset.y if frame.origin.y > currentLocation{ println("Going up!") }else if frame.origin.y < currentLocation{ println("Going down!") } frame.origin.y = scrollView.contentOffset.y + scrollHeight photoButton.frame = frame view.bringSubviewToFront(photoButton) }
我有一个dynamic的视图,当用户滚动时会改变位置,所以视图看起来像停留在屏幕上的相同位置。 我也跟踪用户正在上升或下降。
这也是另一种方式:
func scrollViewWillEndDragging(scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) { if targetContentOffset.memory.y < scrollView.contentOffset.y { println("Going up!") } else { println("Going down!") } }
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset { CGPoint targetPoint = *targetContentOffset; CGPoint currentPoint = scrollView.contentOffset; if (targetPoint.y > currentPoint.y) { NSLog(@"up"); } else { NSLog(@"down"); } }
或者,可以观察关键path“contentOffset”。 当您无法设置/更改滚动视图的委托时,这非常有用。
[yourScrollView addObserver:self forKeyPath:@"contentOffset" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
在添加观察者之后,您现在可以:
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{ CGFloat newOffset = [[change objectForKey:@"new"] CGPointValue].y; CGFloat oldOffset = [[change objectForKey:@"old"] CGPointValue].y; CGFloat diff = newOffset - oldOffset; if (diff < 0 ) { //scrolling down // do something } }
切记在需要时移除观察者。 例如你可以在viewWillAppear中添加观察者,并在viewWillDisappear中将其删除
这里是我的解决scheme,像@followben答案,但没有损失与缓慢的开始(当dy为0)
@property (assign, nonatomic) BOOL isFinding; @property (assign, nonatomic) CGFloat previousOffset; - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView { self.isFinding = YES; } - (void)scrollViewDidScroll:(UIScrollView *)scrollView { if (self.isFinding) { if (self.previousOffset == 0) { self.previousOffset = self.tableView.contentOffset.y; } else { CGFloat diff = self.tableView.contentOffset.y - self.previousOffset; if (diff != 0) { self.previousOffset = 0; self.isFinding = NO; if (diff > 0) { // moved up } else { // moved down } } } } }
这是它为我工作(在Objective-C中):
- (void)scrollViewDidScroll:(UIScrollView *)scrollView{ NSString *direction = ([scrollView.panGestureRecognizer translationInView:scrollView.superview].y >0)?@"up":@"down"; NSLog@(@"%@",direction); }
在迅速:
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) { if scrollView.panGestureRecognizer.translation(in: scrollView).y < 0 { print("down") } else { print("up") } }
你也可以在scrollViewDidScroll中做到这一点。
我检查了一些答案,并通过在UIScrollView类别中放入所有东西来详细解释AnswerBot答案。 “lastContentOffset”保存在uiscrollview里面,然后它只是一个调用的问题:
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView { [scrollView setLastContentOffset:scrollView.contentOffset]; } - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate { if (scrollView.scrollDirectionX == ScrollDirectionRight) { //Do something with your views etc } if (scrollView.scrollDirectionY == ScrollDirectionUp) { //Do something with your views etc } }
源代码https://github.com/tehjord/UIScrollViewScrollingDirection
打开页面时,可以使用这些代码。
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView { self.lastPage = self.currentPage; CGFloat pageWidth = _mainScrollView.frame.size.width; self.currentPage = floor((_mainScrollView.contentOffset.x - pageWidth / 2) / pageWidth) + 1; if (self.lastPage < self.currentPage) { //go right NSLog(@"right"); }else if(self.lastPage > self.currentPage){ //go left NSLog(@"left"); }else if (self.lastPage == self.currentPage){ //same page NSLog(@"same page"); } }
代码解释自己我猜。 CGFloat差异1和差异2在同一类私有接口中声明。 好,如果contentSize保持不变。
- (void)scrollViewDidScroll:(UIScrollView *)scrollView { CGFloat contentOffSet = scrollView.contentOffset.y; CGFloat contentHeight = scrollView.contentSize.height; difference1 = contentHeight - contentOffSet; if (difference1 > difference2) { NSLog(@"Up"); }else{ NSLog(@"Down"); } difference2 = contentHeight - contentOffSet; }
好吧,对我来说这个实现工作真的很好:
@property (nonatomic, assign) CGPoint lastContentOffset; - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView { _lastContentOffset.x = scrollView.contentOffset.x; _lastContentOffset.y = scrollView.contentOffset.y; } - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView { if (_lastContentOffset.x < (int)scrollView.contentOffset.x) { // moved right NSLog(@"right"); } else if (_lastContentOffset.x > (int)scrollView.contentOffset.x) { // moved left NSLog(@"left"); }else if (_lastContentOffset.y<(int)scrollView.contentOffset.y){ NSLog(@"up"); }else if (_lastContentOffset.y>(int)scrollView.contentOffset.y){ NSLog(@"down"); [self.txtText resignFirstResponder]; } }
所以这将激发textView在拖动结束后closures
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset { NSLog(@"px %f py %f",velocity.x,velocity.y);}
使用这个滚动视图的委托方法。
如果y坐标的速度是+ ve滚动视图向下滚动,如果是-ve滚动视图向上滚动。 类似的左右滚动可以使用x坐标来检测。
Short&Easy会是,只要检查速度值,如果它大于零,那么它向左滚动其他的权利:
func scrollViewWillEndDragging(scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) { var targetOffset = Float(targetContentOffset.memory.x) println("TargetOffset: \(targetOffset)") println(velocity) if velocity.x < 0 { scrollDirection = -1 //scrolling left } else { scrollDirection = 1 //scrolling right } }
如果您使用UIScrollView和UIPageControl,则此方法也将更改PageControl的页面视图。
func scrollViewWillEndDragging(scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) { let targetOffset = targetContentOffset.memory.x let widthPerPage = scrollView.contentSize.width / CGFloat(pageControl.numberOfPages) let currentPage = targetOffset / widthPerPage pageControl.currentPage = Int(currentPage) }
感谢@Esq的Swift代码。
Swift 2.2 简单的解决scheme ,可以跟踪单个方向和多个方向而不会丢失
// Keep last location with parameter var lastLocation:CGPoint = CGPointZero // We are using only this function so, we can // track each scroll without lose anyone override func scrollViewWillBeginDragging(scrollView: UIScrollView) { let currentLocation = scrollView.contentOffset // Add each direction string var directionList:[String] = [] if lastLocation.x < currentLocation.x { //print("right") directionList.append("Right") } else if lastLocation.x > currentLocation.x { //print("left") directionList.append("Left") } // there is no "else if" to track both vertical // and horizontal direction if lastLocation.y < currentLocation.y { //print("up") directionList.append("Up") } else if lastLocation.y > currentLocation.y { //print("down") directionList.append("Down") } // scrolled to single direction if directionList.count == 1 { print("scrolled to \(directionList[0]) direction.") } else if directionList.count > 0 { // scrolled to multiple direction print("scrolled to \(directionList[0])-\(directionList[1]) direction.") } // Update last location after check current otherwise, // values will be same lastLocation = scrollView.contentOffset }