ios swift - 在reloadItemsAtIndexPaths之後避免使用UICollectionView的動畫





animation performbatchupdates (8)


調用reloadItemsAtIndexPaths後,UICollectionView動畫項目(淡入淡出動畫)。

有沒有辦法避免這個動畫?

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並且遇到了同樣的問題,所以對於那些遇到同樣問題的人來說,這裡也是如此。

調用reloadItemsAtIndexPaths後,UICollectionView動畫項目(淡入淡出動畫)。

有沒有辦法避免這個動畫?

iOS 6




值得注意的是,如果你的目標是iOS 7及更高版本,你可以使用新的UIView方法performWithoutAnimation: . 我懷疑在幕後這與其他答案(暫時禁用UIView動畫/核心動畫動作)的作用大致相同,但語法很好而且乾淨。

所以對於這個問題尤其......

Objective-C的:

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


迅速:

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


當然,這個原則可以應用於您希望確保更改不是動畫的任何情況。




只是為了增加我的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];



- (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];
    }

}



這是一個Swift 3版本,可以在沒有動畫的情況下執行UICollectionViewUICollectionView 。 我發現這對我來說比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

        })
    }
}



在UICollectionView上寫了一個類別來做到這一點。 訣竅是在重新加載時禁用所有動畫:

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

[self reloadItemsAtIndexPaths:indexPaths];

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



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



這是一個方便的幫手,可以防止一次又一次地發出煩人的GCD呼叫

public func delay(bySeconds seconds: Double, dispatchLevel: DispatchLevel = .main, closure: @escaping () -> Void) {
    let dispatchTime = DispatchTime.now() + seconds
    dispatchLevel.dispatchQueue.asyncAfter(deadline: dispatchTime, execute: closure)
}

public enum DispatchLevel {
    case main, userInteractive, userInitiated, utility, background
    var dispatchQueue: DispatchQueue {
        switch self {
        case .main:                 return DispatchQueue.main
        case .userInteractive:      return DispatchQueue.global(qos: .userInteractive)
        case .userInitiated:        return DispatchQueue.global(qos: .userInitiated)
        case .utility:              return DispatchQueue.global(qos: .utility)
        case .background:           return DispatchQueue.global(qos: .background)
        }
    }
}

現在,您只需像這樣延遲主線程上的代碼

delay(bySeconds: 1.5) { 
    // delayed code
}

如果你想延遲你的代碼到不同的線程

delay(bySeconds: 1.5, dispatchLevel: .background) { 
    // delayed code that will run on background thread
}

如果你更喜歡一個框架 ,也有一些更方便的功能,然後檢查HandySwift 。 您可以通過Carthage將其添加到您的項目中然後像上面的示例中那樣使用它:

import HandySwift    

delay(bySeconds: 1.5) { 
    // delayed code
}




ios animation ios6 uiview uicollectionview