web-dev-qa-db-ja.com

UITableView beginUpdates / endUpdatesでアニメーションが終了したことを検出する方法は?

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

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

114
pixelfreak

これはどうですか?

[CATransaction begin];

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

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

[CATransaction commit];

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

288

迅速なバージョン


CATransaction.begin()

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

tableView.beginUpdates()
tableView.endUpdates()

CATransaction.commit()
27
Michael

次のように、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/63494 へのクレジット。

5
Zdenek

UITableViewは追加または削除された行に合わせてコンテンツサイズを調整するため、可能な解決策は、endUpdatesを呼び出したUITableViewを継承し、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;
    }
}
4
Antoni

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

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

まだ良い解決策が見つかりませんでした(UITableViewのサブクラス化が短い)。今のところperformSelector:withObject:afterDelay:を使用することにしました。理想的ではありませんが、仕事を成し遂げます。

[〜#〜] update [〜#〜]:この目的でscrollViewDidEndScrollingAnimation:を使用できるようです(これは私の実装に固有です、コメントを参照してください)。

1
pixelfreak

次のように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];
}

ただし、これは、既にテーブルにあるセルが画面外から画面上に移動したときにも呼び出されるため、探しているものとは異なる場合があります。すべてのUITableViewおよびUIScrollViewデリゲートメソッドを調べたところ、セルがアニメーションを挿入した直後に処理するものは何もありません。


アニメーションが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];
    }
}
0
chown