1.23. Creating Scrollable Content with UIScrollView

Problem

You have content that needs to get displayed on the screen, but it requires more real estate than what the device’s screen allows for.

Solution

Use the UIScrollView class.

Discussion

Scroll views are one of the features that make iOS a really neat operating system. They are practically everywhere. You’ve been to the Clock or the Contacts apps, haven’t you? Have you seen how the content can be scrolled up and down? Well, that’s the magic of scroll views.

There really is one basic concept you need to learn about scroll views: the content size, which lets the scroll view conform to the size of what it’s displaying. The content size is a value of type CGSize that specifies the width and the height of the contents of a scroll view. A scroll view, as its name implies, is a subclass of UIView, so you can simply add your views to a scroll view using its addSubview: method. However, you need to make sure that the scroll view’s content size is set properly; otherwise, the contents inside the scroll view won’t scroll.

As an example, let’s find a big image and load it to an image view. I will add the same image that I used in Recipe 1.22: a MacBook Air image. I will add it to an image view and place it in a scroll view. Then I will use the contentSize of the scroll view to make sure this content size is equal to the size of the image (width and height). First, let’s start with the implementation file of our view controller:

#import "ViewController.h"

@interface ViewController ()
@property (nonatomic, strong) UIScrollView *myScrollView;
@property (nonatomic, strong) UIImageView *myImageView;
@end

@implementation ViewController

And let’s place the image view inside the scroll view:

- (void)viewDidLoad{
    [super viewDidLoad];

    UIImage *imageToLoad = [UIImage imageNamed:@"MacBookAir"];
    self.myImageView = [[UIImageView alloc] initWithImage:imageToLoad];
    self.myScrollView = [[UIScrollView alloc] initWithFrame:self.view.bounds];
    [self.myScrollView addSubview:self.myImageView];
    self.myScrollView.contentSize = self.myImageView.bounds.size;
    [self.view addSubview:self.myScrollView];

}

If you now load up the app in iOS Simulator, you will see that you can scroll the image horizontally and vertically. The challenge here, of course, is to provide an image that is bigger than the screen’s boundaries. For example, if you provide an image that is 20×20 pixels, the scroll view won’t be of much use to you. In fact, it would be wrong to place such an image into a scroll view, as the scroll view would practically be useless in that scenario. There would be nothing to scroll because the image is smaller than the screen size.

One of the handy features of UIScrollView is support for delegation, so that it can report really important events to the app through a delegate. A delegate for a scroll view must conform to the UIScrollViewDelegate protocol. Here are some of the methods defined in this protocol:

scrollViewDidScroll:

Gets called whenever the contents of a scroll view get scrolled.

scrollViewWillBeginDecelerating:

Gets called when the user scrolls the contents of a scroll view and lifts his finger off the screen as the scroll view scrolls.

scrollViewDidEndDecelerating:

Gets called when the scroll view has finished scrolling its contents.

scrollViewDidEndDragging:willDecelerate:

Gets called when the user finishes dragging the contents of the scroll view. This method is very similar to the scrollViewDidEndDecelerating: method, but you need to bear in mind that the user can drag the contents of a scroll view without scrolling the contents. She can simply put her finger on the content, move her finger to any location on the screen and lift her finger, without giving the contents any momentum to move. This is dragging as opposed to scrolling. Scrolling is similar to dragging, but the user will give momentum to the contents’ movement by lifting her finger off the screen while the content is being dragged around, and not waiting for the content to stop before lifting her finger off the screen. Dragging is comparable to holding down the accelerator in a car or pedaling on a bicycle, whereas scrolling is comparable to coasting in a car or on a bicycle.

So let’s add some fun to our previous app. Now the goal is to set the alpha level of the image inside our image view to 0.50f (half transparent) when the user starts to scroll the scroll view and set this alpha back to 1.0f (opaque) when the user finishes scrolling. Let’s begin by conforming to the UIScrollViewDelegate protocol:

#import "ViewController.h"

@interface ViewController () <UIScrollViewDelegate>
@property (nonatomic, strong) UIScrollView *myScrollView;
@property (nonatomic, strong) UIImageView *myImageView;
@end

@implementation ViewController

Then let’s implement this functionality:

- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
    /* Gets called when user scrolls or drags */
    self.myScrollView.alpha = 0.50f;
}

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView{
    /* Gets called only after scrolling */
    self.myScrollView.alpha = 1.0f;
}

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView
                  willDecelerate:(BOOL)decelerate{
    /* Make sure the alpha is reset even if the user is dragging */
    self.myScrollView.alpha = 1.0f;
}

- (void)viewDidLoad{
    [super viewDidLoad];

    UIImage *imageToLoad = [UIImage imageNamed:@"MacBookAir"];
    self.myImageView = [[UIImageView alloc] initWithImage:imageToLoad];
    self.myScrollView = [[UIScrollView alloc] initWithFrame:self.view.bounds];
    [self.myScrollView addSubview:self.myImageView];
    self.myScrollView.contentSize = self.myImageView.bounds.size;
    self.myScrollView.delegate = self;
    [self.view addSubview:self.myScrollView];

}

As you might have noticed, scroll views have indicators. An indicator is the little tracking line that appears on the sides of a scroll view when its contents are getting scrolled and moved. Figure 1-63 shows an example.

Black indicators appearing on the right and bottom of a scroll view

Figure 1-63. Black indicators appearing on the right and bottom of a scroll view

Indicators simply show the user where the current view is in relation to the content (top, halfway down, etc.). You can control what the indicators look like by changing the value of the indicatorStyle property. For instance, here I have changed the indicator style of my scroll view to white:

self.myScrollView.indicatorStyle = UIScrollViewIndicatorStyleWhite;

One of the great features of scroll views is that they allow pagination. Pagination is the same as scrolling, but locks the scrolling when the user moves to the next page. You have perhaps already seen this if you’ve ever used the Photos app on the iPhone or iPad. When you are looking at photos, you can swipe between them. Each swipe brings the next or previous photo onto the screen. Your swiping never scrolls all the way to the end or all the way to the start. When the scrolling starts, the scroll view detects the next image to display, scrolls and bounces to that image, and stops the scrolling animation. That’s pagination. If you haven’t tried it already, I urge you to do so, because otherwise I could go on and on and none of this would make sense unless you looked at an app that supports pagination.

For this example code, I’ve prepared three images: an iPhone, an iPad, and a MacBook Air. I’ve placed them in their individual image views and added them to a scroll view. Then we can enable pagination by setting the value of the pagingEnabled property of the scroll view to YES:

- (UIImageView *) newImageViewWithImage:(UIImage *)paramImage
                                  frame:(CGRect)paramFrame{

    UIImageView *result = [[UIImageView alloc] initWithFrame:paramFrame];
    result.contentMode = UIViewContentModeScaleAspectFit;
    result.image = paramImage;
    return result;

}

- (void)viewDidLoad{
    [super viewDidLoad];

    UIImage *iPhone = [UIImage imageNamed:@"iPhone"];
    UIImage *iPad = [UIImage imageNamed:@"iPad"];
    UIImage *macBookAir = [UIImage imageNamed:@"MacBookAir"];

    CGRect scrollViewRect = self.view.bounds;

    self.myScrollView = [[UIScrollView alloc] initWithFrame:scrollViewRect];
    self.myScrollView.pagingEnabled = YES;
    self.myScrollView.contentSize = CGSizeMake(scrollViewRect.size.width * 3.0f,
                                               scrollViewRect.size.height);
    [self.view addSubview:self.myScrollView];

    CGRect imageViewRect = self.view.bounds;
    UIImageView *iPhoneImageView = [self newImageViewWithImage:iPhone
                                                         frame:imageViewRect];
    [self.myScrollView addSubview:iPhoneImageView];

    /* Go to next page by moving the x position of the next image view */
    imageViewRect.origin.x += imageViewRect.size.width;
    UIImageView *iPadImageView = [self newImageViewWithImage:iPad
                                                       frame:imageViewRect];
    [self.myScrollView addSubview:iPadImageView];

    /* Go to next page by moving the x position of the next image view */
    imageViewRect.origin.x += imageViewRect.size.width;
    UIImageView *macBookAirImageView =
    [self newImageViewWithImage:macBookAir
                          frame:imageViewRect];
    [self.myScrollView addSubview:macBookAirImageView];
}

Now we have three pages of scrollable content (Figure 1-64).

Scrolling through pages in a page-enabled scroll view

Figure 1-64. Scrolling through pages in a page-enabled scroll view

Get iOS 7 Programming Cookbook now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.