EurekaCommunity / ViewRow

ViewRow is a Eureka row that allows you to display any UIView (or UIView sunclass) within a Eureka row. Views can be created in code or loaded from nib files.
MIT License
77 stars 16 forks source link

How to implement UICollectionView #21

Closed teawithfruit closed 3 years ago

teawithfruit commented 5 years ago

Hello. Thanks for publsihing this repo!

I have a question about implementing UICollectionView. First my current code.

let section = Section("Images")

section <<< ViewRow<LeadFormularImageCollectionView>("LeadFormularImageCollectionView")
.cellSetup { (cell, row) in
  cell.view = LeadFormularImageCollectionView()
}

form +++
  section
import Foundation
import UIKit
import PinLayout

class LeadFormularImageCollectionView: UIView {
    private let button = UIButton(type: .system)

    private let collectionView: UICollectionView!
    private let flowLayout = UICollectionViewFlowLayout()
    var data: [Int] = Array(0..<30)
    var height:CGFloat = 0.0

    init() {
        self.collectionView = UICollectionView(frame: .zero, collectionViewLayout: flowLayout)

        super.init(frame: .zero)
        self.initialize()
    }

    private func initialize() {
        self.setup()
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    func setup() {
        flowLayout.minimumLineSpacing = 10
        flowLayout.minimumInteritemSpacing = 0

        self.collectionView!.dataSource = self
        self.collectionView!.delegate = self
        self.collectionView!.register(LeadFormularImageCollectionViewCell.self, forCellWithReuseIdentifier: LeadFormularImageCollectionViewCell.identifier)
        self.collectionView!.backgroundColor = .white
        self.collectionView!.isScrollEnabled = false
        addSubview(self.collectionView!)

        button.setTitle("Capture", for: .normal)
        button.setTitleColor(.white, for: .normal)
        button.backgroundColor = self.useful.hexStringToUIColor(hex: "#1C9AFF")
        button.frame = CGRect(x: 0, y: 0, width: 150, height: 50)
        button.addTarget(self, action: #selector(self.capture(_:)), for: .touchUpInside)
        addSubview(self.button)

        self.reset()
    }

    @objc func capture(_ sender:UIButton!) {
      print("capture")
    }

    override func layoutSubviews() {
        super.layoutSubviews()

        self.reset()
    }

    func reset() {
        let rows:CGFloat = CGFloat(self.data.count) / (CGFloat(UIScreen.main.bounds.size.width) / CGFloat(100))
        self.height = CGFloat(rows.rounded(.up)) * 110

        // Here I've used https://github.com/layoutBox/PinLayout 
        self.collectionView!.pin.height(self.height).left(self.pin.safeArea).right(self.pin.safeArea)
        self.button.pin.below(of: self.collectionView!).marginTop(10)
        self.pin.height(self.height + 60).sizeToFit(.height)

        self.frame.size.height = self.height + 60
    }

}

extension LeadFormularImageCollectionView: UICollectionViewDelegateFlowLayout, UICollectionViewDataSource, UICollectionViewDelegate {
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return self.data.count
    }

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: LeadFormularImageCollectionViewCell.identifier, for: indexPath) as! LeadFormularImageCollectionViewCell
        let data = self.data[indexPath.item]

        return cell
    }

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        return CGSize(width: 100, height: 100)
    }

    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {

    }
}

The problem is, that the height of the view which holds the UICollectionView have always the wrong height, if I change the device orientation. It seems to me, that the calculation is always one step behind. That means, that your layoutSubviews function is faster than mine and that the height of the ViewRow will not fit the content. Do you have an idea to fix that? Or an idea for something I can do to set the right height?

How would you implement an UICollectionView with dynamic height?

Thanks

alldritt commented 5 years ago

Here's a solution: force the row to resize its self in your reset() function. Here's how:

  1. Add variables holding references to the row and form in your LeadFormularImageCollectionView class:
    weak var row: ViewRow<LeadFormularImageCollectionView>? = nil
    weak var form: ViewViewController? = nil
  1. Assign these when you are building the row:
    <<< ViewRow<LeadFormularImageCollectionView>("LeadFormularImageCollectionView")
    .cellSetup { (cell, row) in
        cell.view = LeadFormularImageCollectionView()
        cell.view!.row = row
        cell.view!.form = self
    }
  1. Force the row to resize in your reset() function:
    func reset() {
        print("UIScreen.main.bounds: \(UIScreen.main.bounds)")
        let rows:CGFloat = CGFloat(self.data.count) / (CGFloat(UIScreen.main.bounds.size.width) / CGFloat(100))
        self.height = CGFloat(rows.rounded(.up)) * 110
        print("height: \(self.height)")

        // Here I've used https://github.com/layoutBox/PinLayout
        self.collectionView!.pin.height(self.height).left(self.pin.safeArea).right(self.pin.safeArea)
        self.button.pin.below(of: self.collectionView!).marginTop(10)
        self.pin.height(self.height + 60).sizeToFit(.height)

        self.frame.size.height = self.height + 60

        //  Force the row to resize its self.
        if let form = form, let row = row { form.tableView.reloadRows(at: [row.indexPath!], with: .none) }
    }