Jahm
Jahm

Reputation: 668

UIPageControl and UIScrollView during landscape orientation

I am having problems with UIPageControl and UIScrollView.

- (void)viewDidLoad
{
    [super viewDidLoad];

    imageArray = [[NSArray alloc] initWithObjects:@"image1.jpg", @"image2.jpg", @"image3.jpg", nil];

    for (int i = 0; i < [imageArray count]; i++) {
        CGRect frame;
        frame.origin.x = self.scrollView.frame.size.width * i;
        frame.origin.y = 0;
        frame.size = self.scrollView.frame.size;

        UIImageView *imageView = [[UIImageView alloc] initWithFrame:frame];
        imageView.image = [UIImage imageNamed:[imageArray objectAtIndex:i]];
        [self.scrollView addSubview:imageView];
    }
    scrollView.contentSize = CGSizeMake(scrollView.frame.size.width * [imageArray count], scrollView.frame.size.height);

    scrollView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin;
    pageControl.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin;
}

#pragma mark - UIScrollView Delegate

- (void)scrollViewDidScroll:(UIScrollView *)sender
{
    CGFloat pageWidth = self.scrollView.frame.size.width;
    int page = floor((self.scrollView.contentOffset.x - pageWidth / 2) / pageWidth) + 1;
    self.pageControl.currentPage = page;
}

The problem occurs when I change the orientation to landscape. image1.jpg shares the view with image2.jpg, which is not what I want.

But if I change the orientation back to portrait, the view is doing fine.

Upvotes: 0

Views: 2480

Answers (1)

Charlie Price
Charlie Price

Reputation: 1376

The problem is that the page size has changed (i.e. the width of the scrollview) but the width of the images hasn't changed.

One solution would be that when you rotate you re-do the computation that sizes the UIImageViews and computes the scrollview's contentSize. That is, move the for loop and assignment to scrollView.contentSize out of viewDidLoad and into a private method, say sizeImagesAndSetContentSize. Call that from willAnimateRotationToInterfaceOrientation:duration: Something like the following:

- (void)viewDidLoad
{
    [super viewDidLoad];

    imageArray = [[NSArray alloc] initWithObjects:@"image1.jpg", @"image2.jpg", @"image3.jpg", nil];

    [self sizeImagesAndSetContentSize];

    scrollView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin;
    pageControl.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin;
}


- (void) sizeImagesAndSetContentSize {
       for (int i = 0; i < [imageArray count]; i++) {
            CGRect frame;
            frame.origin.x = self.scrollView.frame.size.width * i;
            frame.origin.y = 0;
            frame.size = self.scrollView.frame.size;

            UIImageView *imageView = [[UIImageView alloc] initWithFrame:frame];
            imageView.image = [UIImage imageNamed:[imageArray objectAtIndex:i]];
            [self.scrollView addSubview:imageView];
        }
        scrollView.contentSize = CGSizeMake(scrollView.frame.size.width * [imageArray count], scrollView.frame.size.height);
}
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation duration:(NSTimeInterval)duration {
    [self sizeImagesAndSetContentSize];

}

By the way, viewDidLoad is not the place to do geometry calculations -- like things that involve the frame of the scrollView because the geometry isn't set then. in viewDidLoad the sizes of things are the sizes that they were in the storyboard (or nib) and haven't been adjusted to the actual device and the actual orientation. If it works for you so far, it is because you start out in the orientation that the storyboard is laid out. Probably the best place to do that, and thus to call sizeImagesAndSetContentSize is in the method viewDidLayoutSubviews. In fact, if you move the call there, you won't need to explicitly call it on rotation because when rotation occurs the view lays out the subviews and viewDidLayoutSubviews will get called automatically. So I'd suggest:

- (void)viewDidLoad
{
    [super viewDidLoad];

    imageArray = [[NSArray alloc] initWithObjects:@"image1.jpg", @"image2.jpg", @"image3.jpg", nil];

    scrollView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin;
    pageControl.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin;
}

- (void) sizeImagesAndSetContentSize {
       for (int i = 0; i < [imageArray count]; i++) {
            CGRect frame;
            frame.origin.x = self.scrollView.frame.size.width * i;
            frame.origin.y = 0;
            frame.size = self.scrollView.frame.size;

            UIImageView *imageView = [[UIImageView alloc] initWithFrame:frame];
            imageView.image = [UIImage imageNamed:[imageArray objectAtIndex:i]];
            [self.scrollView addSubview:imageView];
        }
        scrollView.contentSize = CGSizeMake(scrollView.frame.size.width * [imageArray count], scrollView.frame.size.height);
}

- (void)viewDidLayoutSubviews {
    [self sizeImagesAndSetContentSize];
}

Addendum:
ADDING TEXT TO THE IMAGE:

To add text on top of the image -- you could add a text element, like a label, "on top" of the scroll view. Consider the screenshot from an app below. In the top-level Cloud Slide Show View there is a full-sceen Scroll View and at the same hierarchy level but further down the list of subviews (and thus "on top" of the scrollview) are a label for a description, 3 buttons, and a UIPageControl. The label is set to resize on rotation via struts and springs (this runs on iOS 5) so you don't have to mess with it.

Storyboard structure for text overlay

This application changes the description label to match the image that is currently displayed in the scrollview page -- so it has to detect page changes. This app does that in the scroll view delegate protocol method scrollViewDidEndDecelerating: where it decides which image is on-screen when scrolling stops and fills-in the correct description.

An alternative would be to put put a label on top of each UIImage, but that is messy which is why my app doesn't do that. You'd probably want a view to contain both the UIImage and a label so that repositioning the label would make sense. Probably create that in code... For me, too much trouble.

As an aside, I should say that using page mode in a scroll view is probably not the "best practice" way to do this anymore. I'm converting my app to use a UIPageViewController which should simplify the app. For instance, you don't have to do the resize-on-rotate thing since the individual child VCs of the UIPageViewControl can resize themselves automatically.

Upvotes: 3

Related Questions