Ios 为什么其他调用方法的东西通过接口生成器xib连接到UIBarButtonItem?

Ios 为什么其他调用方法的东西通过接口生成器xib连接到UIBarButtonItem?,ios,interface-builder,uibarbuttonitem,objective-c-category,unrecognized-selector,Ios,Interface Builder,Uibarbuttonitem,Objective C Category,Unrecognized Selector,按下RightBarButtonim自定义视图中的按钮时,我的iOS应用程序崩溃。使用自定义视图是因为barButtonItem设计需要的不仅仅是按钮 以下是崩溃的输出: [UIViewControllerWrapperView buttonPressed:]: unrecognized selector sent to instance 0x7669430] *** Terminating app due to uncaught exception 'NSInvalidArgumentExce

按下RightBarButtonim自定义视图中的按钮时,我的iOS应用程序崩溃。使用自定义视图是因为barButtonItem设计需要的不仅仅是按钮

以下是崩溃的输出:

[UIViewControllerWrapperView buttonPressed:]: unrecognized selector sent to instance 0x7669430]
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[UIViewControllerWrapperView buttonPressed:]: unrecognized selector sent to instance 0x7669430'
自定义视图在单独的视图控制器的xib RightBarButtonie MVC中定义,该视图还包含以下链接方法:

- (IBAction)buttonPressed:(id)sender {
    NSLog(@"button pressed");
}
viewDidLoad中使用RightBarButtonItem MVC,用于所有需要该项的视图控制器:

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    RightBarButtonItemVC *rightBarButtonItemVC = [[RightBarButtonItemVC alloc] initWithNibName:@"RightBarButtonItemVC" bundle:nil];
    
    UIBarButtonItem *rightBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:rightBarButtonItemVC.view]; 
    
    self.navigationItem.rightBarButtonItem = rightBarButtonItem;
}
请注意,我是如何将rightBarButtonItem的视图指定为rightBarButtonItem的视图的

问题:
  • 为什么UIViewControllerRapperView的实例调用我的选择器而不是我的RightBarButtoniMVC实例
  • 我如何才能防止这种情况发生并使按钮正常工作?我应该为UIViewControllerRapperView编写一个类别吗?如果是,在哪里导入文件

  • UIViewControllerRapperView
    没有调用您的选择器;您的按钮正在调用
    UIViewControllerRapperView
    上的
    -按钮按下:
    。试试看

    看起来您使用的是
    rightbarbuttoniemvc
    ,只是作为一个视图加载程序(我假设您使用的是ARC,否则它会泄漏)。这是昂贵的,除非在其他地方使用视图之前设置
    rightbarbuttoniemvc.view=nil
    ,否则可能会发生奇怪的事情(我完全忘了是什么)。我提出了一种从NIB加载视图的更好方法(我不知道Interface Builder是否支持协议拥有的NIB,这将是理想的)

    代码崩溃的主要原因有两个:

    • 在NIB中,
      -按钮按下:
      连接到错误的对象。我认为这不太可能
    • -按钮按下:
      将被发送到
      右按钮MVC
      ,但
      右按钮MVC
      不被任何东西保留,因此被解除分配。它被发送到在同一地址分配的下一个对象,该对象恰好是
      UIViewControllerRapperView
    有两个简单的解决方法:

    • 删除Interface Builder中的连接,并使用以编程方式添加它。这需要在视图层次结构中查找按钮
    • 首先以编程方式创建它
    我喜欢后者;从长远来看,在代码中维护UI似乎要容易得多,本地化也要容易得多,因为您只需要翻译单个字符串文件。

    直接回答:
  • 正如@tc.的回答所暗示的,在xib中定义视图和使用视图控制器(RightBarButtonItem MVC)在UIBarButtonItem上定义自定义视图之间存在脱节,这一点在UIViewControllerRapperView接收按钮按下调用而不是RightBarButtonItemVC这一事实中很明显。看起来有些东西没有被保留,虽然我不确定是什么
  • 下面是我实施的具体工作解决方案。我确实做了一个分类,但没有像前面提到的那样为UIViewControllerRapperView做分类
  • 具体解决方案: 首先在UIViewController上创建BarbuttoneMLoader,这是一个Objective-C类别:

    @interface UIViewController (BarButtonItemLoader)
    
    在UIViewController+BarbuttonimLoader.h中,定义此方法:

    - (UIBarButtonItem *) rightBarButtonItem;
    
    由于无法跟踪类别中的状态,请在AppDelegate.h中定义UIBarButtonItem:

    @property (strong, nonatomic) UIBarButtonItem *rightBarButtonItem;
    
    接下来,通过从AppDelegate延迟加载RightBarButtonim(不要忘记导入“AppDelegate.h”),开始实现类别的RightBarButtonim方法。这将确保在AppDelegate中只创建并保留一个RightBarButtonim:

    - (UIBarButtonItem *) rightBarButtonItem {
    
        AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
    
        if(!appDelegate.rightBarButtonItem) {
            //create a rightBarButtonItem (see below)
        }
        return appDelegate.rightBarButtonItem;
    }
    
    开始组装将设置为RightBarButtonim的UIView/UIBarButtonim。从旧的Interface Builder/xib实现中传输每个元素/配置。最重要的是,请注意Size inspector中的框架信息,以便您可以通过编程方式定位子视图,就像您在.xib文件中手动定位子视图一样

    - (UIBarButtonItem *) rightBarButtonItem {
    
        AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
    
        if(!appDelegate.rightBarButtonItem) {
            UIView *rightBarView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 264, 44)];
            UIBarButtonItem *rightBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:rightBarView];
            UIImageView *textHeader = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"textHeader.png"]];
            textHeader.frame = CGRectMake(2, 14, 114, 20);
            [rightBarView addSubview:textHeader];
    
            UIButton *button1 = [[UIButton alloc] initWithFrame:CGRectMake(100, 2, 70, 44)];
            [button1 setImage:[UIImage imageNamed:@"button1.png"] forState:UIControlStateNormal];
            [button1 setImage:[UIImage imageNamed:@"button1Highlighted.png"] forState:UIControlStateHighlighted];
            [button1 addTarget:self action:@selector(button1Pressed) forControlEvents:UIControlEventTouchUpInside];
            [rightBarView addSubview:button1];
    
            UIButton *button2 = [[UIButton alloc] initWithFrame:CGRectMake(194, 2, 70, 44)];
            [button2 setImage:[UIImage imageNamed:@"button2.png"] forState:UIControlStateNormal];
            [button2 setImage:[UIImage imageNamed:@"button2Highlighted.png"] forState:UIControlStateHighlighted];
            [button2 addTarget:self action:@selector(button2Pressed) forControlEvents:UIControlEventTouchUpInside];
            [rightBarView addSubview:button2];
    
            appDelegate.rightBarButtonItem = rightBarButtonItem;
        }
        return appDelegate.rightBarButtonItem;
    }
    
    最后,实现UIViewController+barbuttonimloader.m中的buttonXPressed方法,以达到您的目的:

    - (void) button1Pressed {
          NSLog(@"button1 Pressed");
    }
    
    - (void) button2Pressed {
          NSLog(@"button2 Pressed");
    }
    

    通过将此代码添加到任何UIViewController或其子类来使用类别:

    #import "UIViewController+BarButtonItemLoader.h"
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        self.navigationItem.rightBarButtonItem = [self rightBarButtonItem];
    }
    
    总结 这种方法允许您动态地向任何UIViewController添加UIBarButtonItem。缺点是必须将上述代码添加到所创建的所有UIViewController中

    另一种选择
    如果希望进一步封装UIBarButtonims(或其他任何内容)的添加,避免在每个视图控制器中添加代码,则应创建BaseViewController,然后从中对所有其他视图控制器进行子类化。从那里,您可以考虑要包含在所有视图控制器中的其他项。选择类别或子类路由将成为粒度问题。

    我认为您不能像那样“共享”视图控制器的视图。为什么不在xib中创建一个视图而不是视图控制器?我考虑过了,但是如果我这样做了,我必须在每个视图控制器中定义目标方法,对吗?为什么?你放在按钮上的图像与其目标方法有什么关系?我不确定你的意思,因为我正在加载一个带有自定义视图的uibarbuttonite,而不仅仅是将图像应用于按钮。有什么方法可以让这个问题变得更好?我对downvoteTY很好奇——我不会说RightBarButtonItemVC只是一个视图加载器——它应该响应按钮的按下并采取适当的措施。如果我只做一个xib,那么我就失去了将所有按钮响应保持在一个位置的能力。如果我正确理解了您的方法,这意味着我需要在viewDidLoad中为每个想要使用UIBarButtonim的新视图控制器添加目标。我希望如果你有一个解决方案,包括这一点,就可以避免这种情况。@kraftydevil那么要么需要保留正确的MVC,要么希望此功能的VCs需要子类