UIViewサブクラスの初期化ボタンに悩んだ話
csny/ChipflipGame · GitHub
先日つくった簡単なゲームにリセットボタンを導入するのに苦労した。
UIViewのサブクラスでは、ボタンを配置できない。UIViewControllerのサブクラスだと配置できるんだけどね。
そのせいで、どうしてもインスタンス化してaddSubviewしたUIViewのサブクラスが初期化・リセットできない。UIViewサブクラスの- (id)initWithFrame:(CGRect)frameの2回目がどうしても呼び出せないのだ。
UIViewサブクラス内の初期化処理を別なメソッドに分けてインスタンス化して呼び出し、[self setNeedsDisplay]してもダメだった。別なUIViewインスタンスを"作って"操作してることになるんだろうね。UIViewサブクラス内にリセット用のクラスメソッドを用意したり[self viewDidLoad]したり、いや、マジでいろいろ悩んだ。
とにかくクラスの外から"同じUIViewインスタンス"の初期化処理を呼び出すってのが難しいな、と。
結論として、ビューを削除・再作成する方法に落ち着いた。
- ビュー1つ1つにタグ(NSIntegerらしいので1以上の整数)を振って、
- viewDidLoadにまとめて書いていたViewのインスタンス化とaddSubviewする部分を、別なメソッドを作ってお引越し、
- ボタンの処理1.タグを指定してビューを削除。[hoge removeFromSuperview];
- ボタンの処理2.View作成部分のメソッドを再呼び出し。
という感じ。
初期化したいビューのほかにラベルやボタンまで削除・再作成しているのは、UIViewだと並び順が大事だから。ビューだけ削除・再作成しても、ボタンが押せなくなるのだ。いや、ハマるハマる。
座標の効率的な配列化も募集中。NSValue使いづらいんすけど……
試行中
- (void)viewDidLoad { [super viewDidLoad]; // DrawingViewクラスのインスタンス化 DrawingView *drawView = [[DrawingView alloc] initWithFrame:self.view.bounds]; //中略 // ラベルを作成 UILabel *explainLabel1 = [[UILabel alloc] init]; //中略 // ボタンを作成 UIButton *resetBtn = [UIButton buttonWithType:UIButtonTypeRoundedRect]; [resetBtn addTarget:self action:@selector(button_Tapped:) forControlEvents:UIControlEventTouchUpInside]; //中略 // UIView、ボタン、ラベルをビューに追加 [self.view addSubview:drawView]; [self.view addSubview:explainLabel1]; [self.view addSubview:resetBtn]; } // リセットボタン動作 - (void)button_Tapped:(id)sender { // 何書いてもうまくいかん!! }
改善後
- (void)viewDidLoad { [super viewDidLoad]; [self addViews]; } - (void)addViews { // DrawingViewクラスのインスタンス化 DrawingView *drawView = [[DrawingView alloc] initWithFrame:self.view.bounds]; drawView.tag = 73186; //中略 // ラベルを作成 UILabel *explainLabel1 = [[UILabel alloc] init]; explainLabel1.tag = 73187; //中略 // ボタンを作成 UIButton *resetBtn = [UIButton buttonWithType:UIButtonTypeRoundedRect]; resetBtn.tag = 73189; [resetBtn addTarget:self action:@selector(button_Tapped:) forControlEvents:UIControlEventTouchUpInside]; //中略 // UIView、ボタン、ラベルをビューに追加 [self.view addSubview:drawView]; [self.view addSubview:explainLabel1]; [self.view addSubview:resetBtn]; } // リセットボタン動作 - (void)button_Tapped:(id)sender { // ビュー削除 UIView *beforeView1 = [self.view viewWithTag:73186]; UIView *beforeView2 = [self.view viewWithTag:73187]; UIView *beforeView4 = [self.view viewWithTag:73189]; /* 実装順を覚えてればこれでも可 UIView *beforeView1 = [self.view.subviews objectAtIndex:0]; UIView *beforeView2 = [self.view.subviews objectAtIndex:1]; UIView *beforeView4 = [self.view.subviews objectAtIndex:3]; */ [beforeView1 removeFromSuperview]; [beforeView2 removeFromSuperview]; [beforeView4 removeFromSuperview]; // 再作成 [self addViews]; }