概要
いくつかのcontainer viewの表示と非表示の切り替えを行う。
縦に切り替えを行うためにtable view controllerのcellの高さを調節する。
container view controllerの表示
- static table Cell上にcontainerを設置。
- containerにviewControllerをセットして表示する。
- cellかどうかの判定は、cellの比較によって判定し、heightは自分で定義する。
static CGFloat const kViewControllerCellHeight = 300;
@property (nonatomic, weak) IBOutlet UITableViewCell *viewControllerCell;
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [super tableView:tableView cellForRowAtIndexPath:indexPath];
if (cell == self.viewControllerCell) {
return kViewControllerCellHeight;
}
return 0;
}
container view controllerの表示の切り替え
- cellの表示・非表示の処理
- 表示・非表示用のpropertyを追加する
@property (nonatomic, assign, getter = isViewControllerCellHidden) BOOL viewControllerCellHidden;
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [super tableView:tableView cellForRowAtIndexPath:indexPath];
if (cell == self.viewControllerCell && !self.isViewControllerCellHidden) {
return kViewControllerCellHeight;
}
return 0;
}
ライフサイクル
高さが300に指定した場合(画面内に収まる)
- WTDTableViewController prepareForSegue
- WTDContainerViewController viewDidLoad
- WTDTableViewController viewDidLoad
- WTDTableViewController viewWillAppear
- WTDTableViewController viewWillLayoutSubviews
- WTDTableViewController heightForRowAtIndexPath
- WTDContainerViewController viewWillAppear
- WTDTableViewController viewDidLayoutSubviews
- WTDTableViewController viewWillLayoutSubviews
- WTDTableViewController viewDidLayoutSubviews
- WTDContainerViewController viewWillLayoutSubviews
- WTDContainerViewController viewDidLayoutSubviews
- WTDTableViewController viewDidAppear
- WTDContainerViewController viewDidAppear
- WTDContainerViewController viewDidAppear
高さを1000に指定した場合(画面からはみ出るcellの場合)
- WTDTableViewController prepareForSegue
- WTDContainerViewController viewDidLoad
- WTDTableViewController viewDidLoad
- WTDTableViewController viewWillAppear
- WTDTableViewController viewWillLayoutSubviews
- WTDTableViewController heightForRowAtIndexPath
- WTDContainerViewController viewWillAppear
- WTDTableViewController viewDidLayoutSubviews
- WTDTableViewController viewWillLayoutSubviews
- WTDTableViewController viewDidLayoutSubviews
- WTDContainerViewController viewWillLayoutSubviews
- WTDContainerViewController viewDidLayoutSubviews
- WTDTableViewController viewDidAppear
- WTDContainerViewController viewDidAppear
- WTDContainerViewController viewDidAppear
- WTDTableViewController viewWillLayoutSubviews
- WTDTableViewController viewDidLayoutSubviews
※ またスクロールの度に
- WTDTableViewController viewWillLayoutSubviews
- WTDTableViewController viewDidLayoutSubviews
が呼ばれる
viewの非表示を実行した際の動作
- WTDContainerViewController viewWillDisAppear
- WTDTableViewController heightForRowAtIndexPath
- WTDTableViewController viewWillLayoutSubviews
- WTDContainerViewController viewWillAppear
- WTDTableViewController viewDidLayoutSubviews
- WTDContainerViewController viewWillLayoutSubviews
- WTDContainerViewController viewDidLayoutSubviews
- WTDContainerViewController viewDidAppear
viewを再表示させた時の動作
- WTDContainerViewController viewWillDisAppear
- WTDTableViewController heightForRowAtIndexPath
- WTDTableViewController viewWillLayoutSubviews
- WTDContainerViewController viewWillAppear
- WTDTableViewController viewDidLayoutSubviews
- WTDContainerViewController viewWillLayoutSubviews
- WTDContainerViewController viewDidLayoutSubviews
- WTDContainerViewController viewDidAppear
(要件)containerViewControllerを表示された時にキーボードにフォーカスを当てる
1 viewWillApearでキーボードを開くことを呼び出す
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[self.textField resignFirstResponder];
}
※ cellの高さがあるときもないときもキーボードの呼び出しがされる。
2 viewControllerのframeの高さの変化をKVOで受け取る
- (void)viewDidLoad
{
[super viewDidLoad];
[self.view addObserver:self forKeyPath:NSStringFromSelector(@selector(frame)) options:NSKeyValueObservingOptionNew context:NULL];
self.viewControllerCellHidden = YES;
}
#pragma mark - Key Value Observing
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if ([keyPath isEqualToString:NSStringFromSelector(@selector(frame))]) {
fprintf(stderr, "call KVO\n");
[self.containerViewController.textField becomeFirstResponder];
}
}
※ KVOがcallされるのは、containerViewControllerのviewDidLoadが実行されるタイミングだけだった。
3 prepareForSegueでcontainerViewControllerをhiddenにして、heightForRowAtIndexPathでhiddenを解除する
#pragma mark - Table view data source
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
[super prepareForSegue:segue sender:sender];
if ([[segue identifier] isEqualToString:kPushContainerViewController]) {
self.containerViewController = [segue destinationViewController];
self.containerViewController.view.hidden = YES;
}
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [super tableView:tableView cellForRowAtIndexPath:indexPath];
if (cell == self.viewControllerCell && !self.isViewControllerCellHidden) {
self.containerViewController.view.hidden = NO;
return kViewControllerCellHeight;
} else {
self.containerViewController.view.hidden = YES;
}
return 0;
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
if (!self.view.hidden) {
[self.textField becomeFirstResponder];
} else {
[self.textField resignFirstResponder];
}
}
※ これであれば、判定は行える。
問題点
- UITableViewCell *cell = [super tableView:tableView cellForRowAtIndexPath:indexPath];
の処理って遅いのでは。 - cellの高さが0でも、containerの中ではviewのレンダーが行われているのが無駄かも。
- keyboardを出すときや、cellのheightのサイズが大きいときに、
WTDTableViewControllerのviewWillLayoutSubviewsとviewDidLayoutSubviewsがたくさん実行されている。 - 結局、containerViewControllerのコードを変えないとうまくキーボードを表示させることができなかった
今後考えたいこと
- そもそもtableViewControllerではなく、UIViewControllerでscrollViewを使う方がいいのかもしれない。両者を比較してみたい。
- 今回は、containerに、UIViewControllerを設置したが、UITableViewControllerを設置して、その中でviewの大きさが変わるときにどのように対処するかもまとめたい。
- そもそもviewの実行が何度も行われるから、containerを利用するべきでないのかもしれない。。
その後の検証
- static cellでなくscroll viewで実装
- constraintで高さを調整
- 問題としては今まではcontentSizeの変更で親viewの大きさを変える動作をさせていたが、それが動かなかった。blockを受け渡して必要なタイミングで更新するロジックに
- staticセルのように新しくcellを生成するタイミングでのひっかかりがなくなったが、逆にnavigation controllerを使って遷移するまでの時間は長くなった
- ひっかかりがあるほうがUXとして悪いという判断で大幅リファクタリングをした