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
    }
}

结果:

向左滚动一点:

一直滚动到左边: