選択すると、UITableViewCellの高さの変化をアニメーション化できますか?


393

私は使用しています UITableViewiPhoneアプリでしていますが、グループに属する人のリストがあります。ユーザーが特定の人物をクリックして(セルを選択して)クリックすると、セルの高さが大きくなり、その人物のプロパティを編集するためのいくつかのUIコントロールが表示されます。

これは可能ですか?

回答:


879

私はこれに対する本当に簡単な解決策を、UITableView私が取り組んでいる副作用として見つけました...

通常はを介して元の高さを報告する変数にセルの高さtableView: heightForRowAtIndexPath:を保存し、高さの変化をアニメーション化する場合は、変数の値を変更してこれを呼び出します...

[tableView beginUpdates];
[tableView endUpdates];

あなたはそれが完全なリロードを行わないことがわかりますが、それがUITableViewセルを再描画し、セルの新しい高さの値を取得する必要があることを知るのに十分です...そして何を推測しますか?それはあなたのために変化をアニメートします。甘い。

ブログに詳細な説明と完全なコードサンプルがあります... UITableViewセルの高さの変化をアニメーション化


6
これは素晴らしいです。しかし、テーブルのアニメーション速度を制御する方法はありますか?
Josh Kahane

7
これは機能しますが、セルを大きくした場合、たとえば44から74に変更し、次に再び44に変更すると、セパレーターラインは完全に奇妙に機能します。誰かがこれを確認できますか?
plaetzchen 2013年

46
これは奇妙な解決策ですが、これはWWDC 2010の「マスタリングテーブルビュー」セッションでもAppleが推奨するものです。調査に約2時間費やしたので、ドキュメントにこれを追加することについてバグレポートを提出します。
bpapa 2013

5
私はこの解決策を試しましたが、時々しか機能しません。セルを押すと、50%の確率で機能します。誰もが同じバグを持っていますか?そのiOS7からですか?
Joan Cardona 2014年

7
:今、それはまた、公式ドキュメントに書かれているYou can also use this method followed by the endUpdates method to animate the change in the row heights without reloading the cell. developer.apple.com/library/ios/documentation/UIKit/Reference/...
ヤロスラフ

63

サイモン・リーの答えが好きです。実際にはその方法を試していませんが、リスト内のすべてのセルのサイズが変わるようです。タップした細胞だけの交換を期待していた。私はちょっとサイモンのようにそれをやったが、少しだけ違いました。選択すると、セルの外観が変わります。そして、それはアニメーション化します。それを行うためのちょうど別の方法。

現在選択されているセルインデックスの値を保持するintを作成します。

int currentSelection;

次に:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    int row = [indexPath row];
    selectedNumber = row;
    [tableView beginUpdates];
    [tableView endUpdates];
}

次に:

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {

    if ([indexPath row] == currentSelection) {
        return  80;
    }
    else return 40;


}

tableView:cellForRowAtIndexPath:でも同様の変更を加えて、セルのタイプを変更したり、セルのxibファイルをロードしたりできると思います。

このように、currentSelectionは0から始まります。リストの最初のセル(インデックス0)がデフォルトで選択されているように見せたくない場合は、調整を行う必要があります。


2
私の投稿に添付されたコードを確認してください、私はこれを正確に行いました、選択されたときにセルの高さを2倍にします。:)
サイモンリー

8
「私は実際にはその方法を試していませんが、リスト内のすべてのセルのサイズを変更するように見えます」-それであなたはそれほど難しく見えませんでした。
jamie

13
現在の選択は既にtableView.indexPathForSelectedRowに保存されています。
ニック

22

選択したセルを追跡するプロパティを追加します

@property (nonatomic) int currentSelection;

(たとえば)のセンチネル値に設定して、が「通常」の位置で開始viewDidLoadすることを確認しUITableViewます

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view.

    //sentinel
    self.currentSelection = -1;
}

ではheightForRowAtIndexPath、あなたが選択したセルのためにあなたが望む高さを設定することができます

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
    int rowHeight;
    if ([indexPath row] == self.currentSelection) {
        rowHeight = self.newCellHeight;
    } else rowHeight = 57.0f;
    return rowHeight;
}

ではdidSelectRowAtIndexPath、あなたの現在の選択を保存し、必要に応じて、動的な高さを保存

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
        // do things with your cell here

        // set selection
        self.currentSelection = indexPath.row;
        // save height for full text label
        self.newCellHeight = cell.titleLbl.frame.size.height + cell.descriptionLbl.frame.size.height + 10;

        // animate
        [tableView beginUpdates];
        [tableView endUpdates];
    }
}

didDeselectRowAtIndexPathセンチネル値の選択指標バックを設定し、正規形への細胞バックをアニメート

- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath {       
        // do things with your cell here

        // sentinel
        self.currentSelection = -1;

        // animate
        [tableView beginUpdates];
        [tableView endUpdates];
    }
}

ありがとう、ありがとう、ありがとう!セルを切り替え可能にするコードを少し追加しました。以下のコードを追加しました。
Septronic、2015年

14

アニメーションがないためreloadDataは適切ではありません...

これは私が現在試していることです:

NSArray* paths = [NSArray arrayWithObject:[NSIndexPath indexPathForRow:0 inSection:0]];
[self.tableView beginUpdates];
[self.tableView insertRowsAtIndexPaths:paths withRowAnimation:UITableViewRowAnimationFade];
[self.tableView deleteRowsAtIndexPaths:paths withRowAnimation:UITableViewRowAnimationFade];
[self.tableView endUpdates];

ほとんど問題なく動作します。ほとんど。セルの高さを増やしており、セルが置き換えられると、テーブルビューにスクロールの位置が保持されているように、新しいセル(最初のセル)テーブル内)は、オフセットが高すぎる状態になり、スクロールビューが跳ね返って再配置されます。


この方法をUITableViewRowAnimationNoneと一緒に使用すると、スムーズになりますが完全な結果は得られないことが個人的にわかりました。
Ron Srebro、2010年

11

beginUpdates / endUpdatesを連続して呼び出すことに関するすべてのことは何なのかわかりません-[UITableView reloadRowsAtIndexPaths:withAnimation:]。単にを使用できます。これがプロジェクトの例です


自動レイアウトセルのテキストビューが伸びないという優れた点です。ただし、セルサイズを更新するときに[アニメーションなし]オプションが異常に見えるため、アニメーションにちらつきが発生する必要があります。
h3dkandi 2017

10

で解決しましたreloadRowsAtIndexPaths

didSelectRowAtIndexPath選択したセルのindexPath に保存し、reloadRowsAtIndexPaths最後に呼び出します(リロードする要素のリストのNSMutableArrayを送信できます)。

heightForRowAtIndexPath indexPathがリストにあるかどうかexpandIndexPathセルのとセンド・高さのかどうかをチェックすることができます。

この基本的な例を確認できますhttps : //github.com/ferminhg/iOS-Examples/tree/master/iOS-UITableView-Cell-Height-Change/celdascambiadetam これは簡単なソリューションです。

私があなたを助けるなら、私は一種のコードを追加します

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return 20;
}

-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath: (NSIndexPath*)indexPath
{
    if ([indexPath isEqual:_expandIndexPath])
        return 80;

    return 40;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    static NSString *CellIdentifier = @"Celda";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

    [cell.textLabel setText:@"wopwop"];

    return cell;
}

#pragma mark -
#pragma mark Tableview Delegate Methods

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    NSMutableArray *modifiedRows = [NSMutableArray array];
    // Deselect cell
    [tableView deselectRowAtIndexPath:indexPath animated:TRUE];
    _expandIndexPath = indexPath;
    [modifiedRows addObject:indexPath];

    // This will animate updating the row sizes
    [tableView reloadRowsAtIndexPaths:modifiedRows withRowAnimation:UITableViewRowAnimationAutomatic];
}


3

これをインデックスごとの行を拡張するために試してください:

@property (nonatomic) NSIndexPath *expandIndexPath;
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath*)indexPath
{
if ([indexPath isEqual:self.expandedIndexPath])
    return 100;

return 44;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSMutableArray *modifiedRows = [NSMutableArray array];
if ([indexPath isEqual:self.expandIndexPath]) {
    [modifiedRows addObject:self.expandIndexPath];
    self.expandIndexPath = nil;
} else {
    if (self.expandedIndexPath)
        [modifiedRows addObject:self.expandIndexPath];

    self.expandIndexPath = indexPath;
    [modifiedRows addObject:indexPath];
}

// This will animate updating the row sizes
[tableView reloadRowsAtIndexPaths:modifiedRows withRowAnimation:UITableViewRowAnimationAutomatic];

// Preserve the deselection animation (if desired)
[tableView selectRowAtIndexPath:indexPath animated:NO scrollPosition:UITableViewScrollPositionNone];
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ViewControllerCellReuseIdentifier];
    cell.textLabel.text = [NSString stringWithFormat:@"I'm cell %ld:%ld", (long)indexPath.section, (long)indexPath.row];

return cell;
}

3
BOOL flag;

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    flag = !flag;
    [tableView beginUpdates];
    [tableView reloadRowsAtIndexPaths:@[indexPath] 
                     withRowAnimation:UITableViewRowAnimationAutomatic];
    [tableView endUpdates];
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    return YES == flag ? 20 : 40;
}

2

カスタムセルに「詳細」を追加することを検索している私のような人への単なるメモ。

[tableView beginUpdates];
[tableView endUpdates];

すばらしい作品でしたが、セルビューを「トリミング」することを忘れないでください。Interface Builderからセルを選択->コンテンツビュー->プロパティインスペクターから " Clip subview "を選択


2

短いバージョンのSimonsがSwift 3に回答します。セルの選択を切り替えることもできます

var cellIsSelected: IndexPath?


  func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    cellIsSelected = cellIsSelected == indexPath ? nil : indexPath
    tableView.beginUpdates()
    tableView.endUpdates()
  }


  func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    if cellIsSelected == indexPath {
      return 250
    }
    return 65
  }

2

Swift 4以降

以下のコードをテーブルビューのdidselect行デリゲートメソッドに追加してください

tableView.beginUpdates()
tableView.setNeedsLayout()
tableView.endUpdates()

1

Simon Leeの回答のSwiftバージョン。

// MARK: - Variables 
  var isCcBccSelected = false // To toggle Bcc.



    // MARK: UITableViewDelegate
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {

    // Hide the Bcc Text Field , until CC gets focused in didSelectRowAtIndexPath()
    if self.cellTypes[indexPath.row] == CellType.Bcc {
        if (isCcBccSelected) {
            return 44
        } else {
            return 0
        }
    }

    return 44.0
}

次にdidSelectRowAtIndexPath()で

  func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
    self.tableView.deselectRowAtIndexPath(indexPath, animated: true)

    // To Get the Focus of CC, so that we can expand Bcc
    if self.cellTypes[indexPath.row] == CellType.Cc {

        if let cell = tableView.cellForRowAtIndexPath(indexPath) as? RecipientTableViewCell {

            if cell.tag == 1 {
                cell.recipientTypeLabel.text = "Cc:"
                cell.recipientTextField.userInteractionEnabled = true
                cell.recipientTextField.becomeFirstResponder()

                isCcBccSelected = true

                tableView.beginUpdates()
                tableView.endUpdates()
            }
        }
    }
}

1

はい、可能です。

UITableView デリゲートメソッドがあります didSelectRowAtIndexPath

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    [UIView animateWithDuration:.6
                          delay:0
         usingSpringWithDamping:UIViewAnimationOptionBeginFromCurrentState
          initialSpringVelocity:0
                        options:UIViewAnimationOptionBeginFromCurrentState animations:^{

                            cellindex = [NSIndexPath indexPathForRow:indexPath.row inSection:indexPath.section];
                            NSArray* indexArray = [NSArray arrayWithObjects:indexPath, nil];
                            [violatedTableView beginUpdates];
                            [violatedTableView reloadRowsAtIndexPaths:indexArray withRowAnimation:UITableViewRowAnimationAutomatic];
                            [violatedTableView endUpdates];
                        }
                     completion:^(BOOL finished) {
    }];
}

しかし、あなたの場合、ユーザーがスクロールして別のセルを選択した場合、現在選択されているセルのreloadRowsAtIndexPaths:呼び出しを縮小および展開するには、最後に選択されたセルを使用する必要があるため、heightForRowAtIndexPath:それに応じて処理します。


0

リロードせずに(そしてキーボードフォーカスを失って)テーブルセルUITableViewで展開するカスタムサブクラスの私のコードは次UITextViewのとおりです。

- (void)textViewDidChange:(UITextView *)textView {
    CGFloat textHeight = [textView sizeThatFits:CGSizeMake(self.width, MAXFLOAT)].height;
    // Check, if text height changed
    if (self.previousTextHeight != textHeight && self.previousTextHeight > 0) {
        [self beginUpdates];

        // Calculate difference in height
        CGFloat difference = textHeight - self.previousTextHeight;

        // Update currently editing cell's height
        CGRect editingCellFrame = self.editingCell.frame;
        editingCellFrame.size.height += difference;
        self.editingCell.frame = editingCellFrame;

        // Update UITableView contentSize
        self.contentSize = CGSizeMake(self.contentSize.width, self.contentSize.height + difference);

        // Scroll to bottom if cell is at the end of the table
        if (self.editingNoteInEndOfTable) {
            self.contentOffset = CGPointMake(self.contentOffset.x, self.contentOffset.y + difference);
        } else {
            // Update all next to editing cells
            NSInteger editingCellIndex = [self.visibleCells indexOfObject:self.editingCell];
            for (NSInteger i = editingCellIndex; i < self.visibleCells.count; i++) {
                UITableViewCell *cell = self.visibleCells[i];
                CGRect cellFrame = cell.frame;
                cellFrame.origin.y += difference;
                cell.frame = cellFrame;
            }
        }
        [self endUpdates];
    }
    self.previousTextHeight = textHeight;
}

0

私は@Joyの素晴らしい答えを使用しましたが、iOS 8.4およびXCode 7.1.1で完全に機能しました。

セルを切り替え可能にする場合は、-tableViewDidSelectを次のように変更しました。

-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
//This is the bit I changed, so that if tapped once on the cell, 
//cell is expanded. If tapped again on the same cell, 
//cell is collapsed. 
    if (self.currentSelection==indexPath.row) {
        self.currentSelection = -1;
    }else{
        self.currentSelection = indexPath.row;
    }
        // animate
        [tableView beginUpdates];
        [tableView endUpdates];

}

これがお役に立てば幸いです。


0

iOS 7以降でこのメソッドを確認してください。

- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath{
    return UITableViewAutomaticDimension;
}

これはiOS 8で改善されました。テーブルビュー自体のプロパティとして設定できます。



0

入力-

tableView.beginUpdates()tableView.endUpdates() これらの関数は呼び出しません

func tableView(_ tableView:UITableView、cellForRowAt indexPath:IndexPath)-> UITableViewCell {}

ただし、その場合、 tableView.reloadRows(at:[selectedIndexPath!as IndexPath]、with:.none)

func tableView(_ tableView:UITableView、cellForRowAt indexPath:IndexPath)-> UITableViewCell {} この関数を呼び出し ます。


-1

私はちょっとしたハックでこの問題を解決しました:

static int s_CellHeight = 30;
static int s_CellHeightEditing = 60;

- (void)onTimer {
    cellHeight++;
    [tableView reloadData];
    if (cellHeight < s_CellHeightEditing)
        heightAnimationTimer = [[NSTimer scheduledTimerWithTimeInterval:0.001 target:self selector:@selector(onTimer) userInfo:nil repeats:NO] retain];
}

- (CGFloat)tableView:(UITableView *)_tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
        if (isInEdit) {
            return cellHeight;
        }
        cellHeight = s_CellHeight;
        return s_CellHeight;
}

セルの高さを拡張する必要がある場合は、設定isInEdit = YESしてメソッド[self onTimer]を呼び出し、s_CellHeightEditing値に達するまでセルの成長をアニメーション化します:-)


シミュレータではうまく機能しますが、iPhoneではハードウェアが遅れます。0.05タイマーの遅延とcellHeightの増加が5ユニットの場合、はるかに優れていますが、CoreAnimationのようなものはありません
Dzamir

1
投稿する前に確認してください。
ajay_nasa

-2

選択された行のインデックスパスを取得します。テーブルをリロードします。UITableViewDelegateのheightForRowAtIndexPathメソッドで、選択した行の高さを別の高さに設定し、その他の場合は通常の行の高さを返します


2
-1は機能しません。呼び出し[table reloadData]を行うと、アニメーション化するのではなく、即座に高さの変化が起こります。
マークアメリー2013
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.