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两种方式写出布局逻辑。
使用 frame
和 SnapKit
两种方式自定义 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
}
}
}
}
总结
以上代码展示了如何使用 frame
和 SnapKit
两种方式为 GridView
自定义布局。注意,布局逻辑非常依赖于 list
的内容和屏幕宽度,因此在实际应用中,你可能需要根据具体需求进行进一步调整。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具