iOS Swift - 固定高度的 CollectionView 或 TableView 水平扩展..有什么想法吗?
iOS Swift - CollectionView or TableView with fixed height that expands horizontally .. Any Idea?
亲爱的开发者您好!
我希望有人能帮助我。
我正在寻找一种方法来创建具有固定高度但动态宽度的 TableView(即具有动态部分和行的列表)..
示例:
TableView(或 CollectionView)的高度为 600px,假设 10 行适合该高度。第11行现在应该不在下面了,继续往右..
下面是图片。
真心希望有人能帮帮我..在此先感谢您的阅读。
PS: 我请有解决方案的人喝杯咖啡:b
->>> I think the picture shows clearly what I mean <<<-
这可以很好地 straight-forward 作为具有水平流布局的 UICollectionView
。
- 创建一个单元格布局作为“Header”单元格。
- 创建第二个单元格布局作为“详细信息”单元格。
- 将您的数据安排为一个部分,数据元素标识为“header”或“详细信息”
快速示例:
enum MyCellType: Int {
case header
case detail
}
struct MyStruct {
var type: MyCellType = .header
var headerString: String = ""
var detailString: String = ""
var detailValue: String = ""
}
class TestViewController: UIViewController {
var myData: [MyStruct] = []
var collectionView: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemBlue
// some sample data
let headers: [String] = [
"SOFTWARE", "BATTERY", "DEVICE", "HARDWARE", "MLB", "USAGE",
]
let counts: [Int] = [
4, 2, 7, 5, 1, 8,
]
var val: Int = 1
for (i, s) in headers.enumerated() {
let t: MyStruct = MyStruct(type: .header, headerString: s, detailString: "", detailValue: "")
myData.append(t)
for j in 1...counts[i % counts.count] {
let t: MyStruct = MyStruct(type: .detail, headerString: "", detailString: "Detail \(i),\(j)", detailValue: "Val: \(val)")
myData.append(t)
val += 1
}
}
let fl = UICollectionViewFlowLayout()
fl.scrollDirection = .horizontal
fl.minimumLineSpacing = 12
fl.minimumInteritemSpacing = 0
fl.itemSize = CGSize(width: 200, height: 30)
collectionView = UICollectionView(frame: .zero, collectionViewLayout: fl)
collectionView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(collectionView)
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
collectionView.topAnchor.constraint(equalTo: g.topAnchor, constant: 20.0),
collectionView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
collectionView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
collectionView.heightAnchor.constraint(equalToConstant: 300.0),
])
collectionView.register(MyHeaderCell.self, forCellWithReuseIdentifier: "headerCell")
collectionView.register(MyDetailCell.self, forCellWithReuseIdentifier: "detailCell")
collectionView.dataSource = self
collectionView.delegate = self
}
}
extension TestViewController: UICollectionViewDataSource, UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let t = myData[indexPath.item]
if t.type == .header {
let c = collectionView.dequeueReusableCell(withReuseIdentifier: "headerCell", for: indexPath) as! MyHeaderCell
c.label.text = t.headerString
return c
}
let c = collectionView.dequeueReusableCell(withReuseIdentifier: "detailCell", for: indexPath) as! MyDetailCell
c.detailLabel.text = t.detailString
c.valueLabel.text = t.detailValue
// we don't want to show the "separator line" if the next
// item is a "header"
if indexPath.item < myData.count - 1 {
c.hideSepLine(myData[indexPath.item + 1].type == .header)
}
return c
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return myData.count
}
}
class MyHeaderCell: UICollectionViewCell {
let label = UILabel()
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() {
label.translatesAutoresizingMaskIntoConstraints = false
label.font = .systemFont(ofSize: 10, weight: .bold)
contentView.addSubview(label)
contentView.backgroundColor = UIColor(white: 0.9, alpha: 1.0)
let g = contentView.layoutMarginsGuide
NSLayoutConstraint.activate([
label.topAnchor.constraint(equalTo: g.topAnchor, constant: 0.0),
label.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 0.0),
label.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: 0.0),
label.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: 0.0),
])
}
}
class MyDetailCell: UICollectionViewCell {
let detailLabel = UILabel()
let valueLabel = UILabel()
let sepLineView = UIView()
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() {
detailLabel.font = .systemFont(ofSize: 12.0, weight: .light)
valueLabel.font = .systemFont(ofSize: 12.0, weight: .light)
valueLabel.textColor = .gray
sepLineView.backgroundColor = .lightGray
[detailLabel, valueLabel, sepLineView].forEach { v in
v.translatesAutoresizingMaskIntoConstraints = false
contentView.addSubview(v)
}
let g = contentView.layoutMarginsGuide
NSLayoutConstraint.activate([
detailLabel.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 16.0),
detailLabel.centerYAnchor.constraint(equalTo: g.centerYAnchor),
valueLabel.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: 0.0),
valueLabel.centerYAnchor.constraint(equalTo: g.centerYAnchor),
sepLineView.leadingAnchor.constraint(equalTo: detailLabel.leadingAnchor),
sepLineView.trailingAnchor.constraint(equalTo: valueLabel.trailingAnchor),
sepLineView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
sepLineView.heightAnchor.constraint(equalToConstant: 1.0),
])
}
func hideSepLine(_ b: Bool) {
sepLineView.isHidden = b
}
}
结果:
向左滚动一点:
一直滚动到左边:
亲爱的开发者您好!
我希望有人能帮助我。
我正在寻找一种方法来创建具有固定高度但动态宽度的 TableView(即具有动态部分和行的列表)..
示例: TableView(或 CollectionView)的高度为 600px,假设 10 行适合该高度。第11行现在应该不在下面了,继续往右..
下面是图片。
真心希望有人能帮帮我..在此先感谢您的阅读。
PS: 我请有解决方案的人喝杯咖啡:b
->>> I think the picture shows clearly what I mean <<<-
这可以很好地 straight-forward 作为具有水平流布局的 UICollectionView
。
- 创建一个单元格布局作为“Header”单元格。
- 创建第二个单元格布局作为“详细信息”单元格。
- 将您的数据安排为一个部分,数据元素标识为“header”或“详细信息”
快速示例:
enum MyCellType: Int {
case header
case detail
}
struct MyStruct {
var type: MyCellType = .header
var headerString: String = ""
var detailString: String = ""
var detailValue: String = ""
}
class TestViewController: UIViewController {
var myData: [MyStruct] = []
var collectionView: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemBlue
// some sample data
let headers: [String] = [
"SOFTWARE", "BATTERY", "DEVICE", "HARDWARE", "MLB", "USAGE",
]
let counts: [Int] = [
4, 2, 7, 5, 1, 8,
]
var val: Int = 1
for (i, s) in headers.enumerated() {
let t: MyStruct = MyStruct(type: .header, headerString: s, detailString: "", detailValue: "")
myData.append(t)
for j in 1...counts[i % counts.count] {
let t: MyStruct = MyStruct(type: .detail, headerString: "", detailString: "Detail \(i),\(j)", detailValue: "Val: \(val)")
myData.append(t)
val += 1
}
}
let fl = UICollectionViewFlowLayout()
fl.scrollDirection = .horizontal
fl.minimumLineSpacing = 12
fl.minimumInteritemSpacing = 0
fl.itemSize = CGSize(width: 200, height: 30)
collectionView = UICollectionView(frame: .zero, collectionViewLayout: fl)
collectionView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(collectionView)
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
collectionView.topAnchor.constraint(equalTo: g.topAnchor, constant: 20.0),
collectionView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
collectionView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
collectionView.heightAnchor.constraint(equalToConstant: 300.0),
])
collectionView.register(MyHeaderCell.self, forCellWithReuseIdentifier: "headerCell")
collectionView.register(MyDetailCell.self, forCellWithReuseIdentifier: "detailCell")
collectionView.dataSource = self
collectionView.delegate = self
}
}
extension TestViewController: UICollectionViewDataSource, UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let t = myData[indexPath.item]
if t.type == .header {
let c = collectionView.dequeueReusableCell(withReuseIdentifier: "headerCell", for: indexPath) as! MyHeaderCell
c.label.text = t.headerString
return c
}
let c = collectionView.dequeueReusableCell(withReuseIdentifier: "detailCell", for: indexPath) as! MyDetailCell
c.detailLabel.text = t.detailString
c.valueLabel.text = t.detailValue
// we don't want to show the "separator line" if the next
// item is a "header"
if indexPath.item < myData.count - 1 {
c.hideSepLine(myData[indexPath.item + 1].type == .header)
}
return c
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return myData.count
}
}
class MyHeaderCell: UICollectionViewCell {
let label = UILabel()
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() {
label.translatesAutoresizingMaskIntoConstraints = false
label.font = .systemFont(ofSize: 10, weight: .bold)
contentView.addSubview(label)
contentView.backgroundColor = UIColor(white: 0.9, alpha: 1.0)
let g = contentView.layoutMarginsGuide
NSLayoutConstraint.activate([
label.topAnchor.constraint(equalTo: g.topAnchor, constant: 0.0),
label.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 0.0),
label.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: 0.0),
label.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: 0.0),
])
}
}
class MyDetailCell: UICollectionViewCell {
let detailLabel = UILabel()
let valueLabel = UILabel()
let sepLineView = UIView()
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() {
detailLabel.font = .systemFont(ofSize: 12.0, weight: .light)
valueLabel.font = .systemFont(ofSize: 12.0, weight: .light)
valueLabel.textColor = .gray
sepLineView.backgroundColor = .lightGray
[detailLabel, valueLabel, sepLineView].forEach { v in
v.translatesAutoresizingMaskIntoConstraints = false
contentView.addSubview(v)
}
let g = contentView.layoutMarginsGuide
NSLayoutConstraint.activate([
detailLabel.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 16.0),
detailLabel.centerYAnchor.constraint(equalTo: g.centerYAnchor),
valueLabel.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: 0.0),
valueLabel.centerYAnchor.constraint(equalTo: g.centerYAnchor),
sepLineView.leadingAnchor.constraint(equalTo: detailLabel.leadingAnchor),
sepLineView.trailingAnchor.constraint(equalTo: valueLabel.trailingAnchor),
sepLineView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
sepLineView.heightAnchor.constraint(equalToConstant: 1.0),
])
}
func hideSepLine(_ b: Bool) {
sepLineView.isHidden = b
}
}
结果:
向左滚动一点:
一直滚动到左边: