如何使用UINavigationController的自定义视图创建backBarButtomItem

时间:2009-02-08 22:18:39

标签: cocoa-touch

我有一个UINavigationController我可以推送几个视图。在其中一个视图的viewDidLoad内,我想将self.navigationItem.backBarButtonItem设置为自定义视图(基于自定义图像)。我不知道为什么,但似乎没有用。相反,我得到标准的“后退”按钮。

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];

我测试了一个标准的标题,它的工作原理。上面的代码出了什么问题?

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

感谢您提供任何帮助。

14 个答案:

答案 0 :(得分:27)

从iOS5开始,我们有一个使用UIAppearance protocol定制几乎所有控件的外观的优秀新方法,即[UIBarButtonItem appearance]。外观代理允许您创建应用程序范围内的控件外观更改。下面是使用外观代理创建的自定义后退按钮的示例。

enter image description here

使用下面的示例代码创建一个后退按钮,其中包含正常和突出显示状态的自定义图像。从appDelegate的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];
}

这只是一个简单的例子。通常,您可能希望为正常和突出显示(按下)状态设置单独的图像。

如果您有兴趣自定义其他控件的外观,可以在此处找到一些很好的示例:http://ios.biomsoft.com/2011/10/13/user-interface-customization-in-ios-5/

答案 1 :(得分:19)

我非常确定backBarButtonItem是一个只读属性。不要修改backBarButtonItem,而是尝试设置自定义leftBarButtonItem并隐藏backBarButtonItem

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

您还需要确保连接自定义按钮以在UINavigationBar上调用后退操作。

答案 2 :(得分:15)

我无法使用自定义视图和UIBarButtonItem创建合适的setBackBarButtonItem

这是我发现的解决方案:让net UINavigationControllerDelegate处理所有事情!这里的诀窍是调用popViewControllerAnimated:的{​​{1}}方法,这样您就不必创建任何自定义方法。

viewController.navigationController

答案 3 :(得分:10)

如果您的最终目标是简单地替换用于后退按钮的图像,则可以在iOS 5.0中使用的UIBarButtonItem上使用新方法:

setBackButtonBackgroundImage:forState:barMetrics:

Apple Docs: http://developer.apple.com/library/ios/#documentation/UIKit/Reference/UIBarButtonItem_Class/Reference/Reference.html

这是一个简单的示例,为您应用中的所有后退按钮设置自定义背景图片:

UIImage *toolbarBackButtonBackgroundPortrait = [[UIImage imageNamed:@"toolbarBackButtonPortrait"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 17, 0, 6)];
UIImage *toolbarBackButtonBackgroundLandscape = [[UIImage imageNamed:@"toolbarBackButtonLandscape"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 17, 0, 6)];

[[UIBarButtonItem appearance] setBackButtonBackgroundImage:toolbarBackButtonBackgroundPortrait forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
[[UIBarButtonItem appearance] setBackButtonBackgroundImage:toolbarBackButtonBackgroundLandscape forState:UIControlStateNormal barMetrics:UIBarMetricsLandscapePhone];

答案 4 :(得分:6)

我愿意打赌,这是Apple的一个错误,因为我遇到了同样的问题。原因是我可以显示自定义UIBarButtonItem,但仅当我尝试使用initWithCustomView:方法时。根据API,导航控制器在按下新视图控制器时检查以下内容:

  1. 如果新的顶级视图控制器具有自定义左侧栏按钮项,则显示该项。要指定自定义左栏按钮项,请设置视图控制器导航项的leftBarButtonItem属性。
  2. 如果顶级视图控制器没有自定义左栏按钮项,但前一个视图控制器的导航项在其backBarButtonItem属性中有一个有效项,则导航栏会显示该项。
  3. 如果任一视图控制器未指定自定义栏按钮项,则使用默认后退按钮,并将其标题设置为前一个视图控制器的title属性的值 - 即视图控制器在堆栈上降低。 (如果导航堆栈上只有一个视图控制器,则不显示后退按钮。)
  4. 我的情况(以及你的情况)是2.我指定代码完全与您的相同(即创建UIButton,为各种设置image属性状态,创建UIBarButtonItem,使用UIButton初始化它,然后将我当前的视图控制器的backBarButtonItem属性设置为UIBarButtonItem);然而,当我稍后推动我的视图控制器时,我的导航控制器的左侧没有显示任何内容。奇怪的是,我可以单击“后退”按钮所在的位置,然后弹出视图控制器。

    有趣的是,如果我使用UIBarButtonItem方法而不是initWithTitle:style:target:action:方法创建initWithCustomView:,则显示带有自定义标题的自定义按钮。此外,正如特拉维斯所说,使用leftBarButtonItem属性可以正常工作。但是,我宁愿遵守认可的逻辑,通过为当前视图控制器指定“后退”按钮 - 稍后在按下新视图控制器时显示 - 而不是为下一个视图控制器创建左按钮可以说,它应该不关心与它之前的视图控制器有关的任何事情。 : - \

答案 5 :(得分:5)

在使用navigationController推送viewController之前设置backBarButtonItem 之前。在backBarButtonItem中设置viewDidLoad不起作用。

说我有MyTableViewController。当用户点击特定行时,我想使用navigationController推送AnotherViewController。代码看起来像这样:

// in MyTableViewController's tableView:didSelectRowAtIndexPath method...
UIBarButtonItem *backButton = [[UIBarButtonItem alloc] 
                               initWithImage:[UIImage imageNamed:@"yourImage.png"]
                                       style:UIBarButtonItemStyleBordered 
                                      target:nil 
                                      action:nil];

self.navigationItem.backBarButtonItem = backButton;
[backButton release];

[self.navigationController pushViewController:anotherViewController];

当显示anotherViewController时,导航栏中的后退按钮将具有@"yourImage.png"和默认后退按钮样式(矩形箭头)。另请注意,将nil作为target传递是合适的。按钮的行为类似于后退按钮。

答案 6 :(得分:2)

即使已经回答,这对我有用:

UIBarButtonItem *backButton = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"backArrow.png"] style:UIBarButtonItemStyleBordered target:nil action:nil];
self.navigationItem.backBarButtonItem = backButton;
[backButton release];
BTW:即使在iOS4中使用initWithCustomView初始化我的后退按钮:对我来说也不起作用。 ;(

答案 7 :(得分:2)

customView上的navigationItem.backBarButtonItem也遇到了问题。我怀疑它可能只是SDK中的b0rked。

虽然这里列出的解决方法确实有效,但我提出了一个更优雅的解决方案:它仍然使用navigationItem.leftBarButtonItem,但是会自动为您处理(不再需要'儿童' '查看控制器需要了解有关'父母'的任何信息和/或手动设置navigationItem.leftBarButtonItem)。


首先,让某些课程成为您感兴趣的后退按钮的UINavigationControllerDelegate的{​​{1}}。然后,在此课程中,设置类似以下UINavigationController委托的内容方法:

willShowViewController

我还有一些问题;似乎-(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 ]; } } UIBarButtonItem.actionUIBarButtonItem.target时无效。所以,你留下了一个实际上没有返回的自定义后退按钮。我会在您的自定义视图中回复触摸作为读者的练习(我使用了UIButton),但您需要将此方法添加到您的委托类中:

navigationItem.leftBarButtonItem

并在点击自定义视图时将其连接起来。

答案 8 :(得分:1)

  

backBarButtonItem不是只读的   属性。我不确定它为什么会这样   太奇怪了,以上是有效的   (如果不太理想)解决方法。

它表现得很奇怪,因为设置vc的backBarButtonItem不会改变vc导航项的外观 - 相反,它会更改指向BACK的按钮到vc。请参阅Apple FMI的updating the navigation bar

那说我自己没有运气好运。如果你环顾这个网站,你会发现一些线程建议放置代码非常类似于你在调用之前已经拥有的代码,以便在堆栈上推送新视图。我在那里运气不错,但遗憾的是在使用自定义图像方面没有。

答案 9 :(得分:1)

navigationController的backBarButtonItem设置在您试图影响其标题的项目上。

即。在第1页的视图控制器中,比如,viewdidLoad:

self.title = @"Page 1 of 4";

self.navigationItem.backBarButtonItem =
[[[UIBarButtonItem alloc] initWithTitle:@"Page 1"
                                  style:UIBarButtonItemStyleBordered
                                 target:nil
                                 action:nil] autorelease];

不会在第2页中覆盖它。

UINavigationItem的文档:backBarButtonItem清楚地说明了这一点:

  

当此项目是后面的项目时   导航栏 - 当它是下一个   顶部项目下面的项目 - 它可能是   表示为后退按钮   导航栏。使用此属性   指定后退按钮。目标   和后栏按钮项的操作   你设定应该是零。默认   value是一个显示的条形按钮项   导航项目的标题。

答案 10 :(得分:1)

这就是我用箭头而不是通常的文字创建自定义方形后退按钮的方法。

我只是为我的UINavigationController设置了一个委托。我使用app委托,因为窗口根视图控制器是我想要控制的UINavigationController。

所以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];
    }
}

我正在使用BlocksKit来捕捉按钮点击事件。这样的东西很方便,但你也可以使用常规的addTarget:action:forControlEvents:method

答案 11 :(得分:0)

我想我找到了解决方法。 只需在上一个控制器上导航项目上设置按钮(您想要返回的那个)

所以,如果我有一个根控制器 我推了第二个控制器,想要自定义后退按钮,然后我应该执行以下操作:

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

其中self是根视图控制器而不是第二个。

答案 12 :(得分:0)

我的逻辑:

  1. 创建自定义按钮(也称为自定义视图子类)
  2. 使用自定义按钮/视图
  3. 初始化一个barbutton项
  4. 添加一项操作,允许我们“返回”我们之前的视图控制器
  5. 将左侧栏按钮项设置为您创建的此自定义栏按钮项
  6. 隐藏您要推送到的视图控制器的后栏按钮项
  7. 第3步非常重要。我认为模拟“返回”最干净的方法就是利用UINavigationController的方法(popViewControllerAnimated :)。所以,我只是将这个动作添加到我正在推动的viewController的navigationController中(viewControllerToPush),如下所示:

    [navItemButton addTarget:viewControllerToPush.navigationController action:@selector(popViewControllerAnimated:) forControlEvents:UIControlEventTouchUpInside];
    

    示例:

    UIViewController *viewControllerToPush = [[UIViewController alloc] init];
    UIImage *navImage = [UIImage imageNamed:@"your_back_button_image"];
    
    UIButton *navItemButton = [UIButton buttonWithType:UIButtonTypeCustom];
    [navItemButton setImage:navImage forState:UIControlStateNormal];
    [navItemButton setImage:navImage forState:UIControlStateHighlighted];
    [navItemButton setFrame:CGRectMake(0, 0, navImage.size.width, navImage.size.height)];
    
    [navItemButton addTarget:viewControllerToPush.navigationController action:@selector(popViewControllerAnimated:) forControlEvents:UIControlEventTouchUpInside];
    
    UIBarButtonItem *barButtonItem = [[UIBarButtonItem alloc] initWithCustomView:navItemButton];
    
    viewControllerToPush.navigationItem.leftBarButtonItem = barButtonItem;
    viewControllerToPush.navigationItem.hidesBackButton = YES;
    [self.navigationController pushViewController:viewControllerToPush animated:YES];
    

答案 13 :(得分:0)

您可以使用leftBarButtonItem而不是后退按钮项。要删除默认的后退按钮项,请将其设置为nil,如下所示;

navigationController?.navigationBar.backIndicatorImage = nil
navigationController?.navigationBar.backIndicatorTransitionMaskImage = nil

let button = UIButton.init(type: .custom)
button.imageView?.contentMode = UIViewContentMode.scaleAspectFit
button.setImage(UIImage.init(named: "top_back"), for: UIControlState.normal)
button.frame = CGRect.init(x: 0, y: 0, width: 75, height: 50) 
button.addTarget(self, action: #selector(handleBackButton), for: .touchUpInside)

let barButton = UIBarButtonItem.init(customView: button)
self.navigationItem.leftBarButtonItem = barButton