web-dev-qa-db-ja.com

reload / reloadRowなしでセルの高さを更新する方法はありますか?

私はiMessageのようなビューを作成し、下部のテキストビューにテキストを入力するだけです。これを行うにはテーブルビューを使用し、最後のセルにはテキストビューを使用します。 2行以上の長いテキストを入力すると、テキストビューが必要になり、セルが調整されます。セルの高さを更新する必要があります。しかし、テーブルビューの再読み込みまたは行の再読み込みを使用すると、テキストビューのコンテンツが消え、キーボードも消えます。それを修正するより良い方法はありますか?

それを簡単にするためにツールバーを使うべきでしょうか?しかし、私はまだテーブルビューがそれを行うことができるとは思っていません。

26
ZhouQi

beginUpdatesendUpdatesを呼び出すと、セルはスムーズにサイズ変更されます。これらの呼び出しの後、tableViewはテーブル内のすべてのセルに対してtableView:heightForRowAtIndexPath:を送信します。tableViewがすべてのセルのすべての高さを取得すると、サイズ変更をアニメーション化します。

また、セルのプロパティを直接設定することで、セルを再読み込みせずに更新できます。 tableViewとtableView:cellForRowAtIndexPath:を使用する必要はありません。

セルのサイズを変更するには、次のようなコードを使用します

- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {
    NSString *newText = [textView.text stringByReplacingCharactersInRange:range withString:text];
    CGSize size = // calculate size of new text
    if ((NSInteger)size.height != (NSInteger)[self tableView:nil heightForRowAtIndexPath:nil]) {
        // if new size is different to old size resize cells. 
        // since beginUpdate/endUpdates calls tableView:heightForRowAtIndexPath: for all cells in the table this should only be done when really necessary.
        [self.tableView beginUpdates];
        [self.tableView endUpdates];
    }
    return YES;
}

リロードせずにセルのコンテンツを変更するには、次のようにします。

- (void)configureCell:(FancyCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
    MyFancyObject *object = ...
    cell.textView.text = object.text;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    FancyCell *cell = (FancyCell *)[tableView dequeueReusableCellWithIdentifier:@"CellWithTextView"];
    [self configureCell:cell forRowAtIndexPath:indexPath];
    return cell;
}

// whenever you want to change the cell content use something like this:

    NSIndexPath *indexPath = ...
    FancyCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
    [self configureCell:cell forRowAtIndexPath:indexPath];
67
Matthias Bauch

この機能を処理するために、UITableViewCellのサブクラスを作成しました。

.hファイル:

#import <UIKit/UIKit.h>

@protocol AECELLSizeableDelegate;

@interface AECELLSizeable : UITableViewCell

@property (weak, nonatomic) id <AECELLSizeableDelegate> delegate;

@property IBOutlet UIView *viewMinimized;
@property IBOutlet UIView *viewMaximized;
@property BOOL maximized;

@property CGFloat height;

- (IBAction)clickedConfirm:(id)sender;
- (IBAction)clickedCancel:(id)sender;

- (void)minimizeForTableview: (UITableView*)tableView;
- (void)maximizeForTableview: (UITableView*)tableView;
- (void)toggleForTableview: (UITableView*)tableView;

@end

@protocol AECELLSizeableDelegate <NSObject>

- (void)sizeableCellConfirmedForCell: (AECELLSizeable*)cell;
- (void)sizeableCellCancelledForCell: (AECELLSizeable*)cell;

@end

.mファイル:

#import "AECELLSizeable.h"

@implementation AECELLSizeable

- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
    if (self) {
        // Initialization code
    }
    return self;
}

- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
    [super setSelected:selected animated:animated];

    // Configure the view for the selected state
}

- (void)minimizeForTableview: (UITableView*)tableView
{
    self.maximized = NO;
    [self.viewMinimized setHidden:NO];
    [self.viewMaximized setHidden:YES];
    self.height = self.viewMinimized.frame.size.height;
    [tableView beginUpdates];
    [tableView endUpdates];
}

- (void)maximizeForTableview: (UITableView*)tableView
{
    self.maximized = YES;
    [self.viewMinimized setHidden:YES];
    [self.viewMaximized setHidden:NO];
    self.height = self.viewMaximized.frame.size.height;
    [tableView beginUpdates];
    [tableView endUpdates];
}

- (void)toggleForTableview:(UITableView *)tableView
{
    if (self.maximized) {
        [self minimizeForTableview:tableView];
    } else {
        [self maximizeForTableview:tableView];
    }
}

- (void)clickedConfirm:(id)sender
{
    [self.delegate sizeableCellConfirmedForCell:self];
}

- (void)clickedCancel:(id)sender
{
    [self.delegate sizeableCellCancelledForCell:self];
}

@end

使用例:

  1. IBで静的UITableViewを使用してUITableViewControllerを作成する
  2. AECELLSizeable(またはそのサブクラス)であるセルをテーブルビューに追加します。
  3. このセルに2つのUIViewを作成します。 1つのUIViewは最小化されている間に表示されるコンテンツに使用され、もう1つは最大化されている間に表示されるコンテンツに使用されます。これらのセルがy軸の0から始まり、その高さが各状態のセルに必要な高さと同じであることを確認してください。
  4. 必要なこれらの2つのビューにサブビューを追加します。オプションで、最大化されたUIViewに確認とキャンセルのUIButtonを追加し、提供されたIBActionをフックして、これらのイベントでデリゲートコールバックを受信します。
  5. AECELLSizeableDelegateに準拠するようにテーブルビューコントローラーを設定し、セルのデリゲートプロパティをテーブルビューコントローラーに設定します。
  6. AECELLSizeableセルのUIViewControllerのインターフェイスファイルにIBOutletを作成します。
  7. IBに戻り、セルの初期高さが最小化されたバージョンの高さであることを確認し、先ほど作成したIBOutletを接続します。
  8. 次のように、テーブルビューコントローラの実装ファイルでテーブルビューのheightForRowAtIndexPathコールバックメソッドを定義します。

    - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
      {
          if (indexPath.row == 0) {
              //Sizeable Cell
              return self.cellSizeable.height;
          } else {
              return [super tableView:tableView heightForRowAtIndexPath:indexPath];
          }
      }
    
  9. テーブルビューコントローラーのviewDidLoadメソッドで、セルでminimizeForTableviewを呼び出して、最小化された状態で開始するようにします。

  10. 次に、テーブルビューからdidSelectRowAtIndexPathコールバックを介して選択されたときにセルでMaximizeForTableview:を呼び出します(または、それを処理したい場合)。セル(または、繰り返しますが、それ以外の場合は処理します)。
2
Adam Eisfeld