xci
xci

Reputation: 450

UIBarButtonItem with separate portrait and landscape images - layoutSubviews not called when popping a view controller from UINavigationController

I want to show completely custom buttons in UINavigationController's UIToolbar, and support portrait and landscape. Currently I have implemented a RotatingButton (a UIView subclass) class, which contains one UIButton that fills the whole RotatingButton frame. A RotatingButton also contains two images, for portrait and landscape orientations, and the heights of these images differ. Then this RotatingButton gets wrapped into UIBarButtonItem as a custom view.

Currently, in RotatingButton's layoutSubviews, I am setting the whole view's bounds, and setting the button the appropriate image for the current orientation. This works well and handles rotations as desired.

- (void) createLayout {
    [self addButtonIfNeeded];
    UIDeviceOrientation currentOrientation = [[UIDevice currentDevice] orientation];
    if(UIInterfaceOrientationIsLandscape(currentOrientation)) {
        [self.button setImage:self.landscapeImage forState:UIControlStateNormal];
        self.button.frame = CGRectMake(0.0, 0.0, self.landscapeImage.size.width / 2, self.landscapeImage.size.height / 2);
        self.bounds = CGRectMake(0.0, 0.0, self.landscapeImage.size.width / 2, self.landscapeImage.size.height / 2);
    } else {
        [self.button setImage:self.portraitImage forState:UIControlStateNormal];
        self.button.frame = CGRectMake(0.0, 0.0, self.portraitImage.size.width / 2, self.portraitImage.size.height / 2);
        self.bounds = CGRectMake(0.0, 0.0, self.portraitImage.size.width / 2, self.portraitImage.size.height / 2);
    }
}

- (void) layoutSubviews {
    [super layoutSubviews];
    [self createLayout];
}

However, this problem remains:

  1. Starting view at portrait orientation
  2. Push a view controller onto stack
  3. Rotate the device to landscape (the current view reacts appropriately)
  4. Pop the last view controller: the previous view reacts otherwise well, but the RotatingButtons' layoutSubviews don't get called, and the buttons remain larger than they should.

So, currently after popping a view controller, the previous UIBarButtonItems don't have their layoutSubviews called, and they remain too large (or too small, if we start from landscape and rotate to portrait in another view). How to solve this problem?

Upvotes: 3

Views: 941

Answers (2)

xci
xci

Reputation: 450

I didn't find a completely satisfactory solution, but my buttons happened to be of suitable size, and this kind of a solution worked very well for me:

UIBarButtonItem* b = [[UIBarButtonItem alloc] initWithTitle:@"" style:UIBarButtonItemStylePlain target:target action:selector];
UIImage *barButton = [portraitImage resizableImageWithCapInsets:UIEdgeInsetsMake(10, 10, 10, 10)];
UIImage *barButton_land = [landscapeImage resizableImageWithCapInsets:UIEdgeInsetsMake(10, 10, 10, 10)];
[b setBackgroundImage:barButton forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
[b setBackgroundImage:barButton_land forState:UIControlStateNormal barMetrics:UIBarMetricsLandscapePhone];

And then obviously adding the created button as rightBarButtonItem/leftBarButtonItem, or as you may want to use it.

The problem with this is that if your buttons are not wide enough, the buttons may look completely wrong (since the middle content of the image is tiled in this solution).

Upvotes: 0

Ash Furrow
Ash Furrow

Reputation: 12421

This is a really tricky question. You should try overriding viewWillAppear: to call [self.view setNeedsLayout] to force a layout update whenever the view is about to appear.

Upvotes: 1

Related Questions