Search code examples
objective-cioscocoa-touchuinavigationbaruiinterfaceorientation

Objects position incorrectly in view and after device orientation changes


I am creating an iOS application. It functions within a navigation controller. In a view I've recently built, the UITextViews and UIImageView have been displaying incorrectly, at the wrong coordinates. I believe that this may be due to the UINavigationBar.

When I set up the view to mirror that in the Interface Builder, it actually displayed the view wrongly. I again assume that this is due to the UINavigationBar. I managed to "fix" this by altering the Interface Builder coordinates until it displayed correctly. This is what the Interface Builder now looks like:

I want the view to work in both device orientations (portrait and landscape). The objects in the view can't easily be told to rotate, so I am positioning the views programmatically. The code is as follows:

-(void) adjustViewsForOrientation:(UIInterfaceOrientation)orientation {
    if (orientation == UIInterfaceOrientationPortrait) {
        appInfo.frame = CGRectMake(20, 19, 280, 90);
        updateInfo.frame = CGRectMake(20, 117, 280, 86);
        authorInfo.frame = CGRectMake(20, 229, 172, 200);
        authorImage.frame = CGRectMake(190, 274, 110, 110);
    }
    else if (orientation == UIInterfaceOrientationLandscapeLeft || orientation == UIInterfaceOrientationLandscapeRight) {
        appInfo.frame = CGRectMake(11, 44, 199, 124);
        updateInfo.frame = CGRectMake(218, 53, 242, 124);
        authorInfo.frame = CGRectMake(11, 180, 340, 90);
        authorImage.frame = CGRectMake(350, 170, 110, 110);
    }
}

-(void) willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
    [self adjustViewsForOrientation:toInterfaceOrientation];
}

-(void) viewWillAppear:(BOOL)animated {
    [self adjustViewsForOrientation:self.interfaceOrientation];

    [super viewWillAppear:animated];
}

Here is my main problem: When the view is rotated to landscape, the objects to not position directly. They do not follow what is described in the coordinates to be called during rotation.

This problem also occurs when rotated back to portrait:

How can I resolve this? Thank you in advance.


Solution

  • You could do something complicated using sizeWithFont:constrainedToSize to figure out the size that the various text boxes need to be at the new width to fit the text you want to put in them, but probably easier would be to eliminate the UILabel/UITextView's and the UIImageView, and just create a UIWebView that fills the whole view and use it's loadHTMLString:baseURL to supply a HTML string that includes your copy and a link to your image in your bundle.

    Update:

    The problem is that you (a) you clearly have autoResizingMasks set; and (b) you're setting your new frames too early (so once the reorientation takes place, the autoResizingMask adjusts them again).

    So, a couple of thoughts:

    First, you probably have the auto sizing masks turned on in Interface Builder, e.g. something like:

    size inspector with auto resizing on

    So, turn off the autoresizing masks:

    size inspector with auto resizing off

    Second, while I'm sure you'll do that in IB, FYI you can also turn it off programmatically:

    appInfo.autoresizingMask = 0;
    updateInfo.autoresizingMask = 0;
    authorInfo.autoresizingMask = 0;
    authorImage.autoresizingMask = 0;
    

    Third, you might want to change the frame coordinates after autoresizing has a chance to screw up your controls. Typically, I'll see guys who defer this sort of frame adjustments to didRotateFromInterfaceOrientation rather than willRotateToInterfaceOrientation because (a) you have the coordinates of the new orientation (and because navigation controllers are different heights in landscape v portrait, this can be important, though maybe not in your case); and coincidentally (b) you remedy any adjustments that some unintended autoresizing masks may have introduced.

    Personally, I've taken to resetting my frames in viewWillLayoutSubviews in iOS 5 and later, and in didRotateFromInterfaceOrientation and viewWillAppear in earlier versions of iOS (I do all of this programmatically in my code). The iOS 5 alternative of viewWillLayoutSubviews is superior because it sets the frames right before the rotation animation, and animates the changing of the frames with the rotation animation, which is very, subtle change, but very slick. Once you start noticing apps that do this right, you'll always want to do it that way.