iOS 5 Back Button Size

12,054

Solution 1

As you've discovered, UIAppearance proxies won't let you adjust the dimensions of the button itself: the list of supported appearance methods for UIBarButtonItem is given in the documentation, and whilst it includes metrics for the title label itself the button is off-limits.

For what it's worth, the button itself is actually 44 pts (88 pixels, if you're in retina) high in terms of touchable area, it's just the button graphic is smaller than that.

If you're dead set on what amounts to a 3 pt difference in height then the best option is probably going to be to create your own custom button and use the UINavigationItem setLeftBarButtonItem method. As you point out, you're going to need to implement a short method attached to that button that implements popNavigationController to ensure the correct behavior remains.

Update: I've just found out that if you keep your back button around you can, in fact, animate it to have it slide in a similar fashion to a standard back button. In the code below self.backButton is the UIButton you would have used in your initWithCustomView UIBarButtonItem method.

- (void)viewWillDisappear:(BOOL)animated {
    [UIView animateWithDuration:0.3 
                     animations:^{
                         self.backButton.frame = CGRectMake(self.backButton.frame.origin.x+100, 
                                                            self.backButton.frame.origin.y, 
                                                            self.backButton.frame.size.width, 
                                                            self.backButton.frame.size.height);
                     }];
}

...this will trigger whenever the view controller disappears (ie, when popped and pushed), but you could hook into the UINavigationController delegate to only trigger it when the nav controller is popped instead. It definitely seems to move the button when controller is pushed, although I've only tested it on the simulator.

Solution 2

I ended up using a custom view controller class for every VC I'm using in my navigation controller.

OEViewController.h

#import <UIKit/UIKit.h>
#import "OEBarButtonItem.h"

@interface OEViewController : UIViewController

@property (nonatomic, retain) UIButton *backButton;
@property (atomic) BOOL pushed;

- (void)pushViewController:(UIViewController*)vc;

@end

OEViewController.m

#import "OEViewController.h"

#define ANIMATION_DURATION 0.33

@implementation OEViewController
@synthesize backButton, pushed;

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    return self;
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    pushed=YES;
    self.navigationItem.hidesBackButton = YES;

    backButton = [OEBarButtonItem backButtonWithTitle:[(UIViewController*)[self.navigationController.viewControllers objectAtIndex:[self.navigationController.viewControllers count]-2] title]];

    [backButton addTarget:self action:@selector(backButtonPressed:) forControlEvents:UIControlEventTouchUpInside];

    self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:backButton];
}

- (void)viewDidUnload
{
    [super viewDidUnload];
    // Release any retained subviews of the main view.
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    return (interfaceOrientation == UIInterfaceOrientationPortrait);
}

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    if (pushed) {
        self.backButton.frame = CGRectMake(self.backButton.frame.origin.x+100, 
                                           self.backButton.frame.origin.y+6, 
                                           self.backButton.frame.size.width, 
                                           self.backButton.frame.size.height);
        [UIView animateWithDuration:ANIMATION_DURATION 
                         animations:^{
                             self.backButton.frame = CGRectMake(self.backButton.frame.origin.x-100, 
                                                                self.backButton.frame.origin.y, 
                                                                self.backButton.frame.size.width, 
                                                                self.backButton.frame.size.height);
                         }];
        pushed=NO;
    } else {
        self.backButton.frame = CGRectMake(self.backButton.frame.origin.x-100, 
                                           self.backButton.frame.origin.y, 
                                           self.backButton.frame.size.width, 
                                           self.backButton.frame.size.height);
        [UIView animateWithDuration:ANIMATION_DURATION 
                         animations:^{
                             self.backButton.frame = CGRectMake(self.backButton.frame.origin.x+100, 
                                                                self.backButton.frame.origin.y, 
                                                                self.backButton.frame.size.width, 
                                                                self.backButton.frame.size.height);
                         }];
    }
}

- (void)backButtonPressed:(id)sender {
    [self.navigationController popViewControllerAnimated:YES];
    [UIView animateWithDuration:ANIMATION_DURATION 
                     animations:^{
                         self.backButton.frame = CGRectMake(self.backButton.frame.origin.x+100, 
                                                            self.backButton.frame.origin.y, 
                                                            self.backButton.frame.size.width, 
                                                            self.backButton.frame.size.height);
                     }];
}

- (void)pushViewController:(UIViewController*)vc {
    [self.navigationController pushViewController:vc animated:YES];
    [UIView animateWithDuration:ANIMATION_DURATION 
                     animations:^{
                         self.backButton.frame = CGRectMake(self.backButton.frame.origin.x-100, 
                                                            self.backButton.frame.origin.y, 
                                                            self.backButton.frame.size.width, 
                                                            self.backButton.frame.size.height);
                     }];
}

@end

This has the added benefit of working with iOS 4.0+ as opposed to using UIAppearance.

Thanks to @lxt for his help!

Share:
12,054
jpsim
Author by

jpsim

Updated on June 13, 2022

Comments

  • jpsim
    jpsim almost 2 years

    I'm using the iOS 5 UIAppearance protocol to use a custom navigation bar back button as seen here. Unlike other methods I've found, this one is the best since it preserves the default back button animations and behaviours.

    The only problem is that I can't change its size or set it to not clip subviews. Here's what's happening:

    Clipping Image

    A is intended behaviour, B is default style, C is the clipped result.

    Unfortunately, it's not as easy as setting UIBarButtonItem to clipsToBounds=NO.

    Does anyone know how to solve this issue? Thanks!