How to create backBarButtomItem with custom view f

2020-01-27 00:28发布

I have a UINavigationController into which I push several views. Inside viewDidLoad for one of these views I want to set the self.navigationItem.backBarButtonItem to a custom view (based on a custom image). I don't know why, but it doesn't seem to work. Instead, I get the standard "back" button.

UIButton *backButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 63, 30)];
[backButton setImage:[UIImage imageNamed:@"back_OFF.png"] forState:UIControlStateNormal];
[backButton setImage:[UIImage imageNamed:@"back_ON.png"] forState:UIControlStateSelected];
UIBarButtonItem *backButtonItem = [[UIBarButtonItem alloc] initWithCustomView:backButton];
self.navigationItem.backBarButtonItem = backButtonItem;
[backButtonItem release];
[backButton release];

I tested with a standard title and it worked. What is wrong with the above code ?

self.navigationItem.backBarButtonItem = [[[UIBarButtonItem alloc] initWithTitle:@"Prout" style:UIBarButtonItemStyleDone target:nil action:nil] autorelease];

Thanks for any help on this.

标签: cocoa-touch
14条回答
2楼-- · 2020-01-27 00:43

backBarButtonItem is not a read-only property. I'm not sure why it behaves so strangely, and the above is a valid (if less-than-ideal) workaround.

It behaves strangely because setting a vc's backBarButtonItem doesn't change anything about the appearance of the vc's navigation item - instead, it changes the button that points BACK to the vc. See updating the navigation bar from Apple FMI.

That said I haven't had a whole lot of luck getting it to work myself. If you look around this site, you'll find some threads that suggest placing code very similar to what you already have immediately before the call to push a new view on the stack. I've had some luck there, but unfortunately not when it comes to using a custom image.

查看更多
甜甜的少女心
3楼-- · 2020-01-27 00:43

This is how I create a custom square back button with an arrow instead of the usual text.

I simply setup a delegate for my UINavigationController. I use the app delegate for that because the window root view controller is the UINavigationController i want to control.

So AppDelegate.m (ARC):

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    ((UINavigationController*)_window.rootViewController).delegate = self;
    return YES;
}

#pragma mark - UINavigationControllerDelegate

- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
    if(navigationController.viewControllers.count > 1) {
        UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
        [button setImage:[UIImage imageNamed:@"back-arrow.png"] forState:UIControlStateNormal];
        [button setBackgroundColor:[UIColor grayColor]];
        button.frame = CGRectMake(0, 0, 44, 44);
        viewController.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:button];
        [button addEventHandler:^(id sender) {
            [navigationController popViewControllerAnimated:YES];
        } forControlEvents:UIControlEventTouchUpInside];
    }
}

I'm using BlocksKit to catch the button tap event. It's very convenient for stuff like this but you can also use the regular addTarget:action:forControlEvents: method

查看更多
小情绪 Triste *
4楼-- · 2020-01-27 00:45

As of iOS5 we have an excellent new way of customizing the appearance of almost any control using the UIAppearance protocol, i.e. [UIBarButtonItem appearance]. The appearance proxy allows you to create application wide changes to the look of controls. Below is an example of a custom back button created with the appearance proxy.

enter image description here

Use the example code below to create a back button with custom images for normal and highlighted states. Call the following method from you appDelegate's application:didFinishLaunchingWithOptions:

- (void) customizeAppearance {

UIImage *i1 = [[UIImage imageNamed:@"custom_backButton_30px"]
                      resizableImageWithCapInsets:UIEdgeInsetsMake(0, 15, 0, 6)];
UIImage *i2 = [[UIImage imageNamed:@"custom_backButton_24px"] 
                      resizableImageWithCapInsets:UIEdgeInsetsMake(0, 15, 0, 6)];

[[UIBarButtonItem appearance] setBackButtonBackgroundImage:i1 
                              forState:UIControlStateNormal 
                              barMetrics:UIBarMetricsDefault];

[[UIBarButtonItem appearance] setBackButtonBackgroundImage:i2 
                              forState:UIControlStateNormal 
                              barMetrics:UIBarMetricsLandscapePhone];

[[UIBarButtonItem appearance] setBackButtonBackgroundImage:i1
                              forState:UIControlStateHighlighted 
                              barMetrics:UIBarMetricsDefault];

[[UIBarButtonItem appearance] setBackButtonBackgroundImage:i2 
                              forState:UIControlStateHighlighted 
                              barMetrics:UIBarMetricsLandscapePhone];
}

This is just a quick example. Normally you would want to have separate images for normal and highlighted (pressed) state.

If you are interested in customizing the appearance of other controls, some good examples can be found here: http://ios.biomsoft.com/2011/10/13/user-interface-customization-in-ios-5/

查看更多
SAY GOODBYE
5楼-- · 2020-01-27 00:45

I too have been having problems with customView on a navigationItem.backBarButtonItem. I suspect it's probably just b0rked in the SDK.

While the workarounds outlined here do work, I've come up with a solution which is a little more elegant: it still uses navigationItem.leftBarButtonItem, but takes care of it for you automagically (no more need for 'child' view controllers to need to know anything about their 'parent' and/or to manually set navigationItem.leftBarButtonItem).


First up, have some class be a UINavigationControllerDelegate for the UINavigationController whose back button you're interested in. Then, in this class, set up something like the following willShowViewController delegate method:

-(void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated{
    // reference to view controller stack
    NSArray *viewControllers = [ navigationController viewControllers ];
    if( [ viewControllers count ] > 1 ){
        // the view controller we'll be linking to
        UIViewController *backViewController = [ viewControllers objectAtIndex: [ viewControllers count ] - 2 ];
        // create custom UIBarButtonItem
        UIBarButtonItem *leftButton = [[ UIBarButtonItem alloc ] initWithCustomView: someCustomView ];
        // set it as the leftBarButtonItem on the incoming viewcontroller
        viewController.navigationItem.leftBarButtonItem = leftButton;
        // tidy up
        [ leftButton release ];
    }
}

I had some further problems with this; it seems that UIBarButtonItem.action and UIBarButtonItem.target don't work when it's a navigationItem.leftBarButtonItem. So, you're left with a custom back button that doesn't actually go back. I'll leave responding to touches in your custom view as an exercise for the reader (I used a UIButton), but you'll need add this method to your delegate class:

-(void)onDummyBackButtonTapped{
    [ someNavigationController popViewControllerAnimated: YES ];
}

and hook it up to fire when your custom view is tapped.

查看更多
甜甜的少女心
6楼-- · 2020-01-27 00:48

I think I found the solution for this. Simply set the button on the navigation item on the previous controller (The one that you want to go back to)

So if I have for example a root controller and I push a second controller and want to customize the back button then I should do the following:

self.navigationItem.backBarButtonItem = [[[UIBarButtonItem alloc]
    initWithImage:[UIImage imageNamed:@"ico.png"] style:UIBarButtonItemStyleBordered
    target:nil action:nil] autorelease];

Where self is the root view controller and not the second one.

查看更多
Luminary・发光体
7楼-- · 2020-01-27 00:49

I'm fairly certain that the backBarButtonItem is a read-only property. Instead of modifying the backBarButtonItem, try setting a custom leftBarButtonItem and hide the backBarButtonItem:

self.navigationItem.leftBarButtonItem = [[[UIBarButtonItem alloc] initWithTitle:@"Prout" style:UIBarButtonItemStyleDone target:nil action:nil] autorelease];
self.navigationItem.hidesBackButton = YES;

You will also need to make sure you hook up the custom button to call the back action on the UINavigationBar.

查看更多
登录 后发表回答