ระยะเวลาการเคลื่อนไหวของแถว UITableView และการโทรกลับที่เสร็จสมบูรณ์


100

มีวิธีระบุระยะเวลาสำหรับภาพเคลื่อนไหวแถว UITableView หรือรับการติดต่อกลับเมื่อภาพเคลื่อนไหวเสร็จสิ้นหรือไม่

สิ่งที่ฉันต้องการจะทำคือกะพริบตัวบ่งชี้การเลื่อนหลังจากภาพเคลื่อนไหวเสร็จสิ้น ทำแฟลชก่อนแล้วไม่ทำอะไร จนถึงตอนนี้วิธีแก้ปัญหาที่ฉันมีคือการหน่วงเวลาครึ่งวินาที (ซึ่งดูเหมือนจะเป็นระยะเวลาเริ่มต้นของภาพเคลื่อนไหว) นั่นคือ:

[self.tableView insertRowsAtIndexPaths:newRows
                      withRowAnimation:UITableViewRowAnimationFade];
[self.tableView performSelector:@selector(flashScrollIndicators)
                     withObject:nil
                     afterDelay:0.5];

ฉันไม่ได้ลองด้วยตัวเอง แต่อาจทำได้ด้วยการจัดการเส้นทางดัชนี:- (void)tableView:(UITableView *)tableView didEndDisplayingCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath*)indexPath
Kalle

คำตอบ:


3

ปัจจุบันหากคุณต้องการทำสิ่งนี้มีฟังก์ชั่นใหม่เริ่มจาก iOS 11 :

- (void)performBatchUpdates:(void (^)(void))updates 
                 completion:(void (^)(BOOL finished))completion;

ในการปิดการอัปเดตคุณวางรหัสเดียวกันกับในส่วน beginUpdates () / endUpdates และดำเนินการเสร็จสิ้นหลังจากภาพเคลื่อนไหวทั้งหมด


นี่มันเยี่ยมมาก ฉันไม่ได้สังเกตเห็นการเพิ่มนี้
Daniel Dickison

207

เพิ่งมาเจอเรื่องนี้ วิธีการทำมีดังนี้

วัตถุประสงค์ -C

[CATransaction begin];
[tableView beginUpdates];
[CATransaction setCompletionBlock: ^{
    // Code to be executed upon completion
}];
[tableView insertRowsAtIndexPaths: indexPaths
                 withRowAnimation: UITableViewRowAnimationAutomatic];
[tableView endUpdates];
[CATransaction commit];

รวดเร็ว

CATransaction.begin()
tableView.beginUpdates()
CATransaction.setCompletionBlock {
    // Code to be executed upon completion
}
tableView.insertRowsAtIndexPaths(indexArray, withRowAnimation: .Top)
tableView.endUpdates()
CATransaction.commit()

2
อีกครั้งทำงานได้อย่างไม่มีที่ติที่นี่ iOS6 และทั้งหมด นี่เป็นกลไกที่รองรับ SDK ที่เหมาะสมสำหรับการแทนที่คุณสมบัติในภาพเคลื่อนไหวเริ่มต้น บางทีคุณอาจมีภาพเคลื่อนไหวเพิ่มเติมที่ใช้งานได้นานกว่าในธุรกรรม CAT ของคุณ? พวกเขาซ้อนกันคุณก็รู้
karwag

1
ใช้งานได้ดีสำหรับฉันใน iOS6 ขอบคุณสำหรับสิ่งนั้น!
Aron

5
setAnimationDurationดูเหมือนจะไม่มีผลกับระยะเวลาการแทรก / ลบ iOS 6
Tom Redman

2
มีข้อเสนอแนะในการเปลี่ยนระยะเวลาอย่างไร CATransaction setAnimationDuration: ดูเหมือนจะไม่สร้างความแตกต่าง
Jeff Grimes

5
ทำงานได้ดีสำหรับฉันเช่นกันใน iOS 5.1.1, 6.1, 7.0; แต่ถ้าคุณต้องการรับ tableView.contentSize ใหม่หลังภาพเคลื่อนไหว (เหมือนในกรณีของฉัน) คุณต้องใช้ [self performSelectorOnMainThread: withObject: waitUntilDone:]; ใน setCompletionBlock เพื่อเรียกตัวแทนของคุณใน runloop ถัดไป หากคุณโทรหาผู้รับมอบสิทธิ์โดยตรงโดยไม่ใช้ performSelectorOnMainThread คุณจะได้รับค่าเก่าสำหรับ tableView.contentSize
slamor

38

การขยายคำตอบที่ดีของ karwagโปรดทราบว่าบน iOS 7 โดยรอบ CATransaction ด้วย UIView Animation ให้การควบคุมระยะเวลาการเคลื่อนไหวของตาราง

[UIView beginAnimations:@"myAnimationId" context:nil];

[UIView setAnimationDuration:10.0]; // Set duration here

[CATransaction begin];
[CATransaction setCompletionBlock:^{
    NSLog(@"Complete!");
}];

[myTable beginUpdates];
// my table changes
[myTable endUpdates];

[CATransaction commit];
[UIView commitAnimations];

ระยะเวลาของแอนิเมชั่น UIView ไม่มีผลกับ iOS 6 บางทีการใช้งานแอนิเมชั่นตาราง iOS 7 อาจแตกต่างกันในระดับ UIView


ดูเหมือนว่าระยะเวลาของภาพเคลื่อนไหวจะถูกละเว้น
Dustin

26

นั่นเป็นเคล็ดลับที่มีประโยชน์อย่างหนึ่ง! ฉันเขียนส่วนขยาย UITableView เพื่อหลีกเลี่ยงการเขียน CATransaction ตลอดเวลา

import UIKit

extension UITableView {

    /// Perform a series of method calls that insert, delete, or select rows and sections of the table view.
    /// This is equivalent to a beginUpdates() / endUpdates() sequence, 
    /// with a completion closure when the animation is finished.
    /// Parameter update: the update operation to perform on the tableView.
    /// Parameter completion: the completion closure to be executed when the animation is completed.

    func performUpdate(_ update: ()->Void, completion: (()->Void)?) {

        CATransaction.begin()
        CATransaction.setCompletionBlock(completion)

        // Table View update on row / section
        beginUpdates()
        update()
        endUpdates()

        CATransaction.commit()
    }

}

สิ่งนี้ใช้ดังนี้:

// Insert in the tableView the section we just added in sections
self.tableView.performUpdate({
            self.tableView.insertSections([newSectionIndex], with: UITableViewRowAnimation.top)

        }, completion: {
            // Scroll to next section
            let nextSectionIndexPath = IndexPath(row: 0, section: newSectionIndex)
            self.tableView.scrollToRow(at: nextSectionIndexPath, at: .top, animated: true)
        })

คำตอบสุดเจ๋ง! นี่เป็นหนึ่งในเหตุผลที่ฉันชอบ Swift
Gianni Carlo

@GianniCarlo คุณสามารถทำได้ใน ObjC เช่นกัน
CyberMew

@CyberMew ใช่ แต่การสร้างหมวดหมู่เป็นเรื่องที่เจ็บปวดมาตลอดโดยเฉพาะเนื่องจากชื่อยาวของไฟล์พิเศษ
Gianni Carlo

มันมีเฉพาะใน ios 11 เท่านั้นจะใช้งานใน ios 10 ได้อย่างไร?
kemdo

@kemdo ทำไมถึงบอกว่าใช้ได้เฉพาะใน iOS 11? ทุกอย่างที่นี่คือ iOS 2+ ยกเว้นsetCompletionBlockiOS 4+
Frédéric Adda

25

การย่อคำตอบที่ดีของ Brent ให้สั้นลงสำหรับ iOS 7 เป็นอย่างน้อยคุณสามารถรวมสิ่งนี้ได้ทั้งหมดในการโทร [UIView animateWithDuration: delay: options: animations: complete:]:

[UIView animateWithDuration:10 delay:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
  [self.tableView beginUpdates];
  [self.tableView endUpdates];
} completion:^(BOOL finished) {
  // completion code
}];

แม้ว่าฉันจะไม่สามารถแทนที่เส้นโค้งภาพเคลื่อนไหวเริ่มต้นจากสิ่งอื่นใดนอกจาก EaseInOut


2
เมื่อทำการแทรกแถวด้วยวิธีนี้หรือวิธีของ @ Brent แม้ว่าระยะเวลาจะได้รับการเคารพ แต่ UITableViewRowAnimation ดูเหมือนจะไม่ได้รับการเคารพและดูเหมือนจะเคลื่อนไหวจากบนลงล่างเสมอแม้ว่าฉันจะระบุเช่น UITableViewRowAnimationLeft การทดสอบบน iOS 8.4 - ใครมีวิธีแก้ปัญหา?
Danny


7

สำหรับฉันฉันต้องการสิ่งนี้สำหรับ collectionView ฉันได้สร้างส่วนขยายง่ายๆเพื่อแก้ปัญหานี้:

extension UICollectionView {

    func reloadSections(sections: NSIndexSet, completion: () -> Void){
        CATransaction.begin()
        CATransaction.setCompletionBlock(completion)

        self.reloadSections(sections)

        CATransaction.commit()
    }

}

1

เนื่องจากperformBatchวิธีของ tableView มีให้ใช้งานตั้งแต่iOS 11เท่านั้นคุณสามารถใช้ส่วนขยายต่อไปนี้:

extension UITableView {
func performUpdates(_ updates: @escaping () -> Void, completion: @escaping (Bool) -> Void) {
        if #available(iOS 11.0, *) {
            self.performBatchUpdates({
                updates()
            }, completion: completion)
        } else {
            CATransaction.begin()
            beginUpdates()
            CATransaction.setCompletionBlock {
                completion(true)
            }
            updates()
            endUpdates()
            CATransaction.commit()
        }
    }
}

0

คำตอบของ Antoineค่อนข้างดี - แต่สำหรับ UICollectionView นี่คือสำหรับ UITableView:

extension UITableView {
    func reloadSections(_ sections: IndexSet, with rowAnimation: RowAnimation, completion: (() -> Void)?) {
        CATransaction.begin()
        CATransaction.setCompletionBlock(completion)
        
        self.reloadSections(sections, with: rowAnimation)
        
        CATransaction.commit()
    }
}

เรียกเช่นนั้น:

tableView.reloadSections(IndexSet(0), with: .none, completion: {
    // Do the end of animation thing        
})

-8

คุณสามารถลองใส่ insertRowsAtIndexPath ในไฟล์

- (void)beginUpdates
- (void)endUpdates

ทำรายการแล้วทำการแฟลชในภายหลัง


ดูคำตอบของ karwag ด้านบน คุณต้องแก้ปัญหาของสิ่งที่นับเป็น "หลังจากนั้น"
JLundell
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.