滚动时,单元格高度计算不正确?

问题描述 投票:0回答:1

大家下午好。我有一个单元格高度可变的集合。滚动集合时,内容的高度与单元格的高度不匹配。告诉我如何解决这个问题?

这就是我创建集合的方式。

// MARK: - UI Fabric.
private extension CollectionViewController {
    func createCollectionView() -> UICollectionView {
        let layout = UICollectionViewFlowLayout()
        layout.minimumInteritemSpacing = 16
        layout.minimumLineSpacing = 16
        layout.scrollDirection = .vertical
        let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
        collectionView.showsVerticalScrollIndicator = false
        collectionView.translatesAutoresizingMaskIntoConstraints = false

        return collectionView
    }
}

这是代表或设置大小单元格。

// MARK: - CollectionView Flow Layout.
extension CollectionViewController: UICollectionViewDelegateFlowLayout {
    /// Setting cell sizes.
    func collectionView(
        _ collectionView: UICollectionView,
        layout collectionViewLayout: UICollectionViewLayout,
        sizeForItemAt indexPath: IndexPath
    ) -> CGSize {
        delegateSettingCell.calculateCellSize(screenWidth: view.bounds.width, index: indexPath.row)
    }
}

这里我正在计算像元大小。

    func calculateCellSize(screenWidth: CGFloat, index: Int) -> CGSize {
        let item = modelForDisplay[index].addonDetail.count
        let heightCell = CGFloat((item * 44) + 44)
        let sizeCellWidth = (view.bounds.width - 32)
        return CGSize(width: sizeCellWidth, height: heightCell)
    }

enter image description here

enter image description here

我通过单击一个单元格来显示尺寸。并在滚动后。大小不会改变。 enter image description here


final class AddonCreatorCell: UICollectionViewCell {

    // MARK: - Dependencies
    var delegate: IHandlerAddonCreatorCellDelegate?

    // MARK: - Public properties
    static var reuseIdentifier: String = "AddonCreatorCell.cell"

    // MARK: - Private properties
    private lazy var textFields: [UITextField] = []
    private lazy var viewDie = createView()
    private lazy var gradientViewDie = createGradient(GradientColors.yellowGradient)
    private lazy var labelTitle = createUILabel()
    private lazy var switchShow = createSwitch()
    private lazy var vStack = createStack()
    private lazy var headerStack = createStack()

    // MARK: - Initializator
    override init(frame: CGRect) {
        super.init(frame: frame)
        addUIView()
        setupConfiguration()
        setupLayout()
    }

    convenience init(handlerAddonCreatorCellDelegate: IHandlerAddonCreatorCellDelegate?) {
        self.init(frame: CGRect.zero)
        self.delegate = handlerAddonCreatorCellDelegate
    }

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


    // MARK: - Public methods
    func reloadFrame(size: CGSize) {
        self.gradientViewDie.frame.size = size

    }

    func reloadData(model: CreateAddon) {
    // Here I delete unnecessary UIStask with content
        vStack.subviews.forEach {
            if $0 != headerStack {
                $0.removeFromSuperview()
            }
        }

        labelTitle.text = "\(model.title) \(model.addonDetail.count)"

        model.isActive ? activeSwitch() : deactivateSwitch()
        switchShow.setOn(model.isActive, animated: false)

        for (index, element) in model.addonDetail.enumerated() {
            createContent(item: element, index: index)
        }
    }

    private func createContent(item: AddonDetail, index: Int) {
        let convertToString = String(item.count)
        let label = createUILabel()
        let textField = createTextField(id: index, text: convertToString)
        let hStack = createStack()

        label.text = item.title
        textFields.append(textField)
        textField.delegate = self
        textField.addTarget(self, action: #selector(self.myTextFieldChanged(_:)), for: .editingChanged)

        hStack.axis = .horizontal
        hStack.alignment = .center
        hStack.distribution = .fillProportionally
        hStack.addArrangedSubview(label)
        hStack.addArrangedSubview(textField)
        vStack.addArrangedSubview(hStack)
        addConstraintTextField(textField)
    }
}

swift uicollectionview uikit
1个回答
0
投票

您不需要每次都“重新构建”可重复使用的单元...

将堆栈视图视为“行”

如果您知道任何单个单元格将拥有的 max 行数,请在单元格的 init 中创建它们。

然后,当您在

cellForItemAt
中设置数据时,根据需要显示/隐藏“行”。像这样的东西:

    // show used "rows" and hide unused "rows"
    for (i, v) in vStack.arrangedSubviews.enumerated() {
        v.isHidden = !(i < model.addons.count)
    }

如果您不知道潜在的 max 行数,请根据需要创建它们(同样,在

cellForItemAt
中设置单元格数据时:

    // if we have fewer "rows" than addons, create new "rows"
    while vStack.arrangedSubviews.count < model.addons.count {
        // add a new textfield & switch
        vStack.addArrangedSubview(...)
    }
    // show used "rows" and hide unused "rows"
    for (i, v) in vStack.arrangedSubviews.enumerated() {
        v.isHidden = !(i < model.addons.count)
    }

当排列子视图被隐藏时,它仍然存在,但堆栈视图将其视为不存在。

这是一个完整的示例 - 大致基于您问题中的代码:

struct Addon {
    var text: String = ""
    var selected: Bool = false
}
struct CreateAddon {
    var title: String = ""
    var addons: [Addon] = []
}
class CollectionViewController: UIViewController {

    var myData: [CreateAddon] = []
    
    var collectionView: UICollectionView!
    
    override func viewDidLoad() {
        super.viewDidLoad()

        collectionView = createCollectionView()
        collectionView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(collectionView)
        let g = view.safeAreaLayoutGuide
        NSLayoutConstraint.activate([
            collectionView.topAnchor.constraint(equalTo: g.topAnchor, constant: 0.0),
            collectionView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 0.0),
            collectionView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: 0.0),
            collectionView.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: 0.0),
        ])
        
        collectionView.register(AddonCreatorCell.self, forCellWithReuseIdentifier: AddonCreatorCell.reuseIdentifier)
        collectionView.dataSource = self
        collectionView.delegate = self
        
        view.backgroundColor = .init(red: 0.108, green: 0.379, blue: 0.129, alpha: 1.0)
        collectionView.backgroundColor = .init(red: 0.108, green: 0.379, blue: 0.129, alpha: 1.0)

        // create some sample data
        let numRows: [Int] = [3, 5, 2, 6, 4, 3, 3, 4, 7, 5, 3, 4, 6, 5, 6, 2]
        for (row, n) in numRows.enumerated() {
            var theseAddons: [Addon] = []
            for i in 0..<n {
                let a: Addon = Addon(text: "User Text \(i)", selected: false)
                theseAddons.append(a)
            }
            let cr = CreateAddon(title: "Addon Title \(row)", addons: theseAddons)
            myData.append(cr)
        }
        
    }
    
}
extension CollectionViewController: UICollectionViewDataSource, UICollectionViewDelegate {
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return myData.count
    }
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let c = collectionView.dequeueReusableCell(withReuseIdentifier: AddonCreatorCell.reuseIdentifier, for: indexPath) as! AddonCreatorCell
        c.fillData(model: myData[indexPath.item])
        // cell's "die" view width -- it has 8-points "spacing" on each side
        c.wConstraint.constant = collectionView.frame.width - 16.0
        return c
    }
}

private extension CollectionViewController {
    func createCollectionView() -> UICollectionView {
        let layout = UICollectionViewFlowLayout()
        layout.estimatedItemSize = .init(width: 300.0, height: 50.0)
        layout.minimumInteritemSpacing = 16
        layout.minimumLineSpacing = 16
        layout.scrollDirection = .vertical
        let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
        collectionView.showsVerticalScrollIndicator = false
        return collectionView
    }
}

class TextFieldSwitch: UIView {
    
    private let theTextField = UITextField()
    private let theSwitch = UISwitch()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }
    private func commonInit() {
        let sv = UIStackView()
        sv.spacing = 8
        sv.addArrangedSubview(theTextField)
        sv.addArrangedSubview(theSwitch)
        self.addSubview(sv)
        sv.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            sv.topAnchor.constraint(equalTo: self.topAnchor, constant: 0.0),
            sv.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 0.0),
            sv.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: 0.0),
            sv.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: 0.0),
        ])
        theTextField.borderStyle = .bezel
        // make sure the switch doesn't stretch
        theSwitch.setContentHuggingPriority(.required, for: .horizontal)
    }
    public func fillData(d: Addon) {
        theTextField.text = d.text
        theSwitch.isOn = d.selected
    }
    
}
final class AddonCreatorCell: UICollectionViewCell {
    
    // we'll use this to make the cells the width of the collection view
    public var wConstraint: NSLayoutConstraint!
    
    // MARK: - Dependencies
    //var delegate: IHandlerAddonCreatorCellDelegate?
    
    // MARK: - Public properties
    static var reuseIdentifier: String = "AddonCreatorCell.cell"
    
    // MARK: - Private properties
    private let viewDie = AddonGradientView()
    private let labelTitle = UILabel() // createUILabel()
    private let vStack = UIStackView() // createStack()
    
    // MARK: - Initializator
    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }
    private func commonInit() {
        vStack.axis = .vertical
        vStack.spacing = 4
        
        for v in [viewDie, labelTitle, vStack] {
            v.translatesAutoresizingMaskIntoConstraints = false
            contentView.addSubview(v)
        }
        
        let g = contentView
        
        // this will be updated in cellForItemAt
        //  it will keep the cells the full width of the collection view
        wConstraint = viewDie.widthAnchor.constraint(equalToConstant: 300.0)
        
        let bConstraint = vStack.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: -20.0)
        
        // giving these two constraints less-than-required priority avoids auto-layout complaints
        wConstraint.priority = .required - 1
        bConstraint.priority = .required - 1
        
        NSLayoutConstraint.activate([

            viewDie.topAnchor.constraint(equalTo: g.topAnchor, constant: 4.0),
            viewDie.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 8.0),
            viewDie.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -8.0),
            viewDie.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: -4.0),

            labelTitle.topAnchor.constraint(equalTo: g.topAnchor, constant: 16.0),
            labelTitle.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
            labelTitle.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),

            vStack.topAnchor.constraint(equalTo: labelTitle.bottomAnchor, constant: 8.0),
            vStack.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
            vStack.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
            bConstraint,

            wConstraint,
            
        ])
        
        viewDie.colors = [
            .init(red: 0.918, green: 0.990, blue: 0.325, alpha: 1.0),
            .init(red: 0.824, green: 0.894, blue: 0.242, alpha: 1.0),
            .init(red: 0.918, green: 0.990, blue: 0.325, alpha: 1.0),
        ]
        
        viewDie.layer.cornerRadius = 16
    }
    
    
    // MARK: - Public methods
    
    func fillData(model: CreateAddon) {
        // if we have fewer "rows" than addons, create new "rows"
        while vStack.arrangedSubviews.count < model.addons.count {
            vStack.addArrangedSubview(TextFieldSwitch())
        }
        // configure "rows"
        for (row, addon) in zip(vStack.arrangedSubviews, model.addons) {
            if let v = row as? TextFieldSwitch {
                v.fillData(d: addon)
            }
            
        }
        // show used "rows" and hide unused "rows"
        for (i, v) in vStack.arrangedSubviews.enumerated() {
            v.isHidden = !(i < model.addons.count)
        }
        labelTitle.text = model.title
    }
    
}

class AddonGradientView: UIView {
    
    public var colors: [UIColor] = [.gray, .white] {
        didSet {
            gradientLayer.colors = colors.map { $0.cgColor }
        }
    }
    
    override class var layerClass: AnyClass { CAGradientLayer.self }
    private var gradientLayer: CAGradientLayer { layer as! CAGradientLayer }
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }
    private func commonInit() {
        self.backgroundColor = .clear
        gradientLayer.colors = colors.map { $0.cgColor }
    }
    
}

看起来像这样:

enter image description here

enter image description here

注意:当用户在文本字段中键入或切换开关时,我没有实现任何数据更新......我将把它留给你。

© www.soinside.com 2019 - 2024. All rights reserved.