gpt4 book ai didi

Swift - 按单元格分页 UICollectionView,同时保持单元格水平居中

转载 作者:行者123 更新时间:2023-11-28 13:33:39 26 4
gpt4 key购买 nike

首先,不确定标题是否正确或是否提供了最佳描述,但我不确定还能使用什么。

所以,我正在开发一个应用程序,但我遇到了一个部分,在实现 UI 时遇到了困难。基本上,我有一个 VC(下图),它可以根据我从 JSON 文件中获得的信息进行 self 搜索。

enter image description here

问题是我需要在上侧有一个类似旋转木马的菜单,其中包含未定义的单元格数量(同样,取决于我从 JSON 文件中获得的内容)。为此,我决定使用 UICollectionView,并且成功地实现了基础知识。

但这是我卡住的部分:

  1. 由于所选单元格必须始终居中,因此当第一个和最后一个单元格被选中时,我需要在单元格和安全区域之间留出一个空白空间(参见上图)。
  2. 滚动需要分页。通常情况下,如果 UICollectionView 单元格的宽度几乎等于屏幕的宽度,但要求是能够一次滚动一个元素(请参见上面的第二个屏幕)。

我试图找到类似的东西,但也许我没有找到正确的东西,因为我能找到的只有 Paging UICollectionView by cells, not screen

此外,老实说,我从未见过具有这种行为的应用程序/UICollectionView。

我在下面发布了部分代码,但它并没有太大帮助,因为它只是标准的 UICollectionView 方法。

有什么建议吗?

class PreSignupDataVC : UIViewController, UICollectionViewDelegateFlowLayout, UICollectionViewDataSource, UIPickerViewDelegate, UIPickerViewDataSource

@IBOutlet weak var cvQuestions: UICollectionView!

var questionCell : PreSignupDataQuestionCellVC!
var screenData : Array<PreSignupScreenData> = Array<PreSignupScreenData>()
var pvDataSource : [String] = []
var numberOfComponents : Int = 0
var numberOfRowsInComponent : Int = 0
var currentScreen : Int = 1
var selectedType : Int?
var selectedCell : Int = 0
var initialLastCellInsetPoint : CGFloat = 0.0

override func viewDidLoad()
{
super.viewDidLoad()

print("PreSignupDataVC > viewDidLoad")

initialLastCellInsetPoint = (self.view.frame.width - 170)/2
screenData = DataSingleton.sharedInstance.returnPreSignUpUIArray()[selectedType!].screenData
numberOfComponents = screenData[currentScreen - 1].controls[0].numberOfComponents!
numberOfRowsInComponent = screenData[currentScreen - 1].controls[0].controlDataSource.count
pvDataSource = screenData[currentScreen - 1].controls[0].controlDataSource

cvQuestions.register(UINib(nibName: "PreSignupDataQuestionCell",
bundle: nil),
forCellWithReuseIdentifier: "PreSignupDataQuestionCellVC")
}

func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
{
print("PreSignupDataVC > collectionView > numberOfItemsInSection")

return screenData[currentScreen - 1].controls.count
}

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
{
print("PreSignupDataVC > collectionView > cellForItemAt")

questionCell = (cvQuestions.dequeueReusableCell(withReuseIdentifier: "PreSignupDataQuestionCellVC",
for: indexPath) as? PreSignupDataQuestionCellVC)!
questionCell.vQuestionCellCellContainer.layer.cornerRadius = 8.0
questionCell.lblQuestion.text = screenData[currentScreen - 1].controls[indexPath.row].cellTitle
questionCell.ivQuestionCellImage.image = UIImage(named: screenData[currentScreen - 1].controls[indexPath.row].cellUnselectedIcon!)

return questionCell
}

func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath)
{
print("PreSignupDataVC > collectionView > didSelectItemAt")

numberOfComponents = screenData[currentScreen - 1].controls[indexPath.row].numberOfComponents!
numberOfRowsInComponent = screenData[currentScreen - 1].controls[indexPath.row].controlDataSource.count
pvDataSource = screenData[currentScreen - 1].controls[indexPath.row].controlDataSource
selectedCell = indexPath.row

pvData.reloadAllComponents()
}

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets
{
print("PreSignupDataVC > collectionView > insetForSectionAt")

return UIEdgeInsets(top: 0.0, left: initialLastCellInsetPoint, bottom: 00.0, right: initialLastCellInsetPoint)
}

最佳答案

#1。使用 UICollectionViewCompositionalLayout(需要 iOS 13)

自 iOS 13 起,您可以使用 UICollectionViewCompositionalLayout并设置 NSCollectionLayoutSectionorthogonalScrollingBehavior属性为.groupPagingCentered为了有一个居中的水平旋转木马式布局。

以下 Swift 5.1 示例代码显示了 UICollectionViewCompositionalLayout 的可能实现,以获得您想要的布局:

CollectionView.swift

import UIKit

class CollectionView: UICollectionView {

override var safeAreaInsets: UIEdgeInsets {
return UIEdgeInsets(top: super.safeAreaInsets.top, left: 0, bottom: super.safeAreaInsets.bottom, right: 0)
}

}

ViewController.swift

import UIKit

class ViewController: UIViewController {

var collectionView: CollectionView!
var dataSource: UICollectionViewDiffableDataSource<Int, Int>!

override func viewDidLoad() {
super.viewDidLoad()

navigationItem.title = "Collection view"

// Compositional layout

let layout = UICollectionViewCompositionalLayout(sectionProvider: {
(sectionIndex: Int, layoutEnvironment: NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection? in

let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .fractionalHeight(1))
let item = NSCollectionLayoutItem(layoutSize: itemSize)
item.contentInsets = NSDirectionalEdgeInsets(top: 5, leading: 5, bottom: 5, trailing: 5)

let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalHeight(1), heightDimension: .fractionalHeight(1))
let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])

let section = NSCollectionLayoutSection(group: group)
section.orthogonalScrollingBehavior = UICollectionLayoutSectionOrthogonalScrollingBehavior.groupPagingCentered

return section
})

// Set collection view

collectionView = CollectionView(frame: .zero, collectionViewLayout: layout)
collectionView.backgroundColor = .systemGroupedBackground
collectionView.showsHorizontalScrollIndicator = false
collectionView.register(Cell.self, forCellWithReuseIdentifier: "Cell")

// View layout

view.addSubview(collectionView)

collectionView.translatesAutoresizingMaskIntoConstraints = false
collectionView.heightAnchor.constraint(equalToConstant: 160).isActive = true
collectionView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
collectionView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
collectionView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true

// Collection view diffable data source

dataSource = UICollectionViewDiffableDataSource<Int, Int>(collectionView: collectionView, cellProvider: {
(collectionView: UICollectionView, indexPath: IndexPath, identifier: Int) -> UICollectionViewCell? in

let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! Cell
return cell
})

var snapshot = NSDiffableDataSourceSnapshot<Int, Int>()
snapshot.appendSections([0])
snapshot.appendItems(Array(0 ..< 5))
dataSource.apply(snapshot, animatingDifferences: false)
}

}

Cell.swift

import UIKit

class Cell: UICollectionViewCell {

override init(frame: CGRect) {
super.init(frame: frame)

contentView.backgroundColor = .orange
}

required init?(coder: NSCoder) {
fatalError("not implemnted")
}

}

#2。使用 UICollectionViewFlowLayout

如果您的目标是低于 iOS 13 的 iOS 版本,您可以子类化 UICollectionViewFlowLayout , 计算 prepare() 中的水平插图并实现targetContentOffset(forProposedContentOffset:withScrollingVelocity:)以便在用户滚动后强制单元格居中。

以下 Swift 5.1 示例代码显示了如何实现 UICollectionViewFlowLayout 的子类:

ViewController.swift

import UIKit

class ViewController: UIViewController {

let flowLayout = PaggedFlowLayout()

override func viewDidLoad() {
super.viewDidLoad()

navigationItem.title = "Collection view"

let collectionView = UICollectionView(frame: .zero, collectionViewLayout: flowLayout)
collectionView.backgroundColor = .systemGroupedBackground
collectionView.showsHorizontalScrollIndicator = false
collectionView.decelerationRate = .fast
collectionView.dataSource = self
collectionView.contentInsetAdjustmentBehavior = .never
collectionView.register(Cell.self, forCellWithReuseIdentifier: "Cell")

view.addSubview(collectionView)
collectionView.translatesAutoresizingMaskIntoConstraints = false
collectionView.heightAnchor.constraint(equalToConstant: 160).isActive = true
collectionView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
collectionView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
collectionView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
}

}
extension ViewController: UICollectionViewDataSource {

func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 9
}

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! Cell
return cell
}

}

PaggedFlowLayout.swift

import UIKit

class PaggedFlowLayout: UICollectionViewFlowLayout {

override init() {
super.init()

scrollDirection = .horizontal
minimumLineSpacing = 5
minimumInteritemSpacing = 0
sectionInset = UIEdgeInsets(top: 5, left: 5, bottom: 5, right: 5)
}

required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

override func prepare() {
super.prepare()

guard let collectionView = collectionView else { fatalError() }

// itemSize
let itemHeight = collectionView.bounds.height - sectionInset.top - sectionInset.bottom
itemSize = CGSize(width: itemHeight, height: itemHeight)

// horizontal insets
let horizontalInsets = (collectionView.bounds.width - itemSize.width) / 2
sectionInset.left = horizontalInsets
sectionInset.right = horizontalInsets
}

/*
Add some snapping behaviour to center the cell after scrolling.
Source: https://stackoverflow.com/a/14291208/1966109
*/
override func targetContentOffset(forProposedContentOffset proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint {
guard let collectionView = collectionView else { return .zero }

var proposedContentOffset = proposedContentOffset
var offsetAdjustment = CGFloat.greatestFiniteMagnitude
let horizontalCenter = proposedContentOffset.x + collectionView.bounds.size.width / 2
let targetRect = CGRect(x: proposedContentOffset.x, y: 0, width: collectionView.bounds.size.width, height: collectionView.bounds.size.height)

guard let layoutAttributesArray = super.layoutAttributesForElements(in: targetRect) else { return .zero }

for layoutAttributes in layoutAttributesArray {
let itemHorizontalCenter = layoutAttributes.center.x
if abs(itemHorizontalCenter - horizontalCenter) < abs(offsetAdjustment) {
offsetAdjustment = itemHorizontalCenter - horizontalCenter
}
}

var nextOffset = proposedContentOffset.x + offsetAdjustment
let snapStep = itemSize.width + minimumLineSpacing

func isValidOffset(_ offset: CGFloat) -> Bool {
let minContentOffset = -collectionView.contentInset.left
let maxContentOffset = collectionView.contentInset.left + collectionView.contentSize.width - itemSize.width
return offset >= minContentOffset && offset <= maxContentOffset
}

repeat {
proposedContentOffset.x = nextOffset
let deltaX = proposedContentOffset.x - collectionView.contentOffset.x
let velX = velocity.x

if deltaX.sign.rawValue * velX.sign.rawValue != -1 {
break
}

nextOffset += CGFloat(velocity.x.sign.rawValue) * snapStep
} while isValidOffset(nextOffset)

return proposedContentOffset
}

}

Cell.swift

import UIKit

class Cell: UICollectionViewCell {

override init(frame: CGRect) {
super.init(frame: frame)

contentView.backgroundColor = .orange
}

required init?(coder: NSCoder) {
fatalError("not implemnted")
}

}

iPhone 11 Pro Max 上的显示:

enter image description here

关于Swift - 按单元格分页 UICollectionView,同时保持单元格水平居中,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56984736/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com