libc++abi: terminating with uncaught exception of type NSException
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid parameter not satisfying: self.supplementaryViewProvider || (self.supplementaryReuseIdentifierProvider && self.supplementaryViewConfigurationHandler)'
terminating with uncaught exception of type NSException
로그를 대충 읽어보니 supplementaryViewProvider와 supplementaryReuseIdentifierProvider가 만족되지 않는다네.
아래 코드처럼 collectionView에 헤더뷰 register()를 해줬는데 dequeueReusableSupplementaryView 도 같이 해줘야하나보다.
내 코드 >
// 1. Configure collectionView
private lazy var collectionView = UICollectionView(frame: .zero, collectionViewLayout: createLayout()).then {
$0.register(TopBannerCollectionViewCell.self, forCellWithReuseIdentifier: TopBannerCollectionViewCell.cellIdentifier)
$0.register(HomeCategoryCollectionViewCell.self, forCellWithReuseIdentifier: HomeCategoryCollectionViewCell.identifier)
$0.register(CommunityMainCollectionViewCell.self, forCellWithReuseIdentifier: CommunityMainCollectionViewCell.identifier)
// Header register
$0.register(HomeTitleHeaderView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: HomeTitleHeaderView.identifier)
$0.backgroundColor = .clear
}
// 2. configure header
viewModel.datasource.supplementaryViewProvider = { [weak self] view, kind, index in
return self?.collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: HomeTitleHeaderView.identifier, for: index)
}
기존에는 UICollectionViewDelegate의 "collectionView(:viewForSupplementaryElementOfKind:at:)" 함수 내에서 dequeueReusableSupplementaryView를 작성했다면
Diffable Datasource를 사용할 땐, "supplementaryViewProvider" 로 구현할 수 있음.
libc++abi: terminating with uncaught exception of type NSException
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'the view returned from -collectionView:viewForSupplementaryElementOfKind:atIndexPath: does not match the element kind it is being used for. When asked for a view of element kind 'HomeTitleHeaderView' the data source dequeued a view registered for the element kind 'UICollectionElementKindSectionHeader'.'
terminating with uncaught exception of type NSException
이번엔 또 읽어보니 element kind가 매칭이 안된다..? 뭘까..
내가 알고있던 kind는 header인지 footer인지 구분하는 거고 헤더면 "UICollectionView.elementKindSectionHeader" 이거 쓰면 되는거 아니였냐구..!
내 코드 >
// collectionView register header
collectionView.register(CommunityMainCollectionViewCell.self, forCellWithReuseIdentifier: CommunityMainCollectionViewCell.identifier)
// header reusable view dequeue
collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: HomeTitleHeaderView.identifier, for: index)
흠,, 잘 넣어준 것 같은데 다시 잘 찾아보니...
이유를 찾았다.. 맞아 코드는 거짓말을 안하지 ㅠㅠㅠ 항상 사람이 문제야....
// item, group 관련 코드 생략
let headerSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .absolute(56))
let headerElement = NSCollectionLayoutBoundarySupplementaryItem(layoutSize: headerSize,
elementKind: HomeTitleHeaderView.identifier, // 띠용..
alignment: .topLeading)
section.boundarySupplementaryItems = [headerElement]
휴.. layout 잡아주는 부분에서 headerItem elementKind에 reuseIdentifier를 넣어주고 있... ㅠㅠ
서로 값이 다르니 당연히 오류가 날 수 밖에 ㅎㅎ
저 부분을 "UICollectionView.elementKindSectionHeader" 로 넣어주니 에러 없이 잘 나온다!
어차피 kind는 string이기 때문에 직접 string 만들어서 넣어줘도 상관은 없고 값만 서로 동일하면 에러 안남!
.
.
.
이거 때문에 열심히 구글링 해보다가 cell 과 header/footer register()를 해주는 방식이 기존과는 다른게 있길래 그거도 적용해 봤는데 잘 됨! 하지만 그건 iOS 14 이상에서만 가능..ㅎ
우리 앱은 iOS 13이 최소 지원 버전이라.. 버전을 올리자 할수도 없고 ㅠㅠ
이틀 내내 고생하다 원인을 찾고나니 허무.. 앞으론 더 꼼꼼히 봐보자!
코드 ex)
// cell registrations
let topBannerCellRegistration = UICollectionView.CellRegistration<TopBannerCollectionViewCell, [Banner]> { (cell, indexPath, banner) in
cell.setData(banners: banner)
}
let categoryCellRegistration = UICollectionView.CellRegistration<HomeCategoryCollectionViewCell, HomeServiceMenu> { (cell, indexPath, homeMenu) in
// ... (생략)
}
let macapickCellRegistration = UICollectionView.CellRegistration<CommunityMainCollectionViewCell, Story> { (cell, indexPath, story) in
// ... (생략)
}
let headerRegistration = UICollectionView.SupplementaryRegistration<HomeTitleHeaderView>(elementKind: HomeTitleHeaderView.identifier) { supplementaryView, elementKind, indexPath in
// ... (생략)
}
// datasource에 적용
viewModel.datasource = UICollectionViewDiffableDataSource<HomeSection, HomeDataItem>(collectionView: collectionView, cellProvider: { collectionView, indexPath, listItem in
switch listItem {
case .topBanner(let banners):
let cell = collectionView.dequeueConfiguredReusableCell(
using: topBannerCellRegistration, for: indexPath, item: banners
)
return cell
.
.
.
(생략)
}
})
3번째 섹션에 header view 잘 적용된 거 확인~~!!
출처:
https://jamesrochabrun.medium.com/uicollectionviewdiffabledatasource-and-decodable-step-by-step-6b727dd2485
https://swiftsenpai.com/development/reload-diffable-section-header/