หลีกเลี่ยงภาพเคลื่อนไหวของ UICollectionView หลังจากรีโหลดItemsAtIndexPaths


92

UICollectionView ทำให้รายการเคลื่อนไหวหลังจากรีโหลดItemsAtIndexPathsเรียกว่า (ภาพเคลื่อนไหวจาง)

มีวิธีหลีกเลี่ยงภาพเคลื่อนไหวนี้หรือไม่?

iOS 6

คำตอบ:


234

เป็นที่น่าสังเกตว่าหากคุณกำหนดเป้าหมายเป็น iOS 7 ขึ้นไปคุณสามารถใช้UIViewวิธีการใหม่นี้performWithoutAnimation:ได้ ฉันสงสัยว่าภายใต้ประทุนนี้ทำเช่นเดียวกับคำตอบอื่น ๆ ที่นี่ (ปิดใช้งานUIViewแอนิเมชั่นชั่วคราว/ แอ็คชั่น Core Animation) แต่ไวยากรณ์นั้นดีและสะอาด

ดังนั้นสำหรับคำถามนี้โดยเฉพาะ ...

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

[UIView performWithoutAnimation:^{
    [self.collectionView reloadItemsAtIndexPaths:indexPaths];
}];


รวดเร็ว:

UIView.performWithoutAnimation {
    self.collectionView.reloadItemsAtIndexPaths(indexPaths)
}


แน่นอนหลักการนี้สามารถใช้ได้กับทุกสถานการณ์ที่คุณต้องการเพื่อให้แน่ใจว่าการเปลี่ยนแปลงจะไม่เคลื่อนไหว


3
สิ่งนี้ได้ผลดีกว่าคำตอบที่ยอมรับสำหรับฉันบน iOS 7+
Philippe Sabourin

ปิดการใช้งานภาพเคลื่อนไหวทั้งหมดไม่เพียง แต่สำหรับมุมมองคอลเลกชันเท่านั้น
user2159978

10
@ user2159978 ถูกต้อง; คำตอบทั้งหมดที่นี่ปิดใช้งานภาพเคลื่อนไหว UIView ชั่วคราว ภาพเคลื่อนไหวจะถูกปิดใช้งานสำหรับการดำเนินการที่เริ่มต้นจากภายในบล็อกที่ส่งผ่านเท่านั้นในกรณีนี้จะเป็นการรีโหลดของมุมมองคอลเล็กชัน มันเป็นคำตอบที่ถูกต้องอย่างสมบูรณ์สำหรับคำถามที่ถูกถามดังนั้นฉันไม่คิดว่ามันสมควรได้รับการโหวตลดลง
Stuart

โซลูชันที่น่าทึ่ง การใช้สิ่งนี้แทน animateWithDuration: 0 ป้องกันความผิดพลาดของเลย์เอาต์ที่รวดเร็ว แต่มองเห็นได้เมื่อใช้เซลล์มุมมองคอลเลกชันที่ปรับขนาดด้วยตนเองโดยไม่มี itemSize
Ethan Gill

1
โซลูชันนี้ใช้ไม่ได้อีกต่อไปบน iOS 14 ภายในบล็อกนี้ฉันใช้ reloadData แต่กับ iOS 13 มันใช้งานได้
Maray97

161

คุณสามารถลองสิ่งนี้:

UICollectionView *collectionView;

...

[UIView setAnimationsEnabled:NO];

[collectionView performBatchUpdates:^{
    [collectionView reloadItemsAtIndexPaths:indexPaths];
} completion:^(BOOL finished) {
    [UIView setAnimationsEnabled:YES];
}];

แก้ไข:

ฉันยังพบว่าหากคุณรวมperformBatchUpdatesบล็อกแอนิเมชั่น UIView แอนิเมชั่น UIView จะถูกใช้แทนภาพเคลื่อนไหวเริ่มต้นดังนั้นคุณสามารถตั้งค่าระยะเวลาการเคลื่อนไหวเป็น 0 ได้ดังนี้

[UIView animateWithDuration:0 animations:^{
    [collectionView performBatchUpdates:^{
        [collectionView reloadItemsAtIndexPaths:indexPaths];
    } completion:nil];
}];

นี่เป็นสิ่งที่ยอดเยี่ยมมากหากคุณต้องการใช้แอนิเมชั่นสปริงของ iOS 7 ระหว่างการแทรกและลบ!


1
ขอขอบคุณ. สิ่งนี้มีประโยชน์มากในการเพิ่มตัวจับเวลาของนาฬิกาจับเวลาให้กับเซลล์ uicollectionview
hatunike

ยอดเยี่ยม! ฉันใช้สิ่งนี้กับ insertCells และเมื่อใช้แป้นพิมพ์ขึ้นเพื่อทำให้ collectionView เคลื่อนไหวเป็นออฟเซ็ตใหม่หลังจากใส่เซลล์แล้ว
David H

5
ดังที่ปีเตอร์กล่าวว่าสิ่งนี้รบกวนภาพเคลื่อนไหวอื่น ๆ แต่คุณควรเรียกใช้ [UIView setAnimationsEnabled: YES] ที่อยู่นอกบล็อกการทำให้เสร็จสมบูรณ์ วิธีนี้จะช่วยป้องกันภาพเคลื่อนไหว 1 ภาพเท่านั้น
Adlai Holler

2
ฉันเพิ่มวิธีอื่นซึ่งก็เหมือนกันทุกประการ
แซม

1
ห่อperformBatchUpdatesข้างในanimateWithDurationก็แจ่ม! ขอบคุณสำหรับทิป!
Robert

20

UICollectionView ทำให้รายการเคลื่อนไหวหลังจากรีโหลดItemsAtIndexPathsเรียกว่า (ภาพเคลื่อนไหวจาง)

มีวิธีหลีกเลี่ยงภาพเคลื่อนไหวนี้หรือไม่?

iOS 6

ฉันถือว่าคุณกำลังใช้ FlowLayout เนื่องจากคุณกำลังพยายามกำจัดภาพเคลื่อนไหวที่จางลงให้ลองทำดังนี้:

import UIKit

class NoFadeFlowLayout: UICollectionViewFlowLayout {

    override func initialLayoutAttributesForAppearingItem(at itemIndexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
        let attrs = super.initialLayoutAttributesForAppearingItem(at: itemIndexPath)
        attrs?.alpha = 1.0
        return attrs
    }

    override func finalLayoutAttributesForDisappearingItem(at itemIndexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
        let attrs = super.finalLayoutAttributesForDisappearingItem(at: itemIndexPath)
        attrs?.alpha = 1.0
        return attrs
    }

}

นี่เป็นคำถามเก่ามากดังนั้นคุณอาจไม่ได้กำหนดเป้าหมายเป็น iOS 6 อีกต่อไป ฉันทำงานกับ tvOS 11 เป็นการส่วนตัวและมีคำถามเดียวกันดังนั้นนี่จึงเหมาะสำหรับทุกคนที่พบปัญหาเดียวกัน


1
มีความคิดเห็น 3 หรือ 4 ความคิดเห็นเกี่ยวกับผลของ "ว้าวมันใช้งานได้ดีจริงๆ!" มีคนตัดสินใจลบความคิดเห็นดังกล่าว ฉันคิดว่าอันที่จริงแล้วนี่เป็นวิธีที่ทันสมัยและไม่ใช่แฮ็กกี้ในการทำสิ่งนี้ให้สำเร็จและความคิดเห็นเหล่านั้นก็เป็นการรับรู้สิ่งนี้
Matt Mc

8

ฉันเขียนหมวดหมู่บน UICollectionViewเพื่อทำสิ่งนั้น เคล็ดลับคือปิดการใช้งานภาพเคลื่อนไหวทั้งหมดในขณะที่โหลดซ้ำ:

if (!animated) {
    [CATransaction begin];
    [CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions];
}

[self reloadItemsAtIndexPaths:indexPaths];

if (!animated) {
    [CATransaction commit];
}

ฉันยังได้รับคำตอบจาก Apple ว่าสิ่งนี้ไม่ควรทำแอนิเมชั่นใด ๆ หากเป็นข้อบกพร่อง ฉันไม่รู้ว่าฉันทำอะไรผิดหรือเป็นบั๊ก
Marcin

2
คุณสามารถCATransaction.setDisableActions(true)จดชวเลขได้เช่นกัน
CIFilter

5
extension UICollectionView {
    func reloadWithoutAnimation(){
        CATransaction.begin()
        CATransaction.setValue(kCFBooleanTrue, forKey: kCATransactionDisableActions)
        self.reloadData()
        CATransaction.commit()
    }
}

นี่คือไวยากรณ์ 3 ที่รวดเร็ว
Amjad Tubasi

สิ่งนี้ใช้ได้ดียิ่งกว่า UIView.performWithoutAnimation
Lance Samaria

5

นี่คือเวอร์ชัน Swift 3 ที่performBatchUpdatesไม่มีภาพเคลื่อนไหวเป็นUICollectionViewไฟล์. ฉันพบว่าสิ่งนี้ทำงานได้ดีสำหรับฉันมากกว่าcollectionView.reloadData()เพราะมันลดการสลับเซลล์เมื่อใส่ระเบียน

func appendCollectionView(numberOfItems count: Int){

        // calculate indexes for the items to be added
        let firstIndex = dataItems.count - count
        let lastIndex = dataItems.count - 1

        var indexPaths = [IndexPath]()
        for index in firstIndex...lastIndex {
            let indexPath = IndexPath(item: index, section: 0)
            indexPaths.append(indexPath)
        }

   UIView.performWithoutAnimation {

        self.collectionView.performBatchUpdates({ () -> Void in
            self.collectionView.insertItems(at: indexPaths)
        }, completion: { (finished) -> Void in

        })
    }
}

2
- (void)reloadCollectionViewAnimated:(BOOL)animated  {

    if (animated) {
        [self.collectionView performBatchUpdates:^{
            [self.collectionView reloadSections:[NSIndexSet indexSetWithIndex:0]];
        } completion:^(BOOL finished) {

        }];
    } else {
        [CATransaction begin];
        [CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions];
        [self.collectionView reloadSections:[NSIndexSet indexSetWithIndex:0]];
        [CATransaction commit];
    }

}

1

เพียงแค่เพิ่ม 0.02 ดอลลาร์ของฉันฉันได้ลองใช้คำตอบที่เลือกทั้งสองเวอร์ชันและวิธีเดิมใช้ได้ดีกว่าสำหรับวัตถุประสงค์ของฉัน ฉันกำลังทำงานกับมุมมองปฏิทินแบบเลื่อนไม่สิ้นสุดซึ่งอนุญาตให้ผู้ใช้เข้าสู่ปฏิทินในสัปดาห์ที่กำหนดจากนั้นปัดไปมาและเลือกวันละรายการเพื่อกรองรายการ

ในการใช้งานของฉันเพื่อให้สิ่งต่างๆมีประสิทธิภาพบนอุปกรณ์รุ่นเก่าอาร์เรย์ของวันที่ที่แสดงถึงมุมมองปฏิทินจะต้องมีขนาดค่อนข้างเล็กซึ่งหมายถึงการถือวันที่ประมาณ 5 สัปดาห์โดยที่ผู้ใช้อยู่ตรงกลางในสัปดาห์ที่ 3 ปัญหาในการใช้แนวทางที่สองคือมีขั้นตอนที่สองที่คุณต้องเลื่อนมุมมองคอลเลกชันกลับไปตรงกลางโดยไม่มีภาพเคลื่อนไหวซึ่งทำให้มีลักษณะขรุขระมากด้วยเหตุผลบางประการกับภาพเคลื่อนไหวพื้นฐานที่ถูกบล็อก

รหัสของฉัน:

[UIView setAnimationsEnabled:NO];
[self.collectionView performBatchUpdates:^{
    [self.collectionView deleteItemsAtIndexPaths:indexPathDeleteArray];
    [self.collectionView insertItemsAtIndexPaths:indexPathAddArray];

} completion:NULL];
[UIView setAnimationsEnabled:YES];

NSIndexPath *newIndexPath = [NSIndexPath indexPathForItem:14 inSection:0];
[self.collectionView scrollToItemAtIndexPath:newIndexPath atScrollPosition:UICollectionViewScrollPositionLeft animated:NO];

0
 func reloadRowsWithoutAnimation(at indexPaths: [IndexPath]) {
        let contentOffset = collectionView.contentOffset
        UIView.setAnimationsEnabled(false)
        collectionView.performBatchUpdates {
            collectionView.reloadItems(at: indexPaths)
        }
        UIView.setAnimationsEnabled(true)
        collectionView.setContentOffset(contentOffset, animated: false)
    }
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.