UITableView beginUpdates / endUpdatesでアニメーションが終了したことを検出するにはどうすればよいですか?


116

insertRowsAtIndexPaths/deleteRowsAtIndexPathswrapped in を使用してテーブルセルを挿入/削除していますbeginUpdates/endUpdatesbeginUpdates/endUpdatesrowHeightを調整するときにも使用しています。これらの操作はすべて、デフォルトでアニメーション化されています。

使用中にアニメーションが終了したことをどのように検出できbeginUpdates/endUpdatesますか?


1
参考:これは同様に適用さ-scrollToRowAtIndexPath:atScrollPosition:animated:れます。
ベンラックマン、2014

回答:


292

これはどうですか?

[CATransaction begin];

[CATransaction setCompletionBlock:^{
    // animation has finished
}];

[tableView beginUpdates];
// do some work
[tableView endUpdates];

[CATransaction commit];

これは、tableView CALayerアニメーションが内部でアニメーションを使用するため機能します。つまり、開いているにアニメーションを追加しますCATransaction。オープンCATransactionが存在しない場合(通常の場合)、1つが暗黙的に開始され、現在の実行ループの終わりに終了します。しかし、ここで行うように、自分で始めた場合は、それを使用します。


7
私のために働いていませんでした。アニメーションの実行中に完了ブロックが呼び出されます。
mrvincenzo 2013

2
@MrVincenzoは、あなたが設定しなかったcompletionBlockbeginUpdatesendUpdatesスニペットのように?
ルドルフアダムコビッチ

2
[CATransaction commit]前ではなく後に呼び出す必要があります[tableView endUpdates]
ルドルフアダムコビッチ2013

6
セルにアニメーション付きのビュー、つまりUIActivityIndi​​catorViewが含まれている場合、完了ブロックは呼び出されません。
markturnip 2014年

3
結局のところ、私はこれを知りませんでした。純金 !!tableView.reloadData()の必要な悪によって素敵なアニメーションが殺されるのを見ることよりも悪いことはありません。
DogCoffee 2015

31

Swiftバージョン


CATransaction.begin()

CATransaction.setCompletionBlock({
    do.something()
})

tableView.beginUpdates()
tableView.endUpdates()

CATransaction.commit()

6

iOS 11以降をターゲットにしている場合は、UITableView.performBatchUpdates(_:completion:)代わりに次を使用する必要があります。

tableView.performBatchUpdates({
    // delete some cells
    // insert some cells
}, completion: { finished in
    // animation complete
})

5

考えられる解決策は、UITableViewがコンテンツサイズを調整して追加または削除された行に一致するため、呼び出し元のUITableViewから継承してそれをendUpdates上書きすることですsetContentSizeMethod。このアプローチはでも機能するはずですreloadData

endUpdatesが呼び出された後にのみ通知が送信されるようにするために、上書きendUpdatesしてフラグを設定することもできます。

// somewhere in header
@private BOOL endUpdatesWasCalled_;

-------------------

// in implementation file

- (void)endUpdates {
    [super endUpdates];
    endUpdatesWasCalled_ = YES;
}

- (void)setContentSize:(CGSize)contentSize {
    [super setContentSize:contentSize];

    if (endUpdatesWasCalled_) {
        [self notifyEndUpdatesFinished];
        endUpdatesWasCalled_ = NO;
    }
}

5

次のように、UIViewアニメーションブロックで操作を囲むことができます。

- (void)tableView:(UITableView *)tableView performOperation:(void(^)())operation completion:(void(^)(BOOL finished))completion
{
    [UIView animateWithDuration:0.0 animations:^{

        [tableView beginUpdates];
        if (operation)
            operation();
        [tableView endUpdates];

    } completion:^(BOOL finished) {

        if (completion)
            completion(finished);
    }];
}

https://stackoverflow.com/a/12905114/634940へのクレジット。


4
このコードは私にはうまくいきませんでした。完了ブロックが呼び出された後endUpdates、アニメーションが完了する前に呼び出されました。
ティムキャンバー2013

1
これは機能しません。このサンプルを適用すると、テーブルビューアニメーションが機能しなくなりました。
Primoz990 2015

長い(0.3秒のような)時間を作ってみましょう-それが動作するはずです(それは私のために働いていた)
EMEM

1

まだ良い解決策を見つけていません(UITableViewのサブクラス化を除く)。とりあえず使うことにしましたperformSelector:withObject:afterDelay:。理想的ではありませんが、仕事を成し遂げます。

更新:私はscrollViewDidEndScrollingAnimation:この目的に使用できるように見えます(これは私の実装に固有です、コメントを参照してください)。


1
scrollViewDidEndScrollingAnimationsetContentOffsetand scrollRectToVisible
samvermette

@samvermetteまあ、私の場合、beginUpdates / endUpdatesが呼び出されると、UITableViewは常にアニメーションスクロールします。しかし、これは私の実装に固有なので、最善の答えではないが私にとってはうまくいくことに同意します。
pixelfreak 2012年

更新をUIViewアニメーションブロックに含めることができるようです。以下の私の回答をご覧ください:stackoverflow.com/a/14305039/634940
Zdenek

0

次のtableView:willDisplayCell:forRowAtIndexPath:ように使用できます:

- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
    NSLog(@"tableView willDisplay Cell");
    cell.backgroundColor = [UIColor colorWithWhite:((indexPath.row % 2) ? 0.25 : 0) alpha:0.70];
}

しかし、これは、すでにテーブルにあるセルが画面外から画面上に移動したときにも呼び出されるため、探しているものとは正確に一致しない可能性があります。すべてのUITableViewUIScrollViewdelegateメソッドを調べたところ、アニメーションが挿入された直後に何も処理するようには見えません。


の後にアニメーションが終了したときに呼び出されるメソッドを呼び出すだけではendUpdatesどうですか。

- (void)setDownloadedImage:(NSMutableDictionary *)d {
    NSIndexPath *indexPath = (NSIndexPath *)[d objectForKey:@"IndexPath"];
    [indexPathDelayed addObject:indexPath];
    if (!([table isDragging] || [table isDecelerating])) {
        [table beginUpdates];
        [table insertRowsAtIndexPaths:indexPathDelayed withRowAnimation:UITableViewRowAnimationFade];
        [table endUpdates];
        // --> Call Method Here <--
        loadingView.hidden = YES;
        [indexPathDelayed removeAllObjects];
    }
}

ありがとう、chown。うまくいくとwillDisplayCellは思わないでください。すべてが落ち着いた後にのみ視覚的な更新を行う必要があるので、アニメーションの後でそれを行う必要があります。
pixelfreak 2011年

np @pixelfreak。これを行う他の方法を考えた場合は、お知らせします。
chown
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.