11

I have a UIScrollView with a UIView inside. I want to lock the x-axis so that the view is only scrolled vertically. How do I enable directional locking?

Ketan P
  • 4,259
  • 3
  • 30
  • 36
RexOnRoids
  • 14,002
  • 33
  • 96
  • 136

3 Answers3

16

First, set the UIScrollView's contentSize to have a width that is equal to or less than the width of the UIScrollView's frame.

Next, set UIScrollView's alwaysBounceHorizontal to NO. This will prevent the scroll view from "rubber banding" even though you've told it there's no more horizontal content to display.

UIScrollView *scrollView;
CGSize size = scrollView.contentSize;
size.width = CGRectGetWidth(scrollView.frame);
scrollView.contentSize = size;
scrollView.alwaysBounceHorizontal = NO;

It doesn't matter what's actually inside the scroll view.

Swift 5.0

let scrollView = UIScrollView() // Or however you want to initialize it
var size = scrollView.contentSize
size.width = scrollView.frame.width
scrollView.contentSize = size
scrollView.alwaysBounceHorizontal = false
Sergio
  • 1,610
  • 14
  • 28
Nick Farina
  • 4,470
  • 2
  • 32
  • 30
  • This helped me even in Xcode 7. I had to turn off bounce in the storyboard to get the direction lock to work (even though I had direction lock enabled turned on already). – Mike Taverne Oct 05 '15 at 23:42
2

You'll be subclassing UIScrollView and overriding the touchesBegan:withEvent: method, touchesMoved:withEvent: method, and the touchesEnded:withEvent: method.

You'll use those methods, along with the start and end points of a touch, to calculate what kind of touch event took place: was it a simple tap, or a horizontal or vertical swipe?

If it is a horizontal swipe, you cancel the touch event.

Take a look at the source code here to learn how you might get started.

Community
  • 1
  • 1
Alex Reynolds
  • 95,983
  • 54
  • 240
  • 345
  • Better to inform the scrollview of your intent I think rather than manually cancelling touches yourself. With your approach, if you begin dragging horizontally (even if by accident), you cannot then drag vertically and have it work. – Nick Farina May 14 '09 at 07:49
  • I was afraid someone would say this...A sure-fire way to lock an axis i'm sure, but I think I'll keep searching for a lazier way to do this. Thanks for your response though. Nick Farina solved my issue by suggesting to: First, set the UIScrollView's "contentSize" to have a width that is equal to or less than the width of the UIScrollView's frame. Next, set UIScrollView's "alwaysBounceHorizontal" to NO. This will prevent the scroll view from "rubber banding" even though you've told it there's no more horizontal content to display. – RexOnRoids May 14 '09 at 07:50
0
#import <UIKit/UIKit.h>


@interface DemoButtonViewController : UIViewController <UIScrollViewDelegate>

@property (nonatomic, strong) UIScrollView *filterTypeScrollView;
@property (nonatomic, strong) UIBarButtonItem *lockButton;

- (void)lockFilterScroll:(id)sender;

@end

#import "DemoButtonViewController.h"

@implementation DemoButtonViewController

@synthesize filterTypeScrollView;
@synthesize lockButton;

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];

    if (self) 
    {
        // Custom initialization
    }
    return self;
}

- (void)didReceiveMemoryWarning
{
    // Releases the view if it doesn't have a superview.
    [super didReceiveMemoryWarning];

    // Release any cached data, images, etc that aren't in use.
}

- (void)viewDidLoad
{
    [super viewDidLoad];

    self.view.backgroundColor = [UIColor darkGrayColor];
    self.filterTypeScrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 130, self.view.frame.size.width, 320)];
    filterTypeScrollView.contentSize = CGSizeMake(self.view.frame.size.width*4, 320);
    filterTypeScrollView.pagingEnabled = YES;
    filterTypeScrollView.delegate = self;
    [self.view addSubview:filterTypeScrollView];

    UIToolbar *lockbar = [[UIToolbar alloc] initWithFrame:CGRectMake(0, 450, self.view.frame.size.width, 30)];
    lockbar.barStyle = UIBarStyleBlackTranslucent;
    self.lockButton = [[UIBarButtonItem alloc] initWithTitle:@"Lock Filter Scroll" style:UIBarButtonItemStylePlain target:self action:@selector(lockFilterScroll:)];
    [lockbar setItems:[NSArray arrayWithObjects:[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil],lockButton,nil]]; 
    [self.view addSubview:lockbar];
}

- (void)viewDidUnload
{
    [super viewDidUnload];
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    // Return YES for supported orientations  
    return (interfaceOrientation == UIInterfaceOrientationPortrait);
}

- (void)lockFilterScroll:(id)sender
{
    filterTypeScrollView.scrollEnabled = !filterTypeScrollView.scrollEnabled;

    if (filterTypeScrollView.scrollEnabled) 
    {
        [lockButton setTitle:@"Lock Filter Scroll"];
    } 
    else {
        [lockButton setTitle:@"Unlock Filter Scroll"];
    }
}

@end
Ayan Sengupta
  • 5,238
  • 2
  • 28
  • 40
Durul Dalkanat
  • 7,266
  • 4
  • 35
  • 36