답변:
// 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];
합니다 -viewWillDisappear:animated:
.
이것은 나를 위해 일했습니다.
[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
})
reloadData
은를 새로 collectionView
고쳐서 데이터 소스 메서드를 다시 그립니다 ... performBatchUpdates
에 완료 블록이 연결되어 있으므로 둘 다 메인 스레드에서 수행되는 경우 그 자리에 배치 된 코드가 /// collection-view finished reload
새로 배치 된 상태로 실행 된다는 것을 알고 있습니다.collectionView
실제로는 매우 간단합니다.
예를 들어 UICollectionView의 reloadData 메서드를 호출하거나 레이아웃의 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 초에 의해 지연됩니다.
reloadData
이제 메인 스레드에서 셀로드를 대기열에 추가합니다 (메인 스레드에서 호출 된 경우에도). 따라서 셀은 반환 cellForRowAtIndexPath:
후 실제로로드 되지 않습니다 (호출되지 않음) reloadData
. 셀이로드 된 후 실행하려는 코드를 래핑 dispatch_async(dispatch_get_main...
하고를 호출 한 후에이를 호출 reloadData
하면 원하는 결과를 얻을 수 있습니다.
훌륭한 @dezinezync 답변에 추가하려면 다음을 수행하십시오.
Swift 3+
collectionView.collectionViewLayout.invalidateLayout() // or reloadData()
DispatchQueue.main.async {
// your stuff here executing after collectionView has been layouted
}
다음과 같이하십시오.
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 () 호출 직후 layoutIfNeeded ()를 통해 동기식 레이아웃 전달을 시도하십시오. iOS 12에서 UICollectionView 및 UITableView 모두에서 작동하는 것 같습니다.
collectionView.reloadData()
collectionView.layoutIfNeeded()
// cellForItem/sizeForItem calls should be complete
completion?()
dezinezync 가 대답 했듯이 , 필요한 것은 reloadData
a UITableView
또는 에서 코드 블록을 메인 큐에 디스패치하는 UICollectionView
것입니다. 그러면이 블록은 셀 대기열에서 제외 된 후에 실행됩니다.
사용할 때 이것을 더 똑바로 만들기 위해 다음과 같은 확장을 사용합니다.
extension UICollectionView {
func reloadData(_ completion: @escaping () -> Void) {
reloadData()
DispatchQueue.main.async { completion() }
}
}
그것은 또한에 구현 될 수 UITableView
뿐만 아니라
RxSwift / RxCocoa를 사용한 다른 접근 방식 :
collectionView.rx.observe(CGSize.self, "contentSize")
.subscribe(onNext: { size in
print(size as Any)
})
.disposed(by: disposeBag)
performBatchUpdates
제 경우에는 작동하지 않았습니다. 건배!
이것은 나를 위해 작동합니다.
__weak typeof(self) wself= self;
[self.contentCollectionView performBatchUpdates:^{
[wself.contentCollectionView reloadData];
} completion:^(BOOL finished) {
[wself pageViewCurrentIndexDidChanged:self.contentCollectionView];
}];
컬렉션 뷰가 사용자에게 표시되기 전에로드 될 때 표시되는 모든 셀에 대해 수행해야하는 작업이 필요했습니다.
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
}
작업은 초기 상태에서 보이는 셀의 수로 여러 번 수행됩니다. 그러나 이러한 모든 호출에서 동일한 수의 보이는 셀 (모두)을 갖게됩니다. 그리고 부울 플래그는 사용자가 컬렉션 뷰와 상호 작용하기 시작한 후에 다시 실행되지 않도록합니다.
지금까지 찾은 최고의 솔루션은 CATransaction
완성을 처리하기 위해 사용하는 것 입니다.
스위프트 5 :
CATransaction.begin()
CATransaction.setCompletionBlock {
// UICollectionView is ready
}
collectionView.reloadData()
CATransaction.commit()
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: {
})
서브 클래스를 사용하고 있는지 확인하십시오 !!
이것은 나를 위해 일합니다.
- (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으로 문제를 해결 한 방법입니다.
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if !self.collectionView.visibleCells.isEmpty {
// stuff
}
}
이 시도:
- (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;
}
이렇게 할 수 있습니다 ...
- (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.
}