【问题标题】:Adding same button to all view controllers in UINavigationController向 UINavigationController 中的所有视图控制器添加相同的按钮
【发布时间】:2011-09-17 08:39:47
【问题描述】:

我有一个以编程方式创建的UINavigationController(用作向导页面),我需要显示一个“取消”按钮来取消任何UIViewController 中的进程。

创建UINavigationController

FirstVC *firstVC = [[[FirstVC alloc] initWithNibName:@"FirstPage" bundle:nil] autorelease];
firstVC.delegate = self;

navigationController = [[UINavigationController alloc] initWithRootViewController:firstVC];
[self.view addSubview:navigationController.view];

添加取消按钮:

UIBarButtonItem *cancelButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel target:self action:@selector(cancelRequestNewLeave:)];
navigationController.topViewController.navigationItem.rightBarButtonItem = cancelButton;
[cancelButton release];

但是当我将第二页推送到UINavigationController 时,UINavigationBar 上没有显示取消按钮。如果我回到第一页,取消按钮就在那里。因此,显然该按钮仅针对第一个视图添加。我相信这是因为我没有继承UINavigationController,因为我需要在子视图中使用它。但我不知道如何在以编程方式创建的UINavigationController 中设置rightBarButtonItem

navigationController.topViewController.navigationItem.rightBarButtonItem = cancelButton;

有人可以解释一下吗?

提前致谢。

【问题讨论】:

    标签: iphone ios uinavigationcontroller


    【解决方案1】:

    导航项是每个视图控制器。导航栏从当前正在构建其视图的视图控制器的导航项中绘制其内容,该视图对应于导航控制器堆栈顶部的视图控制器。

    您基本上需要每个视图控制器在其导航项中粘贴一个取消按钮。您可以执行以下任何操作:

    • 将代码复制粘贴到所有相关的视图控制器中。
    • 将代码移动到实用函数或类中并调用它。
    • 为处理为其子类设置取消按钮的所有相关视图控制器创建一个公共超类。

    【讨论】:

    • 感谢您的澄清。我将选择第三个选项并创建一个超类。
    • 虽然是迟到的评论,但您应该避免通过超类向类提供功能。在开发的后期,您可能会有一些不“喜欢”继承额外功能的控制器子类。不仅如此,您的代码可能会变得更加复杂。
    • 感谢您对 ViewControllers 和 NavigationItems 之间关系的解释。我关于在 segues(控制器开关)中幸存的自定义按钮的方法是这样的: * ViewControllers 从 BaseViewController 继承。 * BaseViewController 拥有 NSMutableDictionary 的属性 * 此字典保存有关应显示在 NavigationItem 中的按钮的信息。 * BaseViewController 更新当前 NavigationItems 按钮集。也许我的 BaseViewController 将通过协议连接。因此,它会收到有关按钮配置更改的通知。
    • 如果由于继承自 UIKit 中不同类型的 UIViewController(例如 UICollectionViewController 和 UIViewController)而无法拥有一个通用超类,则需要两个超类。相反,调配 viewDidLoad 方法并将初始化代码放在那里成为一个可行的选择。
    • @hariszaman 这听起来像是一个新问题:stackoverflow.com/questions/ask
    【解决方案2】:

    您还可以继承 UINavigationcontroller 并覆盖一些这样的方法:

    - (id)initWithRootViewController:(UIViewController *)rootViewController {
        self = [super initWithRootViewController:rootViewController];
        if (self) {
            [self setCloseButtonToController:rootViewController];
        }
        return self;
    }
    
    - (void)dismissController {
        [self dismissViewControllerAnimated:YES completion:nil];
    }
    
    - (void)setCloseButtonToController:(UIViewController *)viewController {
        UIBarButtonItem *closeItem = [[UIBarButtonItem alloc] initWithTitle:@"Close" style:UIBarButtonItemStylePlain target:self action:@selector(dismissController)];
        [viewController.navigationItem setRightBarButtonItem:closeItem];
    }
    
    - (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated {
        [super pushViewController:viewController animated:animated];
    
        [self setCloseButtonToController:viewController];
    
    }
    

    【讨论】:

    • 谢谢。我以模态方式呈现UINavigationController,我希望有一个简单的取消按钮来应用到其中的所有屏幕。显然,我的用例对 Google 来说非常困难,而且不是流行的。
    • 我喜欢你的风格! :)
    • 和sotryBoard如何设置willShowViewController正确显示按钮?
    【解决方案3】:

    您可以改为在创建UINavigationController 实例的类中采用UINavigationControllerDelegate 协议。也可以提前创建cancelButton,然后像这样实现navigationController:willShowViewController:animated:

    - (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
        viewController.navigationItem.rightBarButtonItem = cancelButton;
    }
    

    您必须记住创建并持有cancelButton,而不是释放它。这也意味着cancelRequestNewLeave: 必须是创建UINavigationController 实例的类中的一个方法,我猜这就是它现在的样子。

    【讨论】:

    • 我不确定将相同的 BBI 放入多个条中是否是一个好主意(iPad 弹出式 API 允许您从 BBI 显示视图,所以大概它应该只在屏幕上的一个位置一次;我不确定 BBI 是否神奇地包含对其父母的引用)
    • 在父视图中使用willShowViewController 设置左键将覆盖所有导航控制器中的后退按钮,并且当您按下 viewController 时,后退按钮不会出现任何修复方法这个问题:stackoverflow.com/questions/30372135/…
    【解决方案4】:
    1. 创建 CommonViewController
    2. 创建 FirstViewController(从 CommonViewController 扩展
    3. 创建 SecondeViewController(从 CommonViewController 扩展
    4. 在CommonViewController中添加函数常用函数

    这样

    CommonViewController.h

    @interface CommonViewController : UIViewController
    
    -(void) initializeCartBarButton;
    
    @end
    

    CommonViewController.m

    #import "CommonViewController.h"
    
    @interface CommonViewController ()
    
    @end
    
    @implementation CommonViewController
    
    -(void) initializeCartBarButton {
    
    
        UIBarButtonItem *cartBarButton = [[UIBarButtonItem alloc] init];
        cartBarButton.title = @"cart";
        [cartBarButton setTarget: self];
        [cartBarButton setAction: @selector(goToCart:)];
    
        self.navigationItem.rightBarButtonItem = cartBarButton;
        }
    
    - (IBAction) goToCart:(id)sender {
        NSLog(@"");
      }
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view.
      }
    
    - (void)didReceiveMemoryWarning {
        [super didReceiveMemoryWarning];
        // Dispose of any resources that can be recreated.
      }
    
    @end
    

    FirstViewController.h

    #import <UIKit/UIKit.h>
    #import "CommonViewController.h"
    
    @interface FirstViewController : CommonViewController
    
    @end
    

    FirstViewController.m

    #import "FirstViewController.h"
    
    @interface FirstViewController ()
    
    @end
    
    @implementation FirstViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
    
        [self initializeCartBarButton];
    }
    @end
    

    SecondViewController.h

    #import <UIKit/UIKit.h>
    #import "CommonViewController.h"
    
    @interface SecondViewController : CommonViewController
    
    @end
    

    SecondViewController.m

    #import "SecondViewController.h"
    
    @interface SecondViewController ()
    
    @end
    
    @implementation SecondViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
    
        [self initializeCartBarButton];
    }
    @end
    

    注意:您可以在 CommonViewController 的 viewDidLoad 中添加 initializeCartBarButton 的代码,并从 CommonViewController 和子类中删除该函数

    【讨论】:

    • @mohamad 我试过你的代码工作正常,但我无法点击那个栏按钮。
    • 对不起我上面的回答。请尝试此代码在 initializeCartBarButton 函数中添加以下 2 行:cartBarButton.title = @"cart"; [barButton setTarget: self]; [barButton setAction: @selector(goToCart:)];比添加功能 goToCart -(IBAction)goToCart:(id)sender { NSLog(@""); }
    • 欢迎欢迎,随时
    【解决方案5】:

    这就是我使用UINavigationController 子类的方法,该子类能够关闭每个推入其中的viewController。

    class CustomNavigationController: UINavigationController, UINavigationControllerDelegate{
    
        //TODO: Use when we have more right bar button types.
        var rightBarButtonType: RightBarButtonType = .Close
    
        enum RightBarButtonType{
            case Close
        }
    
        override func viewDidLoad() {
            super.viewDidLoad()
            self.delegate = self
        }
    
        // MARK: Private Functions
        private func addRightBarButtonTo(viewController: UIViewController){
    
            let barButtonItem: UIBarButtonItem!
            switch self.rightBarButtonType {
            case .Close:
                barButtonItem = UIBarButtonItem(image: UIImage(named: "ic_close_white"), style: .Done, target: self, action: #selector(CustomNavigationController.dismiss(_:)))
    
            }
            viewController.navigationItem.rightBarButtonItem = barButtonItem
        }
    
        // MARK: UINavigationController Delegate
        func navigationController(navigationController: UINavigationController, willShowViewController viewController: UIViewController, animated: Bool) {
            self.addRightBarButtonTo(viewController)
        }
    
        @objc func dismiss(sender: AnyObject){
            self.presentingViewController?.dismissViewControllerAnimated(true, completion: nil)
        }
    }
    

    【讨论】:

      【解决方案6】:

      您需要在每个视图控制器中添加按钮。您不能通过设置一次或在视图控制器之间共享一个(以明智的方式)来做到这一点。添加按钮的好地方是在视图控制器的 viewDidLoad 方法中。如果您觉得这变得重复,您可以为它们创建一个基本的 UIViewConteoller 子类。

      【讨论】:

        【解决方案7】:

        您可以将自定义的“取消”UIButton 直接添加到 NavigationBar 的视图中,而不是使用 UIBarButtonItem。

        UIButton *cancelButton = [UIButton buttonWithType:UIButtonTypeCustom];
        cancelButton.imageView = // Some custom image
        cancelButton.frame = CGRectMake(...);  // Something far to the right.
        [self.navigationController.navigationBar addSubview: cancelButton];
        

        执行此操作的常规方法是将取消按钮添加到导航堆栈中每个视图控制器的 navigationItem。上面的方法可以通过允许您编写更少的代码来使其更简单,但它有点小技巧。

        【讨论】:

        • 问题: (1) 按钮需要将其动作发送到某个视图控制器。当堆栈中还有 2 个以上的 VC 时,您必须计算出 VC 1 应该做什么,并且它们中的任何一个都可能处于不确定状态。 (2) 导航栏会被推送给修改导航栏的VC的VC共享,这样会导致取消按钮出现在不合适的地方。 (3) 可能无法很好地使用 RTL 语言。我什么都看不懂,所以我还没有玩过 UI 如何为他们重新配置自己。
        • 有效的担忧,正如我所说的,这有点像黑客。他真的应该让每个单独的视图控制器添加它自己的 Cancel UIBarButtonItem 并做出适当的响应。不过,我认为您的许多担忧不适用于他。他很可能在一些模态视图控制器中有这个向导,当点击取消时只需要关闭它。无论状态如何,这都会起作用。
        • 你说得对,向导在模态视图控制器中,需要关闭。我认为可能有一种快速的方法来做到这一点。我想我会保持简单,使用继承将按钮添加到每个 VC。
        【解决方案8】:

        在你的rootview viewDidLoad 方法中添加这段代码,并在rootview 控制器中实现cancelMethod。这将在所有视图控制器中可用。您可以通过更改按钮框架来调整按钮位置。对于方向更改,您必须手动调整按钮的位置。

         UIButton *btnCancel = [UIButton buttonWithType:UIButtonTypeRoundedRect];
                [btnCancel addTarget:self
                                action:@selector(cancelMethod)
                      forControlEvents:UIControlEventTouchDown];
                [btnCancel setBackgroundImage:[UIImage imageNamed:@"image"]  
                forState:UIControlStateNormal];
                btnCancel.frame = CGRectMake(280, 27, 45, 25);
        
            [self.navigationController.view addSubview: btnCancel];
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2017-11-29
          • 1970-01-01
          • 1970-01-01
          • 2013-10-15
          • 2013-01-04
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多