ReloadItemsAtIndexPaths sonrası UICollectionView animasyonundan kaçının


91

UICollectionView, reloadItemsAtIndexPaths çağrıldıktan sonra öğelere animasyon uygulayın (fade animation).

Bu animasyondan kaçınmanın bir yolu var mı?

iOS 6

Yanıtlar:


231

İOS 7 ve üstünü hedefliyorsanız, yeni UIViewyöntemi kullanabileceğinizi belirtmek gerekir performWithoutAnimation:. Başlık altında bunun buradaki diğer cevaplarla aynı şeyi yaptığından şüpheleniyorum ( UIViewanimasyonları geçici olarak devre dışı bırakma / Temel Animasyon eylemleri), ancak sözdizimi güzel ve temiz.

Yani özellikle bu soru için ...

Amaç-C:

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


Swift:

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


Tabii bu ilke, bir değişiklik sağlamak istediğiniz her durum için uygulanabilir değildir animasyonlu.


3
Bu benim için iOS 7+ üzerinde kabul edilen cevaptan daha iyi çalıştı.
Philippe Sabourin

Yalnızca koleksiyon görünümü için değil tüm animasyonları devre dışı bırakır
user2159978

10
@ user2159978 Bu doğru; Buradaki tüm cevaplar UIView animasyonlarını geçici olarak devre dışı bırakın. Animasyonlar yalnızca iletilen bloğun içinden başlatılan eylemler için devre dışı bırakılır, bu durumda yalnızca koleksiyon görünümünün yeniden yüklenmesi. Sorulan soruya tamamen geçerli bir cevap, bu yüzden olumsuz bir oylamayı hak ettiğini düşünmüyorum.
Stuart

İnanılmaz çözüm. Bu yerine animateWithDuration Kullanılması: 0 önler hızlı ama görünür düzen aksaklık hiçbir itemSize ile kendini boyutlandırma toplama görünümü hücreleri kullanırken
Ethan Gill

1
Bu çözüm artık iOS 14'te çalışmıyor, bu bloğun içinde reloadData kullanıyorum, ancak iOS 13 ile işe yaradı
Maray97

161

Şunu da deneyebilirsiniz:

UICollectionView *collectionView;

...

[UIView setAnimationsEnabled:NO];

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

Düzenle:

Ayrıca performBatchUpdates, bir UIView animasyon bloğunu sararsanız, varsayılan animasyon yerine UIView animasyonunun kullanıldığını, böylece animasyon süresini 0 olarak ayarlayabileceğinizi buldum , örneğin:

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

Ekleme ve silme işlemleri sırasında iOS 7 yaylı animasyonlarını kullanmak istiyorsanız bu çok harika!


1
Teşekkür ederim. Bu, uicollectionview hücrelerine kronometre zamanlayıcıları eklemede çok yardımcı oldu.
hatunike

Parlak! Bunu, hücre eklendikten sonra collectionView'i yeni bir ofsete canlandırmak için insertCells ile ve klavye yukarıdayken kullandım.
David H

5
Peter'ın dediği gibi, bu diğer animasyonlara müdahale ediyor. Bunun yerine, tamamlama bloğunun dışında [UIView setAnimationsEnabled: YES] çağırmalısınız. Bu şekilde yalnızca o 1 animasyonu engellemiş olursunuz.
Adlai Holler

2
Tam olarak aynı şeyi yapan alternatif bir yöntem ekledim.
Sam

1
performBatchUpdatesİçini sarmak animateWithDurationharika! Bahşiş için teşekkürler!
Robert

19

UICollectionView, reloadItemsAtIndexPaths çağrıldıktan sonra öğelere animasyon uygulayın (fade animation).

Bu animasyondan kaçınmanın bir yolu var mı?

iOS 6

FlowLayout kullandığınızı varsayıyorum. Solma animasyonundan kurtulmaya çalıştığınız için şunu deneyin:

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
    }

}

Bu çok eski bir soru, bu yüzden muhtemelen artık iOS 6'yı hedeflemiyorsunuz. Ben şahsen tvOS 11 üzerinde çalışıyordum ve aynı soruyu sormuştum, yani bu, aynı problemle gelen herkes için burada.


1
Bu cevapta "Vay canına, bu gerçekten işe yaradı!" Birisi yorumları kaldırmaya karar verdi. Aslında, bunu başarmanın modern ve hile içermeyen yolu olduğunu düşünüyorum ve bu yorumlar bunun bir kabulüydü.
Matt Mc

8

Bunu yapmak için UICollectionView'da bir kategori yazdım . İşin püf noktası, yeniden yüklerken tüm animasyonları devre dışı bırakmaktır:

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

[self reloadItemsAtIndexPaths:indexPaths];

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

Ayrıca Apple'dan bunun herhangi bir animasyon yapmaması gerektiği yönünde yanıt aldım, eğer öyleyse bu bir hata. Yanlış bir şey mi yapıyorum yoksa bu bir hata mı bilmiyorum.
Marcin

2
Bunun CATransaction.setDisableActions(true)için bir kısaltma olarak da yapabilirsiniz .
CIFilter

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

bu hızlı 3 sözdizimi
Amjad Tubasi

bu iyi çalıştı, hatta UIView.performWithoutAnimation'dan daha iyi
Lance Samaria

5

İşte bir Swift 3 sürümü performBatchUpdatesanimasyonsuz bir UICollectionView. collectionView.reloadData()Kayıtlar eklendiğinde hücre değişimini azalttığı için bunun benim için daha iyi çalıştığını gördüm .

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

Sadece 0,02 doları eklemek için, seçilen cevabın her iki versiyonunu da denedim ve orijinal yöntem amaçlarım için daha iyi çalıştı. Bir kullanıcının belirli bir haftada takvime girmesine ve ardından ileri geri kaydırıp bir listeyi filtrelemek için tek tek günleri seçmesine olanak tanıyan sonsuz bir kaydırmalı takvim görünümü üzerinde çalışıyorum.

Benim uygulamamda, eski cihazlarda işlerin daha iyi performans göstermesini sağlamak için, takvim görünümünü temsil eden tarih dizisinin nispeten küçük tutulması gerekiyor, bu da kullanıcı 3. haftada ortada olacak şekilde yaklaşık 5 hafta değerinde tarih tutmak anlamına geliyor. İkinci yaklaşımı kullanmakla ilgili sorun, koleksiyon görünümünü bir animasyon olmadan tekrar ortaya kaydırmanız gereken ikinci bir adımdır, bu da engellenen temel animasyonla bir nedenden ötürü çok pürüzlü bir görünüm sağlar.

Kodum:

[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)
    }
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.