frame和snapkit方式布局详情页主表功能

需求:

我现在要自定义GridView:UIView,外部通过gridview的属性list: [RowItem]传递数据,Struct RowItem { var name: string
var value: string
}现在有个需求,根据list遍历每一项item, 每一项item都创建两个label,label.text分别是item的name和value。但是label的布局有个要求:list第i项item对应的 两个label分别是nameLb和valueLb,i+1项item对应的是nextNameLb和nextValueLb。所有的nameLb/nextNameLb宽度必须为GridView宽度totalWidth的四分之一:w4,高度为自适应高度,由计算得出,假设分别是nameheight1和nameheight2。valueLb和nextValueLb宽度由文字单行占用宽度计算得到,假设分别是w1和w2。
每行高度最小值固定为minRowHeight:30。如果w1/w2都小于w4,则展示一行展示四个label(取四个label最大高度为行高并均设置此高度为四个label高度,然行数+1,并从i+2项开始遍历,跳过i+1项);否则一行展示两个label分别是nameLabel和valueLabel,高度同上取最大高度(然后行数+1,并遍历i+1项)。
请分别使用frame和snapkit两种方式写出布局逻辑。
使用 frameSnapKit 两种方式自定义 GridView 的布局逻辑。

1. 使用 frame 方式

import UIKit

struct RowItem {
    var name: String
    var value: String
}

class GridView: UIView {
    var list: [RowItem] = [] {
        didSet {
            layoutLabels()
        }
    }
    
    private let minRowHeight: CGFloat = 30
    private var totalWidth: CGFloat {
        return self.bounds.width
    }
    
    private func layoutLabels() {
        // 移除之前的子视图
        self.subviews.forEach { $0.removeFromSuperview() }
        
        let w4 = totalWidth / 4
        var currentY: CGFloat = 0
        
        for i in 0..<list.count {
            let item = list[i]
            let nameLabel = UILabel()
            nameLabel.text = item.name
            nameLabel.sizeToFit()
            var nameHeight = nameLabel.frame.height
            
            let valueLabel = UILabel()
            valueLabel.text = item.value
            valueLabel.sizeToFit()
            var valueWidth = valueLabel.frame.width
            
            // 计算出每个label的高度
            let maxHeight = max(nameHeight, minRowHeight)

            // 如果valueLabel宽度小于w4,进行相应处理
            if valueWidth < w4 {
                // 这行展示四个label
                let nextNameLabel = UILabel()
                nextNameLabel.text = (i + 1 < list.count) ? list[i + 1].name : ""
                nextNameLabel.sizeToFit()
                
                let nextValueLabel = UILabel()
                nextValueLabel.text = (i + 1 < list.count) ? list[i + 1].value : ""
                nextValueLabel.sizeToFit()

                let nextValueWidth = nextValueLabel.frame.width

                // 先设置nameLabel和valueLabel的frame
                nameLabel.frame = CGRect(x: 0, y: currentY, width: w4, height: maxHeight)
                valueLabel.frame = CGRect(x: w4, y: currentY, width: valueWidth, height: maxHeight)
                nextNameLabel.frame = CGRect(x: w4 + valueWidth, y: currentY, width: w4, height: maxHeight)
                nextValueLabel.frame = CGRect(x: w4 * 2 + valueWidth, y: currentY, width: nextValueWidth, height: maxHeight)

                // 将label添加到GridView
                self.addSubview(nameLabel)
                self.addSubview(valueLabel)
                self.addSubview(nextNameLabel)
                self.addSubview(nextValueLabel)

                // 跳过i+1项
                currentY += maxHeight
                i += 1
            } else {
                // 这行展示两个label
                nameLabel.frame = CGRect(x: 0, y: currentY, width: w4, height: maxHeight)
                valueLabel.frame = CGRect(x: w4, y: currentY, width: valueWidth, height: maxHeight)

                // 将label添加到GridView
                self.addSubview(nameLabel)
                self.addSubview(valueLabel)
                
                currentY += maxHeight
            }
        }
    }
}

2. 使用 SnapKit 方式

import UIKit
import SnapKit

struct RowItem {
    var name: String
    var value: String
}

class GridView: UIView {
    var list: [RowItem] = [] {
        didSet {
            layoutLabels()
        }
    }
    
    private let minRowHeight: CGFloat = 30
    private var totalWidth: CGFloat {
        return self.bounds.width
    }
    
    private func layoutLabels() {
        // 移除之前的子视图
        self.subviews.forEach { $0.removeFromSuperview() }
        
        let w4 = totalWidth / 4
        var currentY: CGFloat = 0
        
        for i in 0..<list.count {
            let item = list[i]
            let nameLabel = UILabel()
            nameLabel.text = item.name
            nameLabel.sizeToFit()
            var nameHeight = nameLabel.frame.height
            
            let valueLabel = UILabel()
            valueLabel.text = item.value
            valueLabel.sizeToFit()
            var valueWidth = valueLabel.frame.width
            
            // 计算出每个label的高度
            let maxHeight = max(nameHeight, minRowHeight)

            // 如果valueLabel宽度小于w4,进行相应处理
            if valueWidth < w4 {
                // 这行展示四个label
                let nextNameLabel = UILabel()
                nextNameLabel.text = (i + 1 < list.count) ? list[i + 1].name : ""
                nextNameLabel.sizeToFit()
                
                let nextValueLabel = UILabel()
                nextValueLabel.text = (i + 1 < list.count) ? list[i + 1].value : ""
                nextValueLabel.sizeToFit()

                // 设置label的约束
                nameLabel.snp.makeConstraints { make in
                    make.left.equalToSuperview()
                    make.top.equalToSuperview().offset(currentY)
                    make.width.equalTo(w4)
                    make.height.equalTo(maxHeight)
                }

                valueLabel.snp.makeConstraints { make in
                    make.left.equalTo(nameLabel.snp.right)
                    make.top.equalToSuperview().offset(currentY)
                    make.width.equalTo(valueWidth)
                    make.height.equalTo(maxHeight)
                }

                nextNameLabel.snp.makeConstraints { make in
                    make.left.equalTo(valueLabel.snp.right)
                    make.top.equalToSuperview().offset(currentY)
                    make.width.equalTo(w4)
                    make.height.equalTo(maxHeight)
                }

                nextValueLabel.snp.makeConstraints { make in
                    make.left.equalTo(nextNameLabel.snp.right)
                    make.top.equalToSuperview().offset(currentY)
                    make.width.equalTo(nextValueLabel.frame.width)
                    make.height.equalTo(maxHeight)
                }

                // 将label添加到GridView
                self.addSubview(nameLabel)
                self.addSubview(valueLabel)
                self.addSubview(nextNameLabel)
                self.addSubview(nextValueLabel)

                // 跳过i+1项
                currentY += maxHeight
                i += 1
            } else {
                // 这行展示两个label
                nameLabel.snp.makeConstraints { make in
                    make.left.equalToSuperview()
                    make.top.equalToSuperview().offset(currentY)
                    make.width.equalTo(w4)
                    make.height.equalTo(maxHeight)
                }

                valueLabel.snp.makeConstraints { make in
                    make.left.equalTo(nameLabel.snp.right)
                    make.top.equalToSuperview().offset(currentY)
                    make.width.equalTo(valueWidth)
                    make.height.equalTo(maxHeight)
                }

                // 将label添加到GridView
                self.addSubview(nameLabel)
                self.addSubview(valueLabel)

                currentY += maxHeight
            }
        }
    }
}

总结

以上代码展示了如何使用 frameSnapKit 两种方式为 GridView 自定义布局。注意,布局逻辑非常依赖于 list 的内容和屏幕宽度,因此在实际应用中,你可能需要根据具体需求进行进一步调整。

posted @   looxy  阅读(20)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示