我如何知道UICollectionView已完全加载?

时间:2012-12-24 10:23:47

标签: uicollectionview delegates reload

每当完全加载UICollectionView时,我必须做一些操作,即在那时应该调用所有UICollectionView的数据源/布局方法。我怎么知道?是否有任何委托方法来了解UICollectionView加载状态?

21 个答案:

答案 0 :(得分:120)

这对我有用:

[self.collectionView reloadData];
[self.collectionView performBatchUpdates:^{}
                              completion:^(BOOL finished) {
                                  /// collection-view finished reload
                              }];

Swift 4语法:

self.collectionView.reloadData()
self.collectionView.performBatchUpdates(nil, completion: {
    (result) in
    // ready
})

答案 1 :(得分:59)

// In viewDidLoad
[self.collectionView addObserver:self forKeyPath:@"contentSize" options:NSKeyValueObservingOptionOld context:NULL];

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary  *)change context:(void *)context
{
    // You will get here when the reloadData finished 
}

- (void)dealloc
{
    [self.collectionView removeObserver:self forKeyPath:@"contentSize" context:NULL];
}

答案 2 :(得分:24)

实际上非常简单。

例如,当你调用UICollectionView的reloadData方法或它的layout的invalidateLayout方法时,你会执行以下操作:

dispatch_async(dispatch_get_main_queue(), ^{
    [self.collectionView reloadData];
});

dispatch_async(dispatch_get_main_queue(), ^{
    //your stuff happens here
    //after the reloadData/invalidateLayout finishes executing
});

为什么会这样:

主线程(我们应该进行所有UI更新的地方)包含主队列,它本质上是串行的,即它以FIFO方式工作。所以在上面的例子中,第一个块被调用,它调用了我们的reloadData方法,然后是第二个块中的任何其他方法。

现在主线程也在阻塞。因此,如果您reloadData需要3秒执行,那么第二个块的处理将被这些3推迟。

答案 3 :(得分:10)

只需添加一个很棒的@dezinezync答案:

Swift 3 +

collectionView.collectionViewLayout.invalidateLayout() // or reloadData()
DispatchQueue.main.async {
    // your stuff here executing after collectionView has been layouted
}

答案 4 :(得分:5)

这样做:

       UIView.animateWithDuration(0.0, animations: { [weak self] in
                guard let strongSelf = self else { return }

                strongSelf.collectionView.reloadData()

            }, completion: { [weak self] (finished) in
                guard let strongSelf = self else { return }

                // Do whatever is needed, reload is finished here
                // e.g. scrollToItemAtIndexPath
                let newIndexPath = NSIndexPath(forItem: 1, inSection: 0)
                strongSelf.collectionView.scrollToItemAtIndexPath(newIndexPath, atScrollPosition: UICollectionViewScrollPosition.Left, animated: false)
        })

答案 5 :(得分:2)

使用RxSwift / RxCocoa的另一种方法:

        collectionView.rx.observe(CGSize.self, "contentSize")
            .subscribe(onNext: { size in
                print(size as Any)
            })
            .disposed(by: disposeBag)

答案 6 :(得分:2)

重新加载集合视图后,我只是执行了以下操作。您甚至可以在API响应中使用此代码。

AUTO FOCUS STATE INACTIVE

答案 7 :(得分:1)

这对我有用:

__weak typeof(self) wself= self;
[self.contentCollectionView performBatchUpdates:^{
    [wself.contentCollectionView reloadData];
} completion:^(BOOL finished) {
    [wself pageViewCurrentIndexDidChanged:self.contentCollectionView];
}];

答案 8 :(得分:1)

到目前为止,我发现最好的解决方案是使用SELECT s0_.slug AS slug, s0_.status AS status, a2_.time AS resumeTime FROM Course s0_ INNER JOIN Log a1_ ON (a1_.course_id = s0_.id) INNER JOIN Log a2_ ON (s0_.id = a2_.course_id AND a2_.type = 'log.resumeComplete') WHERE s0_.status = 'active' AND s0_.currentsss = '72' AND a1_.type = 'log.activated' AND a1_.time < '2020-05-31 14:25:31' AND a2_.time IS NULL OR a2_.time < '2020-07-11 14:25:31' ORDER BY s0_.id ASC; 来处理完成情况。

快捷键5

CATransaction

答案 9 :(得分:1)

正如dezinezync回答的那样,您需要在reloadDataUITableView的{​​{1}}之后向主队列分配一个代码块,然后该块将在单元出队后执行

为了使使用时更直观,我将使用以下扩展名:

UICollectionView

它也可以实现为extension UICollectionView { func reloadData(_ completion: @escaping () -> Void) { reloadData() DispatchQueue.main.async { completion() } } }

答案 10 :(得分:0)

当集合视图在用户可见之前加载时,我需要对所有可见单元格执行一些操作,我使用过:

public func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
    if shouldPerformBatch {
        self.collectionView.performBatchUpdates(nil) { completed in
            self.modifyVisibleCells()
        }
    }
}

注意在滚动浏览集合视图时会调用它,所以为了防止这种开销,我补充道:

private var souldPerformAction: Bool = true

并在行动中:

private func modifyVisibleCells() {
    if self.shouldPerformAction {
        // perform action
        ...
        ...
    }
    self.shouldPerformAction = false
}

该操作仍将执行多次,作为初始状态下的可见单元格数。但是在所有这些调用中,您将拥有相同数量的可见单元格(所有这些单元格)。在用户开始与集合视图交互后,布尔标志将阻止它再次运行。

答案 11 :(得分:0)

这里的大多数解决方案都不可靠或具有不确定的行为(这可能会导致随机错误),因为 UICollectionView 的异步性质令人困惑。

一个可靠的解决方案是将 UICollectionView 子类化以在 layoutSubviews() 的末尾运行一个完成块。

Objectice-C 中的代码: https://stackoverflow.com/a/39648633

Swift 中的代码: https://stackoverflow.com/a/39798079

答案 12 :(得分:0)

SWIFT 5

override func viewDidLoad() {
    super.viewDidLoad()
    
    // "collectionViewDidLoad" for transitioning from product's cartView to it's cell in that view
    self.collectionView?.addObserver(self, forKeyPath: "contentSize", options: NSKeyValueObservingOptions.new, context: nil)
}

override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
    if let observedObject = object as? UICollectionView, observedObject == self.collectionView {
        print("collectionViewDidLoad")
        self.collectionView?.removeObserver(self, forKeyPath: "contentSize")
    }
}

答案 13 :(得分:0)

下面是唯一对我有用的方法。

extension UICollectionView {
    func reloadData(_ completion: (() -> Void)? = nil) {
        reloadData()
        guard let completion = completion else { return }
        layoutIfNeeded()
        completion()
    }
}

答案 14 :(得分:0)

只需在批处理更新中重新加载collectionView,然后在布尔值“ finish”的帮助下检查完成块是否已完成。

self.collectionView.performBatchUpdates({
        self.collectionView.reloadData()
    }) { (finish) in
        if finish{
            // Do your stuff here!
        }
    }

答案 15 :(得分:0)

尝试在reloadData()调用之后立即通过layoutIfNeeded()强制进行同步布局传递。似乎适用于iOS 12上的UICollectionView和UITableView。

collectionView.reloadData()
collectionView.layoutIfNeeded() 

// cellForItem/sizeForItem calls should be complete
completion?()

答案 16 :(得分:0)

为我工作:


- (void)viewDidLoad {
    [super viewDidLoad];

    int ScrollToIndex = 4;

    [self.UICollectionView performBatchUpdates:^{}
                                    completion:^(BOOL finished) {
                                             NSIndexPath *indexPath = [NSIndexPath indexPathForItem:ScrollToIndex inSection:0];
                                             [self.UICollectionView scrollToItemAtIndexPath:indexPath atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:NO];
                                  }];

}

答案 17 :(得分:0)

Def做到这一点:

//Subclass UICollectionView
class MyCollectionView: UICollectionView {

    //Store a completion block as a property
    var completion: (() -> Void)?

    //Make a custom funciton to reload data with a completion handle
    func reloadData(completion: @escaping() -> Void) {
        //Set the completion handle to the stored property
        self.completion = completion
        //Call super
        super.reloadData()
    }

    //Override layoutSubviews
    override func layoutSubviews() {
        //Call super
        super.layoutSubviews()
        //Call the completion
        self.completion?()
        //Set the completion to nil so it is reset and doesn't keep gettign called
        self.completion = nil
    }

}

然后在你的VC中这样打电话

let collection = MyCollectionView()

self.collection.reloadData(completion: {

})

确保您使用的是子类!!

答案 18 :(得分:-1)

这就是我用Swift 3.0解决问题的方法:

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)

    if !self.collectionView.visibleCells.isEmpty {
        // stuff
    }
}

答案 19 :(得分:-9)

你可以这样做......

  - (void)reloadMyCollectionView{

       [myCollectionView reload];
       [self performSelector:@selector(myStuff) withObject:nil afterDelay:0.0];

   }

  - (void)myStuff{
     // Do your stuff here. This will method will get called once your collection view get loaded.

    }

答案 20 :(得分:-9)

试试这个:

- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
    return _Items.count;
}

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    UICollectionViewCell *cell;
    //Some cell stuff here...

    if(indexPath.row == _Items.count-1){
       //THIS IS THE LAST CELL, SO TABLE IS LOADED! DO STUFF!
    }

    return cell;
}