Clean autorotation transitions in a paging UIScrollView
Asked Answered
C

4

12

I have a paging UIScrollView in which the user pages horizontally through images, like Apple's Photos.app. That works, but now I'm trying to add rotation support.

I've got the view rotating OK and have managed to set the contentSize, bounds, and subviews' frames properly to adapt to the different orientations. So before and after the rotation, everything is OK.

However, the transitions themselves are awkward. The first image rotates perfectly, as if the axis of rotation is in the dead center of the image (scrollview frame). The second image "swings" in because the axis of rotation is in the same place: the center of the first image. The farther away I get from the first image, the faster the "swing."

I can probably mask this by overlaying an opaque UIView before rotation and hiding it after. But that's a hack. There must be an elegant way to do this...

Codling answered 23/7, 2010 at 21:33 Comment(0)
T
40

Frankly, I don't know what you're doing, since you haven't shown us much at all.

But!

I created a sample project with a few views in a scroll view, and it works fine. Feel free to pick it apart as you wish. It works by creating 5 views, and adding them to the scroll view. Then after these views are set up for the first time, and every time the application rotates, it calls my method alignSubviews to lay them out at the right page locations and make them the same size as the scroll view, while updating the scroll view's contentSize. Before the rotation occurs, it keeps track of what page the scroll view is currently on, and then resets it to that page during the rotation (because the page size has to change).

Download "Rotolling"!

screenshot

Tew answered 28/7, 2010 at 4:21 Comment(4)
Excellent example! I didn't post any code because I knew that what I had would need to be scrapped. The moral of the story is if I find myself manually setting and resetting view frames all over the place, I'm doing it wrong.Codling
Thanks for great example. Need to do one fix for successful launch on iOS 6: [window setRootViewController:viewController]; to application:didFinishLaunchingWithOptions method.Carlie
Thank you for a great example that solved me a huge problem !!Wristband
Can you re upload the file? Is not working any more. It says Access denyBastard
B
7

I note this additional information for later reference myself. I solved this with idea from @jtvandes' solution. However, in my case, it was enough with these implementations. Forcing layout again broke my view.

- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration 
{
    currentPageOffset = [hostingScrollView contentOffset].x / [hostingScrollView bounds].size.width;
}
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation duration:(NSTimeInterval)duration 
{
    //  Layout changed instantly at this time.
    //  So we have to force to set offset instantly by setting animation to NO.
    [hostingScrollView setContentOffset:CGPointMake([hostingScrollView bounds].size.width * currentPageOffset, 0.0f) animated:NO];
}
Burkhart answered 29/3, 2011 at 11:34 Comment(0)
B
1

It sounds like you are applying the magic, needed to get the rotated version on screen at the right place, the wrong way.

Since you didn't share any code, the following is just guesswork. Presumably, you apply a rotation and a translation. It sounds like you are rotating the contents of the UIScrollView. Getting this right will be tricky. It's hard to say anything conclusive without code, but if you only have 1 rotation and 1 translation, you're doing it the wrong way. You need to translate the current view to the pivot point, rotate, and translate back. Otherwise the translation will be part of the rotation (hence the big swing).

You'd better rotate the UIScrollView itself. It will always be in a defined location (namely: the screen) with a defined center. The content will then rotate with it.

Breeks answered 28/7, 2010 at 3:0 Comment(0)
N
1

A simple solution that works well is to register for notifications when the contentSize of the UIScrollView is changed and then update the contentOffset at that moment.

In your view loading method add this line:

[self addObserver:theScrollView forKeyPath:@"contentSize" options:0 context:nil];

In your view unloading method add this line:

[self removeObserver:self forKeyPath:@"contentSize"];

Catch the notification:

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    theScrollView.contentOffset = page * theScrollView.bounds.width;
}
Nicolasanicolau answered 1/6, 2013 at 0:46 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.