UICollectionView tam olarak yüklendiğinde, yani o sırada tüm UICollectionView veri kaynağı / düzen yöntemleri çağrılmalıdır. Bunu nasıl bilebilirim?? UICollectionView yüklenme durumunu bilmek için herhangi bir temsilci yöntemi var mı?
Yanıtlar:
// 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];
}
[self.collectionView removeObserver:self forKeyPath:@"contentSize" context:NULL];
için -viewWillDisappear:animated:
de.
Bu benim için çalıştı:
[self.collectionView reloadData];
[self.collectionView performBatchUpdates:^{}
completion:^(BOOL finished) {
/// collection-view finished reload
}];
Swift 4 sözdizimi:
self.collectionView.reloadData()
self.collectionView.performBatchUpdates(nil, completion: {
(result) in
// ready
})
reloadData
, collectionView
veri kaynağı yöntemlerini tekrar kullanması için yeniler ... performBatchUpdates
eklenmiş bir tamamlama bloğu vardır, bu nedenle her ikisi de ana iş parçacığı üzerinde gerçekleştirilirse, yerine yerleştirilen herhangi bir kodun /// collection-view finished reload
yeni ve düzenlenmiş bir şekilde yürütüleceğini bilirsinizcollectionView
Aslında oldukça basit.
Örneğin, UICollectionView'ın reloadData yöntemini veya düzenininvalidateLayout yöntemini çağırdığınızda, aşağıdakileri yaparsınız:
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
});
Bu neden işe yarıyor:
Ana iş parçacığı (tüm UI güncellemelerini yapmamız gereken yer), doğası gereği seri olan ana kuyruğu barındırır, yani FIFO tarzında çalışır. Dolayısıyla, yukarıdaki örnekte, bizim reloadData
yöntemimiz çağrılan ilk blok , ardından ikinci bloktaki herhangi bir şey çağrılır.
Şimdi ana iş parçacığı da engelliyor. Yani reloadData
, yürütmek için 3 saniye alırsanız , ikinci bloğun işlenmesi bu 3 saniye tarafından ertelenecektir.
reloadData
şimdi ana iş parçacığı üzerindeki hücrelerin yüklenmesini sıralar (ana iş parçacığından çağrılsa bile). Yani hücreler, dönüşlerden cellForRowAtIndexPath:
sonra aslında yüklenmez ( çağrılmaz) reloadData
. Hücreleriniz yüklendikten sonra çalıştırılmasını istediğiniz kodu sarmak dispatch_async(dispatch_get_main...
ve aramanızdan sonra onu çağırmak reloadData
istenen sonucu elde edecektir.
Sadece harika bir @dezinezync yanıtına eklemek için:
Swift 3+
collectionView.collectionViewLayout.invalidateLayout() // or reloadData()
DispatchQueue.main.async {
// your stuff here executing after collectionView has been layouted
}
Bunu şöyle yapın:
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)
})
ReloadData () çağrısından hemen sonra layoutIfNeeded () yoluyla senkronize bir düzen geçişini zorlamayı deneyin. İOS 12'de hem UICollectionView hem de UITableView için çalışıyor gibi görünüyor.
collectionView.reloadData()
collectionView.layoutIfNeeded()
// cellForItem/sizeForItem calls should be complete
completion?()
Gibi dezinezync cevap, ne gerek sonra kod bloğu ana sıraya sevk etmektir reloadData
bir gelen UITableView
veya UICollectionView
, ve sonra bu blok hücrelerinin sonra infaz edilecek dequeuing
Bunu kullanırken daha düzgün hale getirmek için, şöyle bir uzantı kullanırım:
extension UICollectionView {
func reloadData(_ completion: @escaping () -> Void) {
reloadData()
DispatchQueue.main.async { completion() }
}
}
Ayrıca için uygulanabilir UITableView
yanı
RxSwift / RxCocoa kullanarak farklı bir yaklaşım:
collectionView.rx.observe(CGSize.self, "contentSize")
.subscribe(onNext: { size in
print(size as Any)
})
.disposed(by: disposeBag)
performBatchUpdates
benim durumumda çalışmıyordu, bu yapıyor. Şerefe!
Bu benim için çalışıyor:
__weak typeof(self) wself= self;
[self.contentCollectionView performBatchUpdates:^{
[wself.contentCollectionView reloadData];
} completion:^(BOOL finished) {
[wself pageViewCurrentIndexDidChanged:self.contentCollectionView];
}];
Koleksiyon görünümü kullanıcıya görünür olmadan önce yüklendiğinde tüm görünür hücrelerde bazı işlemlerin yapılmasına ihtiyacım vardı, kullandım:
public func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
if shouldPerformBatch {
self.collectionView.performBatchUpdates(nil) { completed in
self.modifyVisibleCells()
}
}
}
Koleksiyon görünümünde gezinirken bunun çağrılacağına dikkat edin, bu yüzden bu ek yükü önlemek için ekledim:
private var souldPerformAction: Bool = true
ve eylemin kendisinde:
private func modifyVisibleCells() {
if self.shouldPerformAction {
// perform action
...
...
}
self.shouldPerformAction = false
}
Eylem, ilk durumda görünen hücrelerin sayısı kadar birden çok kez gerçekleştirilecektir. ancak tüm bu aramalarda, aynı sayıda görünür hücreye sahip olacaksınız (tümü). Ve boole bayrağı, kullanıcı koleksiyon görünümüyle etkileşim kurmaya başladıktan sonra tekrar çalışmasını engelleyecektir.
Koleksiyon görünümü yeniden yüklendikten sonra herhangi bir şey gerçekleştirmek için aşağıdakileri yaptım. Bu kodu API yanıtında bile kullanabilirsiniz.
self.collectionView.reloadData()
DispatchQueue.main.async {
// Do Task after collection view is reloaded
}
Şimdiye kadar bulduğum en iyi çözüm, CATransaction
tamamlamanın üstesinden gelmek için kullanmaktır .
Swift 5 :
CATransaction.begin()
CATransaction.setCompletionBlock {
// UICollectionView is ready
}
collectionView.reloadData()
CATransaction.commit()
Bunu yapın:
//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
}
}
O zaman VC'nizde böyle arayın
let collection = MyCollectionView()
self.collection.reloadData(completion: {
})
Alt sınıfı kullandığınızdan emin olun !!
Bu benim için çalışıyor:
- (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];
}];
}
Swift 3.0 ile problemi böyle çözdüm:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if !self.collectionView.visibleCells.isEmpty {
// stuff
}
}
Bunu dene:
- (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;
}
Böyle yapabilirsin ...
- (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.
}