How can I force a UIScrollView
in which paging and scrolling are on to only move vertically or horizontally at a given moment?
My understanding is that
I used following method to solve this problem, hope it helps you.
First define following variables in your controllers header file.
CGPoint startPos;
int scrollDirection;
startPos will keep the contentOffset value when your delegate receives scrollViewWillBeginDragging message. So in this method we do this;
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView{
startPos = scrollView.contentOffset;
scrollDirection=0;
}
then we use these values to determine users intended scroll direction in scrollViewDidScroll message.
- (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];
}
}
finally we have to stop all update operations when user end dragging;
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate{
if (decelerate) {
scrollDirection=3;
}
}
I managed to solve this by implementing scrollViewDidBeginDragging(_:) and looking at the velocity on the underlying UIPanGestureRecognizer.
NB: With this solution, every pan that would have been diagonal will be ignored by the 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
}
}
Need to reset _isHorizontalScroll
to NO in touchesEnded
and touchesCancelled
.
If you are having a big scrollview... and you want to restrict the diagonal scrolling http://chandanshetty01.blogspot.in/2012/07/restricting-diagonal-scrolling-in.html
For those looking in Swift at @AndreyTarantsov second solution its like this in code:
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
}
}
what we are doing here is to keep the current position just before scrollview gets dragged and then check if either x has increased or decreased compared to the current position of x. If yes, then set the pagingEnabled to true, if no (y has increased/decreased) then set to pagingEnabled to false
Hope that is useful to new comers!
Thanks Tonetel. I slightly modified your approach, but it was exactly what I needed to prevent horizontal scrolling.
self.scrollView.contentSize = CGSizeMake(self.scrollView.contentSize.width, 1);