带有 groupPagingCentered 的 UICollectionViewCompositionalLayout 没有开始居中
UICollectionViewCompositionalLayout with groupPagingCentered doesn't start centered
我的布局代码非常简单,您会在每篇有关新组合布局的教程或文章中看到这些代码。
func createLayout() -> UICollectionViewLayout {
let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .fractionalHeight(1.0))
let item = NSCollectionLayoutItem(layoutSize: itemSize)
item.contentInsets = .init(top: 0, leading: 5, bottom: 0, trailing: 5)
let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(0.93), heightDimension: .fractionalHeight(1.0))
let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])
let section = NSCollectionLayoutSection(group: group)
section.orthogonalScrollingBehavior = .groupPagingCentered
let layout = UICollectionViewCompositionalLayout(section: section)
return layout
}
当我启动应用程序时,单元格未正确居中。只有当我将单元格拖动到最小量时,它才会 spring 到正确的位置。
之前:
稍微拖一下后:
我还没有在 SO 上看到任何关于相同问题的问题。 Twitter 或博客中没有人谈论它。不确定我在这里做错了什么?
这是预期的行为。 "centered" 表示单元格在滚动后 对齐中心。 在进行 any 滚动之前,整个组一直滚动到正确的。您的组只有 0.93
小数宽度,因此差异很小。当分数宽度较小时,效果不那么难看。
您可能想试试这个:
易于实施并获得您需要的结果。
假设我要显示横幅,使用上面link就可以得到结果。
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
DispatchQueue.main.async {
let layout = self.collBannerView.collectionViewLayout as! UPCarouselFlowLayout
layout.spacingMode = UPCarouselFlowLayoutSpacingMode.fixed(spacing: 10)
//overlap(visibleOffset: 50)
layout.itemSize = CGSize.init(width: self.view.frame.width - 70, height: 200)
self.collBannerView.collectionViewLayout = layout
let direction: UICollectionView.ScrollDirection = self.orientation.isPortrait ? .horizontal : .vertical
layout.scrollDirection = direction
}
}
滚动到第一项:
@available(iOS 13.0, *)
private func centerCollectionView() {
guard collectionView.collectionViewLayout is UICollectionViewCompositionalLayout else { return }
collectionView.scrollToItem(at: IndexPath(item: 0, section: 0), at: .centeredHorizontally, animated: false)
}
如果该项目没有填满整个屏幕,您需要滚动到其他项目,否则会出现奇怪的闪烁。因此,例如,如果您的屏幕充满了两个项目,代码将是:
collectionView.scrollToItem(at: IndexPath(item: 1, section: 0), at: .centeredHorizontally, animated: false)
collectionView.scrollToItem(at: IndexPath(item: 0, section: 0), at: .centeredHorizontally, animated: false)
可能为时已晚,但这里有一个解决方法:
func createLayout() -> UICollectionViewLayout {
let layout = UICollectionViewCompositionalLayout { (sectionIndex, environment) -> NSCollectionLayoutSection? in
let sideInset: CGFloat = 5
let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .fractionalHeight(1.0))
let item = NSCollectionLayoutItem(layoutSize: itemSize)
item.contentInsets = .init(top: 0, leading: sideInset, bottom: 0, trailing: sideInset)
let groupWidth = environment.container.contentSize.width * 0.93
let groupSize = NSCollectionLayoutSize(widthDimension: .absolute(groupWidth), heightDimension: .fractionalHeight(1.0))
let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])
let section = NSCollectionLayoutSection(group: group)
// add leading and trailing insets to the section so groups are aligned to the center
let sectionSideInset = (environment.container.contentSize.width - groupWidth) / 2
section.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: sectionSideInset, bottom: 0, trailing: sectionSideInset)
// note this is not .groupPagingCentered
section.orthogonalScrollingBehavior = .groupPaging
return section
}
return layout
}
我发现,如果您将页眉添加到您的部分(甚至是空的),它就可以解决问题。
let layoutSectionHeaderSize = NSCollectionLayoutSize(widthDimension: .estimated(1.0), heightDimension: .estimated(1.0))
let layoutSectionHeader = NSCollectionLayoutBoundarySupplementaryItem(layoutSize: layoutSectionHeaderSize, elementKind: UICollectionView.elementKindSectionHeader, alignment: .top)
section.boundarySupplementaryItems = [layoutSectionHeader]
是的,你需要这个:
register(SectionHeader.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "SectionHeader")
并在此处提供任何空 UICollectionReusableView
:
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
guard let headerView = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "SectionHeader", for: indexPath) as? SectionHeader else {
fatalError("Could not dequeue SectionHeader")
}
return // some empty SectionHeader
}
工作样本是here
就我而言,我的 collectionView-embedded-viewController 嵌入在自定义标签栏控制器中。因此,当我的 collectionView 被滑动时,我向 parent viewController 的标签栏按钮发送了操作。它奏效了。
class ChildVC: UIViewController {
var parentVC: MY_PARENT_VC?
var collectionView: UICollectionView!
// for the first horizontal row
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
parentVC?.MY_CUSTOM_BUTTON.sendActions(for: .touchUpInside)
}
}
extension ChildVC: UICollectionViewDelegate {
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
parentVC?.MY_CUSTOM_BUTTON.sendActions(for: .touchUpInside)
}
}
轻松修复
正如另一个答案中所建议的,您可以告诉集合视图将某个项目滚动到中心。如果您在视图可见后执行此操作,用户将看到 UI 自行移动……不理想。您可以将项目居中代码放入 viewDidLoad
但它不会工作,除非您设置非常轻微的延迟 - 我假设您只需要在初始布局传递完成后将单元格居中。下面的代码对我有用:
override func viewDidLoad() {
super.viewDidLoad()
// collectionView setup goes here...
DispatchQueue.main.asyncAfter(deadline: .now() + 0.001) { [self] in
collectionView.scrollToItem(at: IndexPath(item: 0, section: 0), at: .centeredHorizontally, animated: false)
}
}
甚至后来,但我在尝试做类似的事情时遇到了类似的问题,这条线神奇地起作用了:
section.contentInsetsReference = .readableContent
我完全不知道它为什么有效。我尝试使用 item.contentInsets、edgeSpacing 等所有方法。神奇的是,这对我有用。也许你会取得类似的成功?
我的布局代码非常简单,您会在每篇有关新组合布局的教程或文章中看到这些代码。
func createLayout() -> UICollectionViewLayout {
let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .fractionalHeight(1.0))
let item = NSCollectionLayoutItem(layoutSize: itemSize)
item.contentInsets = .init(top: 0, leading: 5, bottom: 0, trailing: 5)
let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(0.93), heightDimension: .fractionalHeight(1.0))
let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])
let section = NSCollectionLayoutSection(group: group)
section.orthogonalScrollingBehavior = .groupPagingCentered
let layout = UICollectionViewCompositionalLayout(section: section)
return layout
}
当我启动应用程序时,单元格未正确居中。只有当我将单元格拖动到最小量时,它才会 spring 到正确的位置。
之前:
稍微拖一下后:
我还没有在 SO 上看到任何关于相同问题的问题。 Twitter 或博客中没有人谈论它。不确定我在这里做错了什么?
这是预期的行为。 "centered" 表示单元格在滚动后 对齐中心。 在进行 any 滚动之前,整个组一直滚动到正确的。您的组只有 0.93
小数宽度,因此差异很小。当分数宽度较小时,效果不那么难看。
您可能想试试这个:
易于实施并获得您需要的结果。
假设我要显示横幅,使用上面link就可以得到结果。
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
DispatchQueue.main.async {
let layout = self.collBannerView.collectionViewLayout as! UPCarouselFlowLayout
layout.spacingMode = UPCarouselFlowLayoutSpacingMode.fixed(spacing: 10)
//overlap(visibleOffset: 50)
layout.itemSize = CGSize.init(width: self.view.frame.width - 70, height: 200)
self.collBannerView.collectionViewLayout = layout
let direction: UICollectionView.ScrollDirection = self.orientation.isPortrait ? .horizontal : .vertical
layout.scrollDirection = direction
}
}
滚动到第一项:
@available(iOS 13.0, *)
private func centerCollectionView() {
guard collectionView.collectionViewLayout is UICollectionViewCompositionalLayout else { return }
collectionView.scrollToItem(at: IndexPath(item: 0, section: 0), at: .centeredHorizontally, animated: false)
}
如果该项目没有填满整个屏幕,您需要滚动到其他项目,否则会出现奇怪的闪烁。因此,例如,如果您的屏幕充满了两个项目,代码将是:
collectionView.scrollToItem(at: IndexPath(item: 1, section: 0), at: .centeredHorizontally, animated: false)
collectionView.scrollToItem(at: IndexPath(item: 0, section: 0), at: .centeredHorizontally, animated: false)
可能为时已晚,但这里有一个解决方法:
func createLayout() -> UICollectionViewLayout {
let layout = UICollectionViewCompositionalLayout { (sectionIndex, environment) -> NSCollectionLayoutSection? in
let sideInset: CGFloat = 5
let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .fractionalHeight(1.0))
let item = NSCollectionLayoutItem(layoutSize: itemSize)
item.contentInsets = .init(top: 0, leading: sideInset, bottom: 0, trailing: sideInset)
let groupWidth = environment.container.contentSize.width * 0.93
let groupSize = NSCollectionLayoutSize(widthDimension: .absolute(groupWidth), heightDimension: .fractionalHeight(1.0))
let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])
let section = NSCollectionLayoutSection(group: group)
// add leading and trailing insets to the section so groups are aligned to the center
let sectionSideInset = (environment.container.contentSize.width - groupWidth) / 2
section.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: sectionSideInset, bottom: 0, trailing: sectionSideInset)
// note this is not .groupPagingCentered
section.orthogonalScrollingBehavior = .groupPaging
return section
}
return layout
}
我发现,如果您将页眉添加到您的部分(甚至是空的),它就可以解决问题。
let layoutSectionHeaderSize = NSCollectionLayoutSize(widthDimension: .estimated(1.0), heightDimension: .estimated(1.0))
let layoutSectionHeader = NSCollectionLayoutBoundarySupplementaryItem(layoutSize: layoutSectionHeaderSize, elementKind: UICollectionView.elementKindSectionHeader, alignment: .top)
section.boundarySupplementaryItems = [layoutSectionHeader]
是的,你需要这个:
register(SectionHeader.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "SectionHeader")
并在此处提供任何空 UICollectionReusableView
:
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
guard let headerView = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "SectionHeader", for: indexPath) as? SectionHeader else {
fatalError("Could not dequeue SectionHeader")
}
return // some empty SectionHeader
}
工作样本是here
就我而言,我的 collectionView-embedded-viewController 嵌入在自定义标签栏控制器中。因此,当我的 collectionView 被滑动时,我向 parent viewController 的标签栏按钮发送了操作。它奏效了。
class ChildVC: UIViewController {
var parentVC: MY_PARENT_VC?
var collectionView: UICollectionView!
// for the first horizontal row
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
parentVC?.MY_CUSTOM_BUTTON.sendActions(for: .touchUpInside)
}
}
extension ChildVC: UICollectionViewDelegate {
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
parentVC?.MY_CUSTOM_BUTTON.sendActions(for: .touchUpInside)
}
}
轻松修复
正如另一个答案中所建议的,您可以告诉集合视图将某个项目滚动到中心。如果您在视图可见后执行此操作,用户将看到 UI 自行移动……不理想。您可以将项目居中代码放入 viewDidLoad
但它不会工作,除非您设置非常轻微的延迟 - 我假设您只需要在初始布局传递完成后将单元格居中。下面的代码对我有用:
override func viewDidLoad() {
super.viewDidLoad()
// collectionView setup goes here...
DispatchQueue.main.asyncAfter(deadline: .now() + 0.001) { [self] in
collectionView.scrollToItem(at: IndexPath(item: 0, section: 0), at: .centeredHorizontally, animated: false)
}
}
甚至后来,但我在尝试做类似的事情时遇到了类似的问题,这条线神奇地起作用了:
section.contentInsetsReference = .readableContent
我完全不知道它为什么有效。我尝试使用 item.contentInsets、edgeSpacing 等所有方法。神奇的是,这对我有用。也许你会取得类似的成功?