読者です 読者をやめる 読者になる 読者になる

hachinoBlog

hachinobuのエンジニアライフ

A view can only be associated with at most one view controller at a time!エラー

幾つかの画面(ViewController)で共通のUIActionSheetを使い回したかったので共通のUIActionSheetを管理するシングルトンクラスを生成して、そのシングルトンクラスのUIActionSheetを表示させたいViewControllerでshowFromBarButtonItemしていた。
iPadではBarButtonItemを再度押したら表示しているUIActionSheetを消すという処理を実装してあげないといけないので、その処理を書いたら下記エラーが発生した。

'A view can only be associated with at most one view controller at a time! View ; }; layer = > is associated with <_UIActionSheetHostingController: 0xa109260>. Clear this association before associating this view with <_UIActionSheetHostingController: 0xa517040>.'

1つのViewは1つのViewControllerにしか所属できないよということ。
UIActionSheetは実は_UIActionSheetHostingControllerというControllerに既に所属している?らしい。
これを回避するには共通で使用したいUIActionSheetを使うViewController内でUIActionSheetのインスタンスを作成しておき、そのインスタンスに共通で使用したいUIActionSheetを代入するとエラーを回避できる。

//共通で使い回すUIActionSheetを管理するシングルトンクラス
[ASManager.h]
@interface ASManager : NSObject

@property (readonly, nonatomic) UIActionSheet *as;

+ (ASManager *)sharedManager;

@end

[ASManager.m]
@interface ASManager () 

@property (retain, nonatomic) UIActionSheet *as;

@end

@implementation ASManager

+ (ASManager *)sharedManager
{
    static ASManager *sharedManager;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedManager = [[ASManager alloc] init];
    });
    
    return sharedManager;
}

- (id)init
{
    self = [super init];
    if (self) {
        //共通で使い回したいUIActionSheet作成
        _as = [[UIActionSheet alloc] init];
        _as.title = @"選択してください"
        [_as addButtonWithTitle:@"A"];
        [_as addButtonWithTitle:@"B"];
        [_as addButtonWithTitle:@"C"];
        [_as addButtonWithTitle:@"キャンセル"];
        _as.cancelButtonIndex = 3;
        _as.delegate = self;
    }
    
    return self;
    
}

- (void)dealloc
{
    _as.delegate = nil;
    [_as release];
    [super dealloc];
}

#pragma mark - UIActionSheetDelegate
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex
{
    //選択された各ボタンに応じて処理を書く
    switch (buttonIndex) {
        case 0:
            break;
            
        case 1:
            break;
            
        case 2:
            break;
            
        default:
            return;
            break;
    }
}

@end

このシングルトンを使用するViewControllerクラスのエラー発生Verメソッド

//UIBarButtonの押下がトリガーとなり呼ばれるメソッド
- (IBAction)useASManagerActionSheet:(id)sender
{
    //閉じる処理
    if ([ASManager sharedManager].as.visible) {
        //ここで本題のエラーが発生する
        [[ASManager sharedManager].as dismissWithClickedButtonIndex:-1 animated:YES];
        return;
    }

    //シングルトンのUIActionSheetを直接使用
    [[ASManager sharedManager].as showFromBarButtonItem:sender animated:YES];
}

エラー回避Ver.
前提として@interface部でUIActionSheetのインスタンスas変数が定義しているものとする

//UIBarButtonの押下がトリガーとなり呼ばれるメソッド
- (IBAction)useASManagerActionSheet:(id)sender
{
    //閉じる処理
    if (_as.visible) {
        [_as dismissWithClickedButtonIndex:-1 animated:YES];
        return;
    }
    
    //シングルトンのUIActionSheetを定義したインスタンスに代入して使ってあげる
    _as = [ASManager sharedManager].as;
    [_as showFromBarButtonItem:sender animated:YES];
    
}

こうすればdismissWithClickedButtonIndexした時にクラッシュしなくなる。

参考
http://program.station.ez-net.jp/special/objective-c/xcode/build-error/view-controller.asp