iOS UICollectionView如何创建水平滚动的矩形布局,里面的项目大小不同。
我想用UICollectionView创建一个类似下面的矩形布局,如何实现?
当我滚动 横向 使用 采集视图 1,2,3,4,5,6格会一起滚动带来7。
下面是320*480的iPhone分辨率的尺寸。更新后的画面如下。
前6个项目的尺寸与iPhone 5s的尺寸相同。
Item 1 Size is - (213*148)
Item 2 Size is - (106*75)
Item 3 Size is - (106*74)
Item 4 Size is - (106*88)
Item 5 Size is - (106*88)
Item 6 Size is - (106*88)
第6项之后的尺寸与集合视图的宽度和高度相同,如下图。
Item 7 Size is - (320*237)
Item 8 Size is - (320*237)
Item 9 Size is - (320*237)
如何创建一个简单的自定义布局使用集合视图,有水平滚动?
必须感谢一个快速的解决方案。先谢谢了。
我建议在CollectionViewCell(固定尺寸)内使用StackView来创建一个网格布局,如你的帖子所示。
如下图 GridStackView
根据添加的视图数量,使用方法--------创建动态网格布局。addCell(view: UIView)
.
加此 GridStackView
作为你的CollectionViewCell的唯一子视图,将所有的边缘钉在边上,这样它就能完全填满CollectionViewCell。
在准备你的CollectionViewCell时,使用以下方法为它添加平铺视图 addCell(view: UIView)
.
如果只添加了一个视图,那么它将显示一个视图占据整个 GridStackView
如果添加了多个视图,它会自动将它们布局在CollectionViewCell内部。
你可以调整下面的代码,通过计算行和列来获得想要的布局。当前需要的实现 rowSize
在初始化的时候提供,我在我的一个项目中使用了,你需要修改一下以获得你想要的布局。
class GridStackView: UIStackView {
private var cells: [UIView] = []
private var currentRow: UIStackView?
var rowSize: Int = 3
var defaultSpacing: CGFloat = 5
init(rowSize: Int) {
self.rowSize = rowSize
super.init(frame: .zero)
translatesAutoresizingMaskIntoConstraints = false
axis = .vertical
spacing = defaultSpacing
distribution = .fillEqually
}
required init(coder: NSCoder) {
super.init(coder: coder)
translatesAutoresizingMaskIntoConstraints = false
axis = .vertical
spacing = defaultSpacing
distribution = .fillEqually
}
private func preapreRow() -> UIStackView {
let row = UIStackView(arrangedSubviews: [])
row.spacing = defaultSpacing
row.translatesAutoresizingMaskIntoConstraints = false
row.axis = .horizontal
row.distribution = .fillEqually
return row
}
func removeAllCell() {
for item in arrangedSubviews {
item.removeFromSuperview()
}
cells.removeAll()
currentRow = nil
}
func addCell(view: UIView) {
let firstCellInRow = cells.count % rowSize == 0
if currentRow == nil || firstCellInRow {
currentRow = preapreRow()
addArrangedSubview(currentRow!)
}
view.translatesAutoresizingMaskIntoConstraints = false
cells.append(view)
currentRow?.addArrangedSubview(view)
setNeedsLayout()
}
}
我已经尝试过用 马汉的回答 我得到了部分正确的输出。但问题是,index1有两个项目的全宽。
如何将索引1分割成索引1和索引2?
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
setUpCollectionView()
// Do any additional setup after loading the view.
}
func setUpCollectionView() {
self.view.backgroundColor = .white
let layout = UICollectionViewFlowLayout()
// layout.minimumInteritemSpacing = 1
// layout.minimumLineSpacing = 1
layout.scrollDirection = .horizontal
let collectionView = CollectionView(frame: .zero, collectionViewLayout: layout)
view.addSubview(collectionView)
collectionView.bounces = false
collectionView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
collectionView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
collectionView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
collectionView.heightAnchor.constraint(equalToConstant: 240).isActive = true
collectionView.translatesAutoresizingMaskIntoConstraints = false
}
}
class CollectionView: UICollectionView {
override init(frame: CGRect, collectionViewLayout layout: UICollectionViewLayout) {
super.init(frame: frame, collectionViewLayout: layout)
self.register(CollectionViewCell.self, forCellWithReuseIdentifier: CollectionViewCell.identifier)
self.dataSource = self
self.delegate = self
self.isPagingEnabled = true
}
required init?(coder: NSCoder) {
fatalError()
}
}
extension CollectionView: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 10
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: CollectionViewCell.identifier, for: indexPath) as! CollectionViewCell
cell.backgroundColor = .blue
cell.label.text = "\(indexPath.row)"
let row = indexPath.row
switch row {
case 0:
cell.backgroundColor = .red
case 1:
cell.backgroundColor = .blue
case 2:
cell.backgroundColor = .purple
case 3:
cell.backgroundColor = .orange
case 4:
cell.backgroundColor = .cyan
case 5:
cell.backgroundColor = .green
case 6:
cell.backgroundColor = .magenta
case 7:
cell.backgroundColor = .white
case 8:
cell.backgroundColor = .blue
case 9:
cell.backgroundColor = .green
default:
cell.backgroundColor = .systemPink
}
return cell
}
}
extension CollectionView: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let row = indexPath.row
let width = collectionView.frame.width
let other = width / 3
let height = collectionView.frame.height
let o_height = height / 3
switch row {
case 0:
return CGSize(width: other * 2, height: o_height * 2)
case 1:
return CGSize(width: other * 2, height: o_height)
case 2:
return CGSize(width: other, height: o_height)
case 3:
return CGSize(width: other, height: o_height)
case 4:
return CGSize(width: other, height: o_height)
case 5, 6, 7:
return CGSize(width: other, height: o_height)
default:
return CGSize(width: width, height: height)
}
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return .leastNormalMagnitude
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return .leastNormalMagnitude
}
}
class CollectionViewCell: UICollectionViewCell {
static let identifier = "CollectionViewCell"
let label = UILabel()
override init(frame: CGRect) {
super.init(frame: frame)
self.contentView.addSubview(label)
label.centerXAnchor.constraint(equalTo: contentView.centerXAnchor).isActive = true
label.centerYAnchor.constraint(equalTo: contentView.centerYAnchor).isActive = true
label.translatesAutoresizingMaskIntoConstraints = false
}
required init?(coder: NSCoder) {
fatalError()
}
}
如何把index1分成index1和index2,如下图所示?
先谢谢你
struct ItemData {
var color: [UIColor]
}
// NOTICE: 2nd item contains two colors and the rest one.
let data = [ItemData(color: [.red]), ItemData(color: [.blue, .purple]), ItemData(color: [.orange]),
ItemData(color: [.cyan]), ItemData(color: [.green]), ItemData(color: [.magenta]),
ItemData(color: [.systemPink]), ItemData(color: [.link]), ItemData(color: [.purple])]
class CollectionViewCellOne: UICollectionViewCell {
static let identifier = "CollectionViewCellOne"
var item: ItemData? {
didSet {
if let item = item {
self.leadingLabel.backgroundColor = item.color.first!
self.trailingLabel.backgroundColor = item.color.last!
}
}
}
let leadingLabel = UILabel()
let trailingLabel = UILabel()
override init(frame: CGRect) {
super.init(frame: frame)
self.contentView.addSubview(leadingLabel)
self.contentView.addSubview(trailingLabel)
let width = self.frame.width / 2
leadingLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor).isActive = true
leadingLabel.topAnchor.constraint(equalTo: contentView.topAnchor).isActive = true
leadingLabel.bottomAnchor.constraint(equalTo: contentView.bottomAnchor).isActive = true
leadingLabel.widthAnchor.constraint(equalToConstant: width).isActive = true
leadingLabel.translatesAutoresizingMaskIntoConstraints = false
trailingLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor).isActive = true
trailingLabel.topAnchor.constraint(equalTo: contentView.topAnchor).isActive = true
trailingLabel.bottomAnchor.constraint(equalTo: contentView.bottomAnchor).isActive = true
trailingLabel.widthAnchor.constraint(equalToConstant: width).isActive = true
trailingLabel.translatesAutoresizingMaskIntoConstraints = false
}
required init?(coder: NSCoder) {
fatalError()
}
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if indexPath.row == 1 {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: CollectionViewCellOne.identifier, for: indexPath) as! CollectionViewCellOne
cell.item = data[indexPath.row]
return cell
} else {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: CollectionViewCell.identifier, for: indexPath) as! CollectionViewCell
if let color = data[indexPath.row].color.first {
cell.backgroundColor = color
}
return cell
}
}