จะตรวจจับภาพเคลื่อนไหวนั้นได้สิ้นสุดลงใน UITableView beginUpdates / endUpdates ได้อย่างไร


116

ฉันกำลังแทรก / ลบเซลล์ตารางโดยใช้การinsertRowsAtIndexPaths/deleteRowsAtIndexPathsห่อในbeginUpdates/endUpdates. ฉันยังใช้beginUpdates/endUpdatesเมื่อปรับ rowHeight การดำเนินการทั้งหมดนี้เป็นภาพเคลื่อนไหวโดยค่าเริ่มต้น

ฉันจะสามารถตรวจพบว่าภาพเคลื่อนไหวได้สิ้นสุดลงเมื่อใช้beginUpdates/endUpdates?


1
FYI: สิ่งนี้ใช้ได้-scrollToRowAtIndexPath:atScrollPosition:animated:เช่นกัน
Ben Lachman

คำตอบ:


292

อะไรประมาณนี้

[CATransaction begin];

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

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

[CATransaction commit];

สิ่งนี้ได้ผลเนื่องจากภาพเคลื่อนไหว tableView ใช้CALayerภาพเคลื่อนไหวภายใน CATransactionนั่นคือพวกเขาเพิ่มภาพเคลื่อนไหวที่จะเปิดให้บริการใด ๆ หากไม่มีการเปิดCATransactionอยู่ (กรณีปกติ) จะมีการเริ่มต้นโดยปริยายซึ่งจะสิ้นสุดที่จุดสิ้นสุดของ runloop ปัจจุบัน แต่ถ้าคุณเริ่มด้วยตัวเองเช่นเสร็จที่นี่ก็จะใช้อันนั้น


7
ไม่ได้ผลสำหรับฉัน บล็อกการเสร็จสมบูรณ์ถูกเรียกในขณะที่ภาพเคลื่อนไหวยังคงทำงานอยู่
mrvincenzo

2
@MrVincenzo คุณตั้งค่าcompletionBlockก่อนหน้านี้beginUpdatesและendUpdatesชอบในตัวอย่างหรือไม่?
Rudolf Adamkovič

2
[CATransaction commit][tableView endUpdates]ควรจะเรียกว่าหลังจากที่ไม่ได้ก่อน
Rudolf Adamkovič

6
จะไม่เรียกบล็อกการเสร็จสิ้นหากเซลล์มีมุมมองที่มีภาพเคลื่อนไหวเช่น UIActivityIndicatorView
markturnip

3
ตลอดเวลาที่ผ่านมาฉันไม่เคยรู้เรื่องนี้เลย ทองคำบริสุทธิ์ !! ไม่มีอะไรเลวร้ายไปกว่าการได้เห็นแอนิเมชั่นดีๆที่ถูกฆ่าด้วยความชั่วร้ายที่จำเป็นของ tableView.reloadData ()
DogCoffee


6

หากคุณกำหนดเป้าหมายเป็น iOS 11 ขึ้นไปคุณควรใช้UITableView.performBatchUpdates(_:completion:)แทน:

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

5

วิธีแก้ไขที่เป็นไปได้คือการสืบทอดจาก UITableView ที่คุณเรียกใช้endUpdatesและเขียนทับsetContentSizeMethodเนื่องจาก UITableView จะปรับขนาดเนื้อหาให้ตรงกับแถวที่เพิ่มหรือลบออก วิธีนี้ควรใช้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แต่ก่อนที่ภาพเคลื่อนไหวจะเสร็จสมบูรณ์
Tim Camber

1
วิธีนี้ใช้ไม่ได้ ภาพเคลื่อนไหว Tableview หยุดทำงานเมื่อใช้ตัวอย่างนี้
Primoz990

ลองทำให้ระยะเวลานานขึ้น (เช่น 0.3 วินาที) - ควรใช้งานได้ (ได้ผลสำหรับฉัน)
emem

1

ยังไม่พบวิธีแก้ปัญหาที่ดี (ย่อมาจาก UITableView ย่อย) ฉันตัดสินใจที่จะใช้performSelector:withObject:afterDelay:ตอนนี้ ไม่เหมาะ แต่ได้งานทำ

อัปเดต : ดูเหมือนว่าฉันสามารถใช้scrollViewDidEndScrollingAnimation:เพื่อจุดประสงค์นี้ได้ (ใช้เฉพาะกับการใช้งานของฉันโปรดดูความคิดเห็น)


1
scrollViewDidEndScrollingAnimationถูกเรียกเพื่อตอบสนองsetContentOffsetและscrollRectToVisible
samvermette

@samvermette ในกรณีของฉันเมื่อมีการเรียก beginUpdates / endUpdates UITableView จะเลื่อนแบบเคลื่อนไหวเสมอ แต่นี่เป็นเรื่องเฉพาะสำหรับการนำไปใช้งานของฉันดังนั้นฉันจึงยอมรับว่ามันไม่ใช่คำตอบที่ดีที่สุด แต่ก็เหมาะกับฉัน
pixelfreak

ดูเหมือนจะเป็นไปได้ที่จะรวมการอัปเดตลงในบล็อกภาพเคลื่อนไหว 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];
}

แต่สิ่งนี้จะถูกเรียกเมื่อเซลล์ที่มีอยู่แล้วในตารางย้ายจากนอกหน้าจอไปยังบนหน้าจอดังนั้นอาจไม่ใช่สิ่งที่คุณกำลังมองหา ฉันเพิ่งดูวิธีการ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];
    }
}

ขอบคุณ chown อย่าคิดว่าwillDisplayCellจะได้ผล ฉันต้องการให้มันเกิดขึ้นหลังจากภาพเคลื่อนไหวเพราะฉันต้องการอัปเดตภาพหลังจากที่ทุกอย่างเข้าที่แล้ว
pixelfreak

np @pixelfreak ถ้าฉันคิดวิธีอื่นในการทำเช่นนี้ฉันจะแจ้งให้คุณทราบ
chown
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.