views:

106

answers:

1

I am having trouble using a UIScrollView on the IPhone when dealing with rotation. I used Apple's PageControl example as a model, but am creating the scrollview myself in a View.

1) The scrollview works as expected when loading in default Portrait, scrolling left to right horizontally

2) Out of the box, when rotating CW and reinitializing mostly turns it on its side and it now rotates vertically down instead of the desired horizontal still. It seems the scrollview frame width/height don't change, just the orientation of itself.

3) I tried to add some orientation detection logic to properly size and page the scrollview based on orientation which has kind of worked, but only if I rotate CCW to landscape. If I go CW it starts on the right and scrolls to the left, and things don't work.

Obviously this is getting hackish, so is there a proper way of setting up my UIScrollview and reintializing it with rotations so it always scrolls left to right?


- (void)loadView {
    [self setupPage];
}

-(void) setupPage {
  // lazy init view controllers
  NSMutableArray *controllers = [[NSMutableArray alloc] init];
    for (unsigned i = 0; i = kNumberOfPages) return;

    // replace the placeholder if necessary
    MyViewController *controller = [viewControllers objectAtIndex:page];
    if ((NSNull *)controller == [NSNull null]) {
        controller = [[MyViewController alloc] initWithPageNumber:page];
        [viewControllers replaceObjectAtIndex:page withObject:controller];
        [controller release];
    }

    // add the controller's view to the scroll view
    if (nil == controller.view.superview) {
        CGRect frame = scrollView.frame;

        int x = 0;
        int y = 0;
        UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
        if(orientation == UIDeviceOrientationLandscapeLeft || orientation == UIDeviceOrientationLandscapeRight){
            x = 0;
            y = frame.size.height * page; 
        } else {
            x = frame.size.width * page;
            y = 0;
        }

        frame.origin.x = x;
        frame.origin.y = y;
        controller.view.frame = frame;
        [scrollView addSubview:controller.view];
    }
}

- (void)scrollViewDidScroll:(UIScrollView *)sender {
    // We don't want a "feedback loop" between the UIPageControl and the scroll delegate in
    // which a scroll event generated from the user hitting the page control triggers updates from
    // the delegate method. We use a boolean to disable the delegate logic when the page control is used.
    if (pageControlUsed) {
        // do nothing - the scroll was initiated from the page control, not the user dragging
        return;
    }

    // Switch the indicator when more than 50% of the previous/next page is visible
    CGFloat pageWidth = scrollView.frame.size.width;
    int offset = 0;
    UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
    if(orientation == UIDeviceOrientationLandscapeLeft || orientation == UIDeviceOrientationLandscapeRight){
        pageWidth = scrollView.frame.size.height;
        offset = scrollView.contentOffset.y; 
    } else {
        pageWidth = scrollView.frame.size.width;
        offset = scrollView.contentOffset.x ;
    }

    //CGFloat pageWidth = scrollView.frame.size.width;
    int page = floor((offset - pageWidth / 2) / pageWidth) + 1;
    pageControl.currentPage = page;

    // load the visible page and the page on either side of it (to avoid flashes when the user starts scrolling)
    [self loadScrollViewWithPage:page - 1];
    [self loadScrollViewWithPage:page];
    [self loadScrollViewWithPage:page + 1];

    // A possible optimization would be to unload the views+controllers which are no longer visible
}

// At the begin of scroll dragging, reset the boolean used when scrolls originate from the UIPageControl
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
    pageControlUsed = NO;
}

// At the end of scroll animation, reset the boolean used when scrolls originate from the UIPageControl
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
    pageControlUsed = NO;
}


- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation 
{
    [self setupPage];
}

A: 

You're not changing the contentSize of your scrollview, so when it rotates it doesn't know it's wider than it is taller.

Also, as a shortcut, each VC has a interfaceOrientation property already set, and there are functions built in to detect orientation: UIInterfaceOrientationIsPortrait(self.interfaceOrientation) and UIInterfaceOrientationIsLandscape(self.interfaceOrientation).

Lastly, just a visual thing, using willRotateToInterfaceOrientation:duration is a bit nicer on the eyes, and the user never perceives a change as it happens almost simultaneously with the rotation.

MishieMoo