简体   繁体   中英

viewDidLayoutSubviews in Autolayout with rotation on UIPageViewController

I have a UIPageViewController whose view controllers contain a UIScrollView which contains a UIImageView . In the loaded view controller viewWillAppear , the bounds are incorrect if the view is rotated. This is where I was trying to position the image view dynamically based on the size of the image according to this . I tried to use (void)viewDidLayoutSubviews but I need to call [self.view layoutSubviews] or I get an assertion error. When I added that, then I can no longer zoom the image view. You can see it tries to zoom but get resized back to the minimum zoom.

I am completely new to auto layout and was trying to see if I was missing something.

My current fix involves checking the bounds of the frame to see if the orientation matches the bounds. If so then I use the bounds as is. If not, then I switch the height and width in my calculations.

I'd like to know how to properly use viewDidLayoutSubviews without it taking away my ability to zoom in on the image view.

From two of the answers, it looks like viewDidLayoutSubviews maybe the wrong place to layout the views. I'm trying to find where this should be done and have the view's know their bounds and orientation but before they are visible on the screen. From my reading, I thought that was viewDidLayoutSubviews but it seems using that could lead to an infinite loop.

From two of the answers, it looks like viewDidLayoutSubviews maybe the wrong place to layout the views. I'm trying to find where this should be done and have the view's know their bounds and orientation but before they are visible on the screen.

I would subclass UIScrollView and override there layoutSubviews , which will be called for you by the system only when needed and when all the variables (orientation, size, etc.) have the proper values.

First, you shouldn't call -layoutSubviews yourself. From the UIView documentation for that method :

You should not call this method directly. If you want to force a layout update, call the setNeedsLayout method instead to do so prior to the next drawing update. If you want to update the layout of your views immediately, call the layoutIfNeeded method.

Even without the warning, calling that method from -viewDidLayoutSubviews seems like a recipe for problems -- -viewDidLayoutSubviews is called to notify your view controller that its view just laid out it subviews, so calling [self.view layoutSubviews] might easily lead to a loop.

In the loaded view controller viewWillAppear, the bounds are incorrect if the view is rotated.

Remember that when -viewWillAppear is called, the view hasn't yet been added to a view hierarchy . Given that, it seems like your current approach isn't a bad one. The view doesn't yet have it's bounds set (because it hasn't been added yet), so you can determine what the bounds will be and use that to set up your view. Another, probably better approach would be to use -viewDidAppear , since your view will have been added at that point and should have the correct bounds by then.

When I added that, then I can no longer zoom the image view. You can see it tries to zoom but get resized back to the minimum zoom.

It sounds like you're not taking the zoomed state into account when you position your image view. Make sure that your code in -viewDidLayoutSubviews isn't negating the work that the scroll view is doing.

You shouldn't need to change the position of your image view at all -- that's exactly the kind of thing that auto layout is good at. I notice that the ImageScrollViewController.m file that you're using as an example doesn't change the location of the image view.

I'm not totally sure about your view hierarchy, but this answer explains how to do it if you have a view controller which view contains a UIScrollView. (Said view controller is later shown in the UIPageViewController but that's irrelevant here).

Override the following method in your view controller which controls the scroll view:

- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
    [super willAnimateRotationToInterfaceOrientation:toInterfaceOrientation duration:duration];

    CGRect bounds = self.view.bounds;
    self.scrollView.frame = bounds;

    //
    // Calculate a rect for your image view according to bounds
    //
    self.imageView.frame = calculatedFrame;
}

The problem is that your view will layout during scrolling/zooming, and if you position the view in viewDidLayoutSubviews of a view controller controlling a scrollView, you will position it every time the user scrolls or zooms.

I'm suspecting that your view controller might be controlling a UIScrollView directly, (instead of controlling a view that has a UIScrollView subview), and in that case, just remove the self.scrollView.frame = self.bounds row.

You should never call [self.view layoutSubviews] from viewDidLayoutSubviews. In fact, you should never call that function at all. If you need to update your layout, you call [self.view setNeedsLayout]. But when your view has performed its layoutSubviews, viewDidLayoutSubviews is automatically called. That means that if you call that function from there, you will get an infinite loop.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM