0

バックグラウンド

_tableViewという名前のUITableViewivarを持つクラスがあります。このクラスは、UITableViewDatasourceとUITableViewDelegate_tableViewを実装します。UITableViewセルは、FormTableCellのインスタンスです。FormTableCellは、UILabelとUITextFieldを持つUITableViewCellのサブクラスです。クラスには、textFieldDelegateというプロパティがあります。tableView:cellForIndexPath:が呼び出されると、現在のtextFieldDelegateが返されたセルのtextFieldプロパティに割り当てられます。また、クラスtextFieldDelegateが変更されたときに、セルのテキストフィールドのデリゲートを更新したいと思います。私はこれを行うための3つの方法を考え出しましたが、最善の方法がわかりません。

  1. 表示されている各セルのデリゲートを新しいtextFieldDelegateで更新します。
  2. すべてのセルを繰り返し処理し、新しいtextFieldDelegateを割り当てます
  3. tableViewsデータをリロードします。

オプション1は適切ですが、tableViewのvisibleCells配列にないセルを表示する前に、tableView:cellForIndexPathが呼び出されることを前提としています。これが起こらない場合は、セルごとにデリゲートが異なる可能性があり、それは私が望んでいることではありません。

オプション2は、セルの数が少ない場合にのみ適切に機能するため、最悪です。FormTablesのセルの数が常に少ないことを願っていますが、私にはわかりません。

オプション3は、非表示のセルを表示する前にtableView:cellForIndexPath:が呼び出されるという私の仮定が無効であり、セルが多いテーブルを処理するため、オプション2よりも優先される場合にオプション1よりも優先されます。

質問

どちらが最良のオプションであり、非表示のセルが表示されるか、正しく操作されない前にtableView:cellForIndexPath:が呼び出されるという私の仮定はどれですか?

コード

- (void)setTextFieldDelegate:(id<UITextFieldDelegate>)textFieldDelegate
{
  if(_textFieldDelegate != textFieldDelegate) {
    _textFieldDelegate = textFieldDelegate;


    //Update _textFieldDelegate for all visible cells. 
    //!DEV: Assuming non-visible cells will be recreated with tableView:cellForIndexPath:
    for (FormTableCell *cell in [_tableView visibleCells]) {
      [[cell textField] setDelegate:_textFieldDelegate];
    }

    //!DEV: This will potentially generate memory issues for a large number of cells
    for(NSUInteger i = 0; i < kNumberOfFields; ++i) {
      NSIndexPath *indexPath = [NSIndexPath indexPathForItem:(NSInteger)i inSection:0];
      FormTableCell *cell = (FormTableCell*)[_tableView cellForRowAtIndexPath:indexPath];
      [[cell textField] setDelegate:_textFieldDelegate];
    }

    //!DEV: Maybe this is the best method, but it requires the recreation of visibleCells.
    [_tableView reloadData];

  }
}

最終ステータス

だから、これが私がやったことです。デコレートデリゲート(?)を実装しました

- (void)setTextFieldDelegate:(id<UITextFieldDelegate>)textFieldDelegate
{
  if(_textFieldDelegate != textFieldDelegate) {
    _textFieldDelegate = textFieldDelegate;
  }
}

#pragma mark - UITextFieldDelegate Implementation

//This class forwards UITextFieldDelegate methods on to _textFieldDelegate, if implemented.
//Provides default functionality is not implemented.

- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField
{
  if([_textFieldDelegate respondsToSelector:@selector(textFieldShouldBeginEditing:)]) {
    return [_textFieldDelegate textFieldShouldBeginEditing:textField];
  } //implicit else
  return YES;
}
- (void)textFieldDidBeginEditing:(UITextField *)textField {

  //Setup _closeKeyboardButton behind _tableView
  //view::closeKeyboardButton (_closeKeyboardButton)
  NSAssert(_closeKeyboardButton == nil, @"!DEV: Thought _closeKeyboardButton would always be nil here.");
  if(_closeKeyboardButton != nil) {
    [_closeKeyboardButton removeFromSuperview];
    _closeKeyboardButton == nil;
  }

  CGRect closeKeyboardFrame = {CGPointZero, [self frame].size};

  UIButton *closeKeyboardButton = [UIButton buttonWithType:UIButtonTypeCustom];
  [closeKeyboardButton setFrame:closeKeyboardFrame];

  [closeKeyboardButton addTarget:self
                          action:@selector(_closeKeyboardButtonTapped:)
                forControlEvents:UIControlEventTouchUpInside];

  [self insertSubview:closeKeyboardButton belowSubview:_tableView];
  _closeKeyboardButton = closeKeyboardButton;


  if([_textFieldDelegate respondsToSelector:@selector(textFieldDidBeginEditing:)]) {
    [_textFieldDelegate textFieldDidBeginEditing:textField];
  }
}

- (BOOL)textFieldShouldEndEditing:(UITextField *)textField
{
  if([_textFieldDelegate respondsToSelector:@selector(textFieldShouldEndEditing:)]) {
    return [_textFieldDelegate textFieldShouldEndEditing:textField];
  } //implicit else
  return YES;
}

- (void)textFieldDidEndEditing:(UITextField *)textField
{
  if([_textFieldDelegate respondsToSelector:@selector(textFieldDidBeginEditing:)]) {
    [_textFieldDelegate textFieldDidBeginEditing:textField];
  }

  //Tear down _closeKeyboardButton
  NSAssert(_closeKeyboardButton != nil, @"!DEV: Thought _closeKeyboardButton would always exist here.");
  [_closeKeyboardButton removeFromSuperview];
  _closeKeyboardButton = nil;
}

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
  if([_textFieldDelegate respondsToSelector:@selector(textField:shouldChangeCharactersInRange:replacementString:)]) {
    return [_textFieldDelegate textField:textField shouldChangeCharactersInRange:range replacementString:string];
  } //implicit else
  return YES;
}

- (BOOL)textFieldShouldClear:(UITextField *)textField
{
  if([_textFieldDelegate respondsToSelector:@selector(textFieldShouldClear:)]) {
    return [_textFieldDelegate textFieldShouldClear:textField];
  } //implicit else
  return YES;
}

- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
  if([_textFieldDelegate respondsToSelector:@selector(textFieldShouldReturn:)]) {
    return [_textFieldDelegate textFieldShouldReturn:textField];
  } //implicit else
  return NO;
}
4

1 に答える 1

0

私の経験では、tableViewのvisibleCellsプロパティにアクセスする必要はありません。

すべてをtableView:cellForRowAtIndexPath:メソッドで実行します。セルが作成されるたびに呼び出されるか、dequeReusableCellの呼び出しからリサイクルされたセルが引き出されます...このメソッドは、表示されているセルが画面に表示される前に呼び出されます。

教育目的で、すべてのテーブルビューメソッドにいくつかのNSLogを挿入して、それらが呼び出された順序と、それらが呼び出される原因となるアクションを正確に確認できます。

無関係な注意-セル内のサブビューに対してフレーム操作を行う必要がある場合は、FormTableCellのlayoutSubviewsメソッドをオーバーライドするか、tableView:willDisplayCell:forRowAtIndexPath:メソッドを実装してそこで実行できます。

于 2012-12-10T18:37:01.547 に答える