A Day In The Life

とあるプログラマの備忘録

UINavigationBarにUISearchBarをセットする時に気をつけること

init系(initWithNibName:bundle:とか)メソッドで以下のようにUISearchBarをナビゲーションバーにセットするとナビゲーションバーのバックボタンを押したときに再びtableView:accessoryButtonTappedForRowWithIndexPath:メソッドが呼ばれてしまいます。

// 呼び出し元
@implementation HogeViewController
// バックボタンで戻ってきたときに再度呼び出されてしまう。
- (void)tableView:(UITableView *)tv accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath {
    Hoge2ViewController *controller = [[[Hoge2ViewController alloc] initWithNibName:@"Hoge2ControllerView"
                                                                             bundle:[NSBundle mainBundle]] autorelease];
    [[self navigationController] pushViewController:controller animated:YES];
}
@end

// 呼び出し先
@implementation Hoge2ViewController

@synthesize searchBar;

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
    if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
        self.searchBar = [[[UISearchBar alloc] init] autorelease];
        self.searchBar.delegate = self;
        self.searchBar.autocapitalizationType = UITextAutocapitalizationTypeNone;
        self.navigationItem.titleView = self.searchBar;
        self.navigationItem.titleView.frame = CGRectMake(0, 0, 320, 44);
    }
    return self;
}
@end

UISearchBarをナビゲーションコントローラーにセットするときはviewDidLoadメソッドでやるのが良いみたいです。
他の部品(UIView,UILabel)でも試してみましたがこの現象が起こるのはUISearchBarだけのようです。

// 呼び出し元
@implementation HogeViewController
- (void)tableView:(UITableView *)tv accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath {
    Hoge2ViewController *controller = [[[Hoge2ViewController alloc] initWithNibName:@"Hoge2ControllerView"
                                                                             bundle:[NSBundle mainBundle]] autorelease];
    [[self navigationController] pushViewController:controller animated:YES];
}
@end

// 呼び出し先
@implementation Hoge2ViewController

@synthesize searchBar;

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
    if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
        
    }
    return self;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    self.searchBar = [[[UISearchBar alloc] init] autorelease];
    self.searchBar.delegate = self;
    self.searchBar.autocapitalizationType = UITextAutocapitalizationTypeNone;
    self.navigationItem.titleView = self.searchBar;
    self.navigationItem.titleView.frame = CGRectMake(0, 0, 320, 44);
}
@end

2009/08/28追記

メモリ不足でdidReceiveMemoryWarningメソッド呼ばれるとその後、viewDidLoadメソッドが再び呼ばれるので、init系メソッドでUI部品のインスタンスを生成するのはあまりよくないような気がしました。ややこしくなるのでUI部品のインスタンス生成はviewDidLoadメソッドに集約させるのが無難かと思います。