0

rightBarButtonItem のカスタム ビューにあるボタンを押すと、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、RightBarButtonItemVC で定義されます。これには、次のリンクされたメソッドも含まれます。

- (IBAction)buttonPressed:(id)sender {
    NSLog(@"button pressed");
}

rightBarButtonItemVC は、アイテムを必要とするすべてのビュー コントローラーに対して、viewDidLoad で使用されます。

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    RightBarButtonItemVC *rightBarButtonItemVC = [[RightBarButtonItemVC alloc] initWithNibName:@"RightBarButtonItemVC" bundle:nil];
    
    UIBarButtonItem *rightBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:rightBarButtonItemVC.view]; 
    
    self.navigationItem.rightBarButtonItem = rightBarButtonItem;
}

rightBarButtonItemVC のビューを rightBarButtonItem のビューとして割り当てる方法に注意してください。

質問

  1. UIViewControllerWrapperView のインスタンスが、rightBarButtonItemVC のインスタンスではなくセレクターを呼び出すのはなぜですか?
  2. これを防ぎ、ボタンを機能させるにはどうすればよいですか? UIViewControllerWrapperView のカテゴリを作成する必要がありますか? もしそうなら、どこにファイルをインポートしますか?
4

2 に答える 2

1

UIViewControllerWrapperViewセレクターを呼び出していません。あなたのボタンは を呼び出し-buttonPressed:ていUIViewControllerWrapperViewます。ゾンビを有効にしてみてください。

単にビューローダーとして使用しているようですRightBarButtonItemVC(ARCを使用していると思います。そうしないとリークします)。これはコストがかかり、他の場所でビューを使用する前に設定しない限り、奇妙なことが起こる可能性がrightBarButtonItemVC.view = nilあります (正確に何を忘れたか)。ここで nib からビューをロードするより良い方法を提示します(Interface Builder がプロトコルによって所有される nib をサポートしているかどうかはわかりませんが、これは理想的です)。

コードがクラッシュする主な理由は 2 つあります。

  • NIBで-buttonPressed:、間違ったものに接続されています。これはありそうにないと思います。
  • -buttonPressed:は に送信されますがRightBarButtonItemVCRightBarButtonItemVCは何にも保持されないため、割り当てが解除されます。同じアドレスに割り当てられている次のオブジェクトに送信されますUIViewControllerWrapperView

2 つの簡単な修正方法があります。

  • Interface Builder で接続を削除し、プログラムで追加します-addTarget:action:forControlEvents:。これには、ビュー階層でボタンを見つける必要があります。
  • 最初にプログラムで作成します。

私は後者を好みます。長い目で見れば、UI をコードで管理する方がはるかに簡単で、翻訳する必要があるのは単一の文字列ファイルだけなので、ローカライズもはるかに簡単です。

于 2013-03-07T05:12:16.323 に答える
0

直接的な回答:

  1. @tc.の回答で示唆されているように、xib でビューを定義することと、View Controller (RightBarButtonItemVC) を使用して UIBarButtonItem のカスタム ビューを定義することの間には、どこかに切断があります。 RightBarButtonItemVC の代わりに呼び出します。よくわかりませんが、何かが保持されていないようです。
  2. 以下は、私が実装した特定の実用的なソリューションです。カテゴリを作成しましたが、前述の UIViewControllerWrapperView 用ではありません。

具体的な解決策:

まず、UIViewController の Objective-C カテゴリである BarButtonItemLoader を作成します。

@interface UIViewController (BarButtonItemLoader)

UIViewController+BarButtonItemLoader.h で、次のメソッドを定義します。

- (UIBarButtonItem *) rightBarButtonItem;

カテゴリの状態を追跡できないため、AppDelegate.h で UIBarButtonItem を定義します。

@property (strong, nonatomic) UIBarButtonItem *rightBarButtonItem;

次に、AppDelegate から rightBarButtonItem を遅延ロードして、カテゴリの rightBarButtonItem メソッドの実装を開始します (#import "AppDelegate.h" を忘れないでください)。これにより、1 つの rightBarButtonItem のみが作成され、AppDelegate に保持されます。

- (UIBarButtonItem *) rightBarButtonItem {

    AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];

    if(!appDelegate.rightBarButtonItem) {
        //create a rightBarButtonItem (see below)
    }
    return appDelegate.rightBarButtonItem;
}

rightBarButtonItem に設定される UIView/UIBarButtonItem の組み立てを開始します。古い Interface Builder / xib 実装から各要素/構成を転送します。最も重要なことは、Size インスペクタのフレーム情報に注意して、.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+BarButtonItemLoader.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];
}

概要

このアプローチにより、UIBarButtonItem をオンザフライで任意の UIViewController に追加できます。欠点は、作成するすべての UIViewControllers に上記のコードを追加する必要があることです。

別のオプション

UIBarButtonItems (またはその他のもの) の追加をさらにカプセル化し、各ビュー コントローラーにコードを追加する必要がないようにする場合は、BaseViewController を作成し、そこから他のすべてのビュー コントローラーをサブクラス化する必要があります。そこから、すべてのビュー コントローラーに含めたい他のアイテムを検討できます。カテゴリまたはサブクラスのルートを選択すると、粒度の問題になります。

于 2013-03-14T10:33:26.213 に答える