如何解决N个“收藏夹视图”单元格-自动调整大小以触发屏幕中的所有单元格
我需要始终将收藏视图单元格设置在iphone屏幕范围内。
它应该像:
- 如果是一个单元格,则应填满整个屏幕
- 如果有两个单元格,则第一个单元格应填充前半部分,第二个单元格应填充后半部分
- 如果有三个单元格,则屏幕应划分为四个相等的单元格,第四个单元格的占位符应为空。如果是第四个单元格,则应填充空白区域。
- 如果是五个/六个单元格,则应该有三行,每行两个单元格。
我有一个逻辑,但由于受到一些黑客的攻击,我设法仅支持6个单元,此后它不起作用。
func collectionView(_ collectionView: UICollectionView,layout collectionViewLayout: UICollectionViewLayout,sizeforItemAt indexPath: IndexPath) -> CGSize {
var horizontalTiles = 1
var verticalTiles = 1
var targetWidth = collectionView.frame.width
var targetHeight = collectionView.frame.height - UIApplication.shared.statusBarFrame.height
horizontalTiles = horizontalTiles+(self.contactsArray.count/3)
verticalTiles = verticalTiles+(self.contactsArray.count/2)
if self.contactsArray.count%4 == 0 || self.contactsArray.count%6 == 0 {
return lastCellSize
}
targetWidth = targetWidth / CGFloat(horizontalTiles)
targetHeight = targetHeight / CGFloat(verticalTiles)
let size = CGSize(width: targetWidth,height: targetHeight)
lastCellSize = size
return size
}
如果有更好的方法或使用流布局,那将非常有帮助。
解决方法
将动画放在一边...您可以通过一些聪明的数学/逻辑来获得尺寸(您的问题专门询问尺寸问题)。
一些观察/假设:
- 所有单元格的大小相同,因此我们可以返回一个大小
- 行/列交替增加的数量
- 行在列之前递增
- 在所有列都填入最后一行之前,大小保持不变
给出这个...
func collectionView(_ collectionView: UICollectionView,layout collectionViewLayout: UICollectionViewLayout,sizeForItemAt indexPath: IndexPath) -> CGSize {
let numItems = self.contactsArray.count
let targetWidth = collectionView.frame.width
let targetHeight = collectionView.frame.height - UIApplication.shared.statusBarFrame.height
if numItems == 1 {
return CGSize(width: targetWidth,height: targetHeight)
}
// Below logic only works for numItems > 1,so we check for that ^
var numRows = 1
var numColumns = 1
for index in 1...(numItems-1) {
if index % numColumns == 0 {
if numColumns == numRows {
numRows += 1
} else {
numColumns += 1
}
}
}
return CGSize(width: targetWidth / numColumns,height: targetHeight / numRows)
}
,
如果您不希望使用集合视图,这是另一种方法。
考虑行和列。行数将是总计项的平方根的上限,列数将是总计项的上限除以行数。
然后,您可以轻松计算出“平铺”尺寸。
示例代码:
class ArrangeViewController: UIViewController {
// Add a view button
let addButton: UIButton = {
let v = UIButton()
v.setTitle("Add",for: .normal)
return v
}()
// Remove a view button
let remButton: UIButton = {
let v = UIButton()
v.setTitle("Remove",for: [])
return v
}()
// horizontal stackview to hold the buttons
let btnsStack: UIStackView = {
let v = UIStackView()
v.translatesAutoresizingMaskIntoConstraints = false
v.axis = .horizontal
v.alignment = .fill
v.distribution = .fillEqually
v.spacing = 20
return v
}()
// view to hold the added views
let tilesContainerView: UIView = {
let v = UIView()
v.translatesAutoresizingMaskIntoConstraints = false
v.backgroundColor = .systemRed
v.clipsToBounds = true
return v
}()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemBlue
// button properties
[addButton,remButton].forEach { b in
b.backgroundColor = .yellow
b.setTitleColor(.blue,for: .normal)
b.setTitleColor(.lightGray,for: .highlighted)
}
// add the buttons to the stack view
btnsStack.addArrangedSubview(addButton)
btnsStack.addArrangedSubview(remButton)
// add buttons stack to the view
view.addSubview(btnsStack)
// add border container to the view
view.addSubview(tilesContainerView)
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
// constrain buttons stack Top / Leading / Trailing with a little "padding"
btnsStack.topAnchor.constraint(equalTo: g.topAnchor,constant: 12.0),btnsStack.leadingAnchor.constraint(equalTo: g.leadingAnchor,constant: 20.0),btnsStack.trailingAnchor.constraint(equalTo: g.trailingAnchor,constant: -20.0),// buttons height to 40-pts (just for asthetics)
btnsStack.heightAnchor.constraint(equalToConstant: 40.0),// constrain border container
// 20-pts below buttons
tilesContainerView.topAnchor.constraint(equalTo: btnsStack.bottomAnchor,// 20-pts from view bottom
tilesContainerView.bottomAnchor.constraint(equalTo: g.bottomAnchor,// 20-pts Leading and Trailing
tilesContainerView.leadingAnchor.constraint(equalTo: g.leadingAnchor,tilesContainerView.trailingAnchor.constraint(equalTo: g.trailingAnchor,])
// add actions for the Add and Delete buttons
addButton.addTarget(self,action: #selector(addTapped(_:)),for: .touchUpInside)
remButton.addTarget(self,action: #selector(remTapped(_:)),for: .touchUpInside)
}
override func viewWillTransition(to size: CGSize,with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size,with: coordinator)
coordinator.animate(alongsideTransition: { _ in
}) { [unowned self] _ in
self.arrangeViews()
}
}
@objc func addTapped(_ sender: Any?) -> Void {
// instantiate a new custom view and add it to
// the inner container view
let v = MyArrangeView()
tilesContainerView.addSubview(v)
v.theLabel.text = "\(tilesContainerView.subviews.count)"
// update the arrangement
arrangeViews()
}
@objc func remTapped(_ sender: Any?) -> Void {
// if inner container has at least one custom view
if let v = tilesContainerView.subviews.last {
// remove it
v.removeFromSuperview()
// update the arrangement
arrangeViews()
}
}
func arrangeViews() -> Void {
// make sure there is at least 1 subview to arrange
guard tilesContainerView.subviews.count > 0 else { return }
// init local vars to use
// Note: making them all CGFLoats makes it easier to use in expressions - avoids a lot of casting CGFloat(var)
var numCols: CGFloat = 0
var numRows: CGFloat = 0
var w: CGFloat = 0
var h: CGFloat = 0
// this is the frame we need to fit inside
let containerWidth: CGFloat = tilesContainerView.frame.size.width
let containerHeight: CGFloat = tilesContainerView.frame.size.height
// number of views to arrange
let numTiles: CGFloat = CGFloat(tilesContainerView.subviews.count)
// get the ceil of the square root of number of tiles
// that's the number of rows
numRows = CGFloat(ceil(sqrt(numTiles)))
// get the ceil of the number of tiles divided by number of rows
// that's the number of columns
numCols = CGFloat(ceil(numTiles / numRows))
// size of each tile
w = containerWidth / numCols
h = containerHeight / numRows
var x: CGFloat = 0.0
var y: CGFloat = 0.0
UIView.animate(withDuration: 0.3,animations: {
// loop through,doing the actual layout (setting each item's frame)
self.tilesContainerView.subviews.forEach { v in
v.frame = CGRect(x: x,y: y,width: w,height: h)
x += w
if x + w > containerWidth + 1 {
x = 0.0
y += h
}
}
self.view.layoutIfNeeded()
})
}
}
// simple bordered custom view with a label in a "content container"
class MyArrangeView: UIView {
// this will hold the "content" of the custom view
// for this example,it just holds a label
let theContentView: UIView = {
let v = UIView()
v.translatesAutoresizingMaskIntoConstraints = false
v.backgroundColor = .systemTeal
return v
}()
let theLabel: UILabel = {
let v = UILabel()
v.translatesAutoresizingMaskIntoConstraints = false
v.backgroundColor = .clear
v.textAlignment = .center
v.font = UIFont.systemFont(ofSize: 14.0)
return v
}()
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() -> Void {
self.backgroundColor = .systemYellow
// add the label to the content view
theContentView.addSubview(theLabel)
// add the content view to self
addSubview(theContentView)
NSLayoutConstraint.activate([
// constrain the label to all 4 sides of the content view
theLabel.topAnchor.constraint(equalTo: theContentView.topAnchor,constant: 0.0),theLabel.bottomAnchor.constraint(equalTo: theContentView.bottomAnchor,theLabel.leadingAnchor.constraint(equalTo: theContentView.leadingAnchor,theLabel.trailingAnchor.constraint(equalTo: theContentView.trailingAnchor,// constrain the content view to all 4 sides of self with 5-pts "padding"
theContentView.topAnchor.constraint(equalTo: topAnchor,constant: 5.0),theContentView.bottomAnchor.constraint(equalTo: bottomAnchor,constant: -5.0),theContentView.leadingAnchor.constraint(equalTo: leadingAnchor,theContentView.trailingAnchor.constraint(equalTo: trailingAnchor,])
layer.borderWidth = 1
layer.borderColor = UIColor.systemGray.cgColor
}
}
然后旋转...
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。