21

I am using UIScrollView and an image in it as paging one image per page. I have a problem while rotating the iPhone

When I rotate the iPhone then scrollViewDidScroll (Scroll view delegate method) is calling. Due to this, my paging is disturbed and the page number changes.

What is the solution?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Amit Battan
  • 2,968
  • 2
  • 32
  • 68
  • having same issue..., did you managed to solve the problem? – negersiu Jan 20 '11 at 19:09
  • 1
    @Maciulis..What exactly I was done at that time...but I check the code.. and I am not using the method scrollViewDidScroll and using scrollViewDidEndDecelerating.. and use a global variable for current page number.. hope this will help you or tell me exactly what you want – Amit Battan Jan 21 '11 at 05:06

6 Answers6

25

Raphaël's answer is an excellent description of the problem, and a neat fix. I had the exact same problem and ended up fixing with a scrollingLocked flag that I set to YES (locked) before the rotation starts, and NO (unlocked) when it ends. Perhaps slightly less hacky than temporarily changing the contentSize:

- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)orientation
                                duration:(NSTimeInterval)duration
{
    self.photoViewer.scrollingLocked = YES;
}

- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromOrientation
{
    self.photoViewer.scrollingLocked = NO;
}

- (void)scrollViewDidScroll:(UIScrollView*)scrollView
{
    if (self.scrollingLocked)
    {
        return;
    }
    /* do normal scrollViewDidScroll: stuff */
}
Community
  • 1
  • 1
Ben Hoyt
  • 10,694
  • 5
  • 60
  • 84
  • what component is the photoViewer? - because it is not a scroll, nor a view –  Aug 26 '12 at 14:32
  • 3
    @matheszabi, he's using an BOOL ivar to track. Alternatively, you could just use the `UIScrollView`'s `scrollEnabled` built-in property. By the way, this still for some reason triggers `scrollViewDidScroll` when on the last picture. – Steven Mar 12 '13 at 01:11
  • Is there a similar solution for iOS 8? – vivek241 Dec 16 '14 at 06:08
  • @Steven I first upvoted your comment but in the end this did not work for me because the contentOffset was changed after it anyway, as you already noted... – anneblue Nov 24 '15 at 14:45
14

I found a strange undocumented behavior when rotating a paged UIScrollView.

When the scrollview is positioned at the last page and the user changes the orientation, the OS scrolls the UIScrollView a few pixels back to compensate for the difference between height and width.

Basically I received the following calls for any page.

willRotateToInterfaceOrientation:duration
willAnimateRotationToInterfaceOrientation:duration:
didRotateFromInterfaceOrientation:

And for the last page:

willRotateToInterfaceOrientation:duration
scrollViewDidScroll:
willAnimateRotationToInterfaceOrientation:duration:
didRotateFromInterfaceOrientation:

That messed up with my pages too. The problem is that in willRotate, the bounds have not been updated by the OS yet, and in willAnimate you have the new bounds and can compute the new size, but it's too late...

- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {

    CGSize tempSize = [self.pagingScrollView contentSize];
    NSUInteger padding = abs(pagingScrollView.frame.size.width - pagingScrollView.frame.size.height);
    tempSize.width += padding;
    [self.pagingScrollView setContentSize:tempSize];
    [...]
}
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation duration:(NSTimeInterval)duration{
    CGSize newSize = ... // Compute new content size based on new orientation
    [self.pagingScrollView setContentSize:newSize];
}

This is just a workaround, but I spent countless hours on this issue and could not find an elegant solution.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Raphaël Mor
  • 356
  • 2
  • 14
1

Swift 4 solution:

    override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
        super.traitCollectionDidChange(previousTraitCollection)
        lui.l("apply previousTraitCollection: \(previousTraitCollection)")
        canScroll = true
    }

ergunkocak
  • 3,334
  • 1
  • 32
  • 31
1

You can try this method for Swift:

override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
    super.viewWillTransition(to: size, with: coordinator)
    coordinator.animate(alongsideTransition: { _ in
      // Execute before rotation
    }) { _ in
      //Execute after rotation
    }
}
Max Niagolov
  • 684
  • 9
  • 26
0

My task is to allow scrolling the landscape. The design is for portait. I came up with an idea to add a ScrollView to components, or in "Embed in Scroll View" in Interface Builder. I have expected it will work, but no. I am using Xcode 4.4, iOS 5.1, (office project need support for 4.2 too), but the problem is the same.

In Stack Overflow question iPhone SDK: UIScrollView does not scroll there is one row which solve a problem.

Other try is in Stack Overflow question iOS - UIScrollView is not working (it doesn't scroll at all - the image stays fixed), and this helped me, combined with other, so here is my portait-to-scrollable landscape code:

- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromOrientation
{ 
    if( UIInterfaceOrientationIsPortrait( [[UIApplication sharedApplication] statusBarOrientation] ) ){
        scrollView.contentSize = portaitScrollSize;
    }
    else{//statusbar is Landscape
        scrollView.contentSize = landscapeScrollSize;
    }    
}

The scrollView in bound to an iVar view in Interface Builder. portaitScrollSize and landscapeScrollSize are private variables. They are initialized and doesn't change. In my.h file:

IBOutlet UIScrollView *scrollView;

In my.m file:

CGSize portaitScrollSize, landscapeScrollSize;

...

portaitScrollSize = CGSizeMake(320,440);
landscapeScrollSize = CGSizeMake(480,480);

I hope it will help somebody to add a rotating + scroll feature to a portait design.

Don't forget to allow portait+landscape on the top component:

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    return TRUE;
}
Community
  • 1
  • 1
0

In addition to Raphaël Mor's answer. If you are switching from portrait to landscape, the contentsize and the page structure will brake. Therefore, in order to maintain the current page structure just add extra content size to width:

-(void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration{
    [self.scrollView setContentSize:CGSizeMake(self.scrollView.contentSize.width + 400, self.scrollView.contentSize.height)];
}

And make sure you set the contentsize and offset again after the orientation changed:

- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
    [self.scrollView setContentSize:CGSizeMake(self.scrollView.bounds.size.width *3, self.scrollView.bounds.size.height)];
    [self.scrollView setContentOffset:CGPointMake(self.scrollView.bounds.size.width * self.pageControl.currentPage, 0) animated:NO];

}
rordulu
  • 412
  • 4
  • 12