LoginSignup
9
8

More than 5 years have passed since last update.

static cellとcontainerを利用して、controllerを分割する実験

Last updated at Posted at 2014-01-27

概要

いくつかの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に指定した場合(画面内に収まる)

  1. WTDTableViewController prepareForSegue
  2. WTDContainerViewController viewDidLoad
  3. WTDTableViewController viewDidLoad
  4. WTDTableViewController viewWillAppear
  5. WTDTableViewController viewWillLayoutSubviews
  6. WTDTableViewController heightForRowAtIndexPath
  7. WTDContainerViewController viewWillAppear
  8. WTDTableViewController viewDidLayoutSubviews
  9. WTDTableViewController viewWillLayoutSubviews
  10. WTDTableViewController viewDidLayoutSubviews
  11. WTDContainerViewController viewWillLayoutSubviews
  12. WTDContainerViewController viewDidLayoutSubviews
  13. WTDTableViewController viewDidAppear
  14. WTDContainerViewController viewDidAppear
  15. WTDContainerViewController viewDidAppear

高さを1000に指定した場合(画面からはみ出るcellの場合)

  1. WTDTableViewController prepareForSegue
  2. WTDContainerViewController viewDidLoad
  3. WTDTableViewController viewDidLoad
  4. WTDTableViewController viewWillAppear
  5. WTDTableViewController viewWillLayoutSubviews
  6. WTDTableViewController heightForRowAtIndexPath
  7. WTDContainerViewController viewWillAppear
  8. WTDTableViewController viewDidLayoutSubviews
  9. WTDTableViewController viewWillLayoutSubviews
  10. WTDTableViewController viewDidLayoutSubviews
  11. WTDContainerViewController viewWillLayoutSubviews
  12. WTDContainerViewController viewDidLayoutSubviews
  13. WTDTableViewController viewDidAppear
  14. WTDContainerViewController viewDidAppear
  15. WTDContainerViewController viewDidAppear
  16. WTDTableViewController viewWillLayoutSubviews
  17. WTDTableViewController viewDidLayoutSubviews

※ またスクロールの度に
- WTDTableViewController viewWillLayoutSubviews
- WTDTableViewController viewDidLayoutSubviews
が呼ばれる

viewの非表示を実行した際の動作

  1. WTDContainerViewController viewWillDisAppear
  2. WTDTableViewController heightForRowAtIndexPath
  3. WTDTableViewController viewWillLayoutSubviews
  4. WTDContainerViewController viewWillAppear
  5. WTDTableViewController viewDidLayoutSubviews
  6. WTDContainerViewController viewWillLayoutSubviews
  7. WTDContainerViewController viewDidLayoutSubviews
  8. WTDContainerViewController viewDidAppear

viewを再表示させた時の動作

  1. WTDContainerViewController viewWillDisAppear
  2. WTDTableViewController heightForRowAtIndexPath
  3. WTDTableViewController viewWillLayoutSubviews
  4. WTDContainerViewController viewWillAppear
  5. WTDTableViewController viewDidLayoutSubviews
  6. WTDContainerViewController viewWillLayoutSubviews
  7. WTDContainerViewController viewDidLayoutSubviews
  8. 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として悪いという判断で大幅リファクタリングをした

9
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
8