iOS UICollectionView Horizontal Scrolling Rectangular Layout with different size of items?

别说谁变了你拦得住时间么 提交于 2020-07-09 11:57:46

问题


iOS UICollectionView how to Create Horizontal Scrolling rectangular layout with different size of items inside.

I want to create a Rectangular layout using UICollectionView like below. how can i achieve?

When i scroll horizontally using CollectionView 1,2,3,4,5,6 grid will scroll together to bring 7.

The Below are the dimensions of 320*480 iPhone Resolution. Updated Screen below.

First 6 items have below dimensions for 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)

After item6 have same dimensions as collection View width and height like below.

Item 7 Size is - (320*237)
Item 8 Size is - (320*237)
Item 9 Size is - (320*237)

How to create a simple custom Layout Using collection view, that has horizontal scrolling?

Must appreciate for a quick solution. Thanks in advance.


回答1:


I would suggest using a StackView inside CollectionViewCell(of fixed dimension) to create a grid layout as shown in your post.

Below GridStackView creates a dynamic grid layout based on the number of views added using method addCell(view: UIView).

Add this GridStackView as the only subview of your CollectionViewCell pinning all the edges to the sides so that it fills the CollectionViewCell completely.

while preparing your CollectionViewCell, add tile views to it using the method addCell(view: UIView).

If only one view added, then it will show a single view occupying whole GridStackView and so as whole CollectionViewCell. If there is more than one view added, it will automatically layout them in the inside the CollectionViewCell.

You can tweak the code below to get the desired layout calculating the row and column. Current implementation needed rowSize to be supplied while initializing which I used for one of my project, you need to modify it a bit to get your desired layout.

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




回答2:


I have tried with Mahan's Answer and i am getting the partially Correct output. But the issue is, index1 having full width of two items.

How to split index 1 into index1 and Index2?

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

How to devide index 1 into index1 and Index2 like below?

Thanks in advance!




回答3:


  • Create a new cell that contains two views. Views have equal width.
  • Contstruct your data accordingly

Data

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])] 

Cell

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

dequeueReusableCell

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


来源:https://stackoverflow.com/questions/62130759/ios-uicollectionview-horizontal-scrolling-rectangular-layout-with-different-size

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!