没有情节提要的自定义单元格中自定义 UITableViewCell 内的多个 UIStackViews 无法正常工作
Multiple UIStackViews inside a custom UITableViewCell in Custom Cell without Storyboard not working
我目前正在我的应用程序中构建一个屏幕,它基本上是一个包含 3 个部分的长 UITableView,每个部分都有不同数量的独特自定义单元格。设置 tableview 工作正常,我在单元格中添加了一些随机文本以确保每个单元格都被正确调用和定位。我已经从我的项目中完全删除了我的故事板,因为它会由于某些原因导致以后出现问题。所以我不能通过故事板做任何事情。
下一步是构建自定义单元格。作为初学者,其中一些对我来说相当复杂。这是我的一个手机:
我想将单元格拆分为多个 UIStackView,一个用于图片和名称,一个用于右侧的统计信息,后者本身将包含两个堆栈视图,其中包含两行统计信息中的每一行。然后,其中每一个都可以包含另一个嵌入式堆栈视图,其中包含两个 uiLabel、编号和描述。最重要的是一个切换按钮。
我似乎无法理解如何定义这一切。正如我所说,我定义了 Tableview 并在我的 cellForRowAt
中调用正确的单元格,如下所示:
if indexPath.row == 0 && indexPath.section == 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: StatsOverViewCell.identifier, for: indexPath) as! StatsOverViewCell
cell.configure()
return cell
} else if ...
我已经为每个单元格创建了文件,其中之一是 StatsOverViewCell。
在这个文件中,我有一个与 class 同名的标识符。
我还添加了我从我的 tableview 调用的配置函数,我用来在单元格内布置视图的 layoutSubviews 函数,我已经初始化了我需要的每个标签和图像。我已将文件缩减为几个示例以节省您一些时间:
class StatsOverViewCell: UITableViewCell {
//Set identifier to be able to call it later on
static let identifier = "StatsOverViewCell"
let myProfileStackView = UIStackView()
let myImageView = UIImageView()
let myName = UILabel()
let myGenderAndAge = UILabel()
let myStatsStackView = UIStackView()
let oneView = UIStackView()
let oneStat = UILabel()
let oneLabel = UILabel()
let twoStackView = UIStackView()
let twoStat = UILabel()
let twoLabel = UILabel()
//Do this for each of the labels I have in the stats
public func configure() {
myImageView.image = UIImage(named: "person-icon")
myName.frame = CGRect(x: 0, y: 0, width: 100, height: 100)
myImageView.contentMode = .scaleAspectFill
myName.text = "Name."
myName.frame = CGRect(x: 0, y: 0, width: 100, height: 100)
myName.textAlignment = .center
//Add the Name label to the stackview
myProfileStackView.addArrangedSubview(myName)
myProfileStackView.addArrangedSubview(myImageView)
myName.centerXAnchor.constraint(equalTo: myProfileStackView.centerXAnchor).isActive = true
oneStat.text = "5.187"
oneStat.font = UIFont(name: "montserrat", size: 18)
oneLabel.text = "Text"
oneLabel.font = UIFont(name: "montserrat", size: 14)
}
//Layout in the cell
override func layoutSubviews() {
super.layoutSubviews()
contentView.backgroundColor = Utilities.hexStringToUIColor(hex: "#3F454B")
contentView.layer.borderWidth = 1
//Stackview
contentView.addSubview(myProfileStackView)
myProfileStackView.axis = .vertical
myProfileStackView.distribution = .equalSpacing
myProfileStackView.spacing = 3.5
myProfileStackView.backgroundColor = .red
myProfileStackView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 23).isActive = true
myProfileStackView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 76).isActive = true
}
如您所见,我在 tableview 中创建单元格时调用的配置方法中将所有 arrangedsubview 添加到 stackview。然后我在布局子视图中设置堆栈视图约束。我没有收到任何错误或任何东西。但是单元格显示完全是空的。
我觉得我忘记了什么,或者我不明白如何使用 uistackviews 创建单元格。我应该在哪里创建 stackviews,我应该在哪里将 arrangedsubviews 添加到这个 stackviews 以及我在 LayoutSubviews 中做什么?
我将非常感谢任何见解。
感谢您的宝贵时间!
你做错了几件事...
- 您的 UI 元素应该在 init 中创建和配置,而不是在
configure()
或 layoutSubviews()
中
- 您需要完整的约束才能为您的元素提供正确的布局
看看我对你的手机所做的更改class。它应该让你上路:
class StatsOverViewCell: UITableViewCell {
//Set identifier to be able to call it later on
static let identifier = "StatsOverViewCell"
let myProfileStackView = UIStackView()
let myImageView = UIImageView()
let myName = UILabel()
let myGenderAndAge = UILabel()
let myStatsStackView = UIStackView()
let oneView = UIStackView()
let oneStat = UILabel()
let oneLabel = UILabel()
let twoStackView = UIStackView()
let twoStat = UILabel()
let twoLabel = UILabel()
//Do this for each of the labels I have in the stats
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() {
myImageView.image = UIImage(named: "person-icon")
// frame doesn't matter - stack view arrangedSubvies automatically
// set .translatesAutoresizingMaskIntoConstraints = false
//myName.frame = CGRect(x: 0, y: 0, width: 100, height: 100)
myImageView.contentMode = .scaleAspectFill
myName.text = "Name."
myName.textAlignment = .center
//Add the Name label to the stackview
myProfileStackView.addArrangedSubview(myName)
myProfileStackView.addArrangedSubview(myImageView)
// no need for this
//myName.centerXAnchor.constraint(equalTo: myProfileStackView.centerXAnchor).isActive = true
oneStat.text = "5.187"
oneStat.font = UIFont(name: "montserrat", size: 18)
oneLabel.text = "Text"
oneLabel.font = UIFont(name: "montserrat", size: 14)
contentView.backgroundColor = Utilities.hexStringToUIColor(hex: "#3F454B")
contentView.layer.borderWidth = 1
//Stackview
contentView.addSubview(myProfileStackView)
myProfileStackView.axis = .vertical
// no need for equalSpacing if you're explicitly setting the spacing
//myProfileStackView.distribution = .equalSpacing
myProfileStackView.spacing = 3.5
myProfileStackView.backgroundColor = .red
// stack view needs .translatesAutoresizingMaskIntoConstraints = false
myProfileStackView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
// stack view leading 23-pts from contentView leading
myProfileStackView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 23),
// stack view top 76-pts from contentView top
myProfileStackView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 76),
// need something to set the contentView height
// stack view bottom 8-pts from contentView bottom
myProfileStackView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -8),
// set imageView width and height
myImageView.widthAnchor.constraint(equalToConstant: 100.0),
myImageView.heightAnchor.constraint(equalTo: myImageView.widthAnchor),
])
}
public func configure() {
// here you would set the properties of your elements, such as
// label text
// imageView image
// colors
// etc
}
}
编辑
这是一个示例单元格 class,它与您发布的图片中的布局很接近。
请注意,需要的约束很少:
NSLayoutConstraint.activate([
// role element 12-pts from top
myRoleElement.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 12.0),
// centered horizontally
myRoleElement.centerXAnchor.constraint(equalTo: contentView.centerXAnchor),
// it will probably be using intrinsic height and width, but for demo purposes
myRoleElement.widthAnchor.constraint(equalTo: contentView.widthAnchor, multiplier: 0.4),
myRoleElement.heightAnchor.constraint(equalToConstant: 40.0),
// stack view 24-pts on each side
hStack.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 24),
hStack.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -24),
// stack view 20-pts on bottom
hStack.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -20),
// stack view top 20-pts from Role element bottom
hStack.topAnchor.constraint(equalTo: myRoleElement.bottomAnchor, constant: 20),
// set imageView width and height
myImageView.widthAnchor.constraint(equalToConstant: 100.0),
myImageView.heightAnchor.constraint(equalTo: myImageView.widthAnchor),
// we want the two "column" stack views to be equal widths
hStack.arrangedSubviews[1].widthAnchor.constraint(equalTo: hStack.arrangedSubviews[2].widthAnchor),
])
这是完整的单元格 class,包括一个示例“UserStruct”……当然,您会想要调整字体/大小、间距等:
// sample struct for user data
struct UserStruct {
var profilePicName: String = ""
var name: String = ""
var gender: String = ""
var age: Int = 0
var statValues: [String] = []
}
class StatsOverViewCell: UITableViewCell {
//Set identifier to be able to call it later on
static let identifier = "StatsOverViewCell"
// whatever your "role" element is...
let myRoleElement = UILabel()
let myImageView = UIImageView()
let myName = UILabel()
let myGenderAndAge = UILabel()
var statValueLabels: [UILabel] = []
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() {
// create 6 Value and 6 text labels
// assuming you have 6 "Text" strings, but for now
// we'll use "Text A", "Text B", etc
let tmp: [String] = [
"A", "B", "C",
"D", "E", "F",
]
var statTextLabels: [UILabel] = []
for i in 0..<6 {
var lb = UILabel()
lb.font = UIFont.systemFont(ofSize: 16, weight: .regular)
lb.textAlignment = .center
lb.textColor = .white
lb.text = "0"
statValueLabels.append(lb)
lb = UILabel()
lb.font = UIFont.systemFont(ofSize: 13, weight: .regular)
lb.textAlignment = .center
lb.textColor = .lightGray
lb.text = "Text \(tmp[i])"
statTextLabels.append(lb)
}
// name and Gender/Age label properties
myName.textAlignment = .center
myGenderAndAge.textAlignment = .center
myName.font = UIFont.systemFont(ofSize: 15, weight: .regular)
myGenderAndAge.font = UIFont.systemFont(ofSize: 15, weight: .regular)
myName.textColor = .white
myGenderAndAge.textColor = .white
// placeholder text
myName.text = "Name"
myGenderAndAge.text = "(F, 26)"
myImageView.contentMode = .scaleAspectFill
// create the "Profile" stack view
let myProfileStackView = UIStackView()
myProfileStackView.axis = .vertical
myProfileStackView.spacing = 2
//Add imageView, name and gender/age labels to the profile stackview
myProfileStackView.addArrangedSubview(myImageView)
myProfileStackView.addArrangedSubview(myName)
myProfileStackView.addArrangedSubview(myGenderAndAge)
// create horizontal stack view to hold
// Profile stack + two "column" stack views
let hStack = UIStackView()
// add Profile stack view
hStack.addArrangedSubview(myProfileStackView)
var j: Int = 0
// create two "column" stack views
// each with three "label pair" stack views
for _ in 0..<2 {
let columnStack = UIStackView()
columnStack.axis = .vertical
columnStack.distribution = .equalSpacing
for _ in 0..<3 {
let pairStack = UIStackView()
pairStack.axis = .vertical
pairStack.spacing = 4
pairStack.addArrangedSubview(statValueLabels[j])
pairStack.addArrangedSubview(statTextLabels[j])
columnStack.addArrangedSubview(pairStack)
j += 1
}
hStack.addArrangedSubview(columnStack)
}
// whatever your "Roles" element is...
// here, we'll simulate it with a label
myRoleElement.text = "Role 1 / Role 2"
myRoleElement.textAlignment = .center
myRoleElement.textColor = .white
myRoleElement.backgroundColor = .systemTeal
myRoleElement.layer.cornerRadius = 8
myRoleElement.layer.borderWidth = 1
myRoleElement.layer.borderColor = UIColor.white.cgColor
myRoleElement.layer.masksToBounds = true
// add Role element and horizontal stack view to contentView
contentView.addSubview(myRoleElement)
contentView.addSubview(hStack)
myRoleElement.translatesAutoresizingMaskIntoConstraints = false
hStack.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
// role element 12-pts from top
myRoleElement.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 12.0),
// centered horizontally
myRoleElement.centerXAnchor.constraint(equalTo: contentView.centerXAnchor),
// it will probably be using intrinsic height and width, but for demo purposes
myRoleElement.widthAnchor.constraint(equalTo: contentView.widthAnchor, multiplier: 0.4),
myRoleElement.heightAnchor.constraint(equalToConstant: 40.0),
// stack view 24-pts on each side
hStack.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 24),
hStack.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -24),
// stack view 20-pts on bottom
hStack.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -20),
// stack view top 20-pts from Role element bottom
hStack.topAnchor.constraint(equalTo: myRoleElement.bottomAnchor, constant: 20),
// set imageView width and height
myImageView.widthAnchor.constraint(equalToConstant: 100.0),
myImageView.heightAnchor.constraint(equalTo: myImageView.widthAnchor),
// we want the two "column" stack views to be equal widths
hStack.arrangedSubviews[1].widthAnchor.constraint(equalTo: hStack.arrangedSubviews[2].widthAnchor),
])
//contentView.backgroundColor = Utilities.hexStringToUIColor(hex: "#3F454B")
contentView.backgroundColor = UIColor(red: 0x3f / 255.0, green: 0x45 / 255.0, blue: 0x4b / 255.0, alpha: 1.0)
contentView.layer.borderWidth = 1
contentView.layer.borderColor = UIColor.lightGray.cgColor
// since we're setting the image view to explicit 100x100 size,
// we can make it round here
myImageView.layer.cornerRadius = 50
myImageView.layer.masksToBounds = true
}
public func configure(_ user: UserStruct) {
// here you would set the properties of your elements
// however you're getting your profile image
var img: UIImage!
if !user.profilePicName.isEmpty {
img = UIImage(named: user.profilePicName)
}
if img == nil {
img = UIImage(named: "person-icon")
}
if img != nil {
myImageView.image = img
}
myName.text = user.name
myGenderAndAge.text = "(\(user.gender), \(user.age))"
// probably want error checking to make sure we have 6 values
if user.statValues.count == 6 {
for (lbl, s) in zip(statValueLabels, user.statValues) {
lbl.text = s
}
}
}
}
和示例 table 视图控制器:
class UserStatsTableViewController: UITableViewController {
var myData: [UserStruct] = []
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(StatsOverViewCell.self, forCellReuseIdentifier: StatsOverViewCell.identifier)
// generate some sample data
// I'm using Female "pro1" and Male "pro2" images
for i in 0..<10 {
var user = UserStruct(profilePicName: i % 2 == 0 ? "pro2" : "pro1",
name: "Name \(i)",
gender: i % 2 == 0 ? "F" : "M",
age: Int.random(in: 21...65))
var vals: [String] = []
for _ in 0..<6 {
let v = Int.random(in: 100..<1000)
vals.append("\(v)")
}
user.statValues = vals
myData.append(user)
}
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return myData.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: StatsOverViewCell.identifier, for: indexPath) as! StatsOverViewCell
let user = myData[indexPath.row]
cell.configure(user)
return cell
}
}
这是 运行 时间的样子:
我目前正在我的应用程序中构建一个屏幕,它基本上是一个包含 3 个部分的长 UITableView,每个部分都有不同数量的独特自定义单元格。设置 tableview 工作正常,我在单元格中添加了一些随机文本以确保每个单元格都被正确调用和定位。我已经从我的项目中完全删除了我的故事板,因为它会由于某些原因导致以后出现问题。所以我不能通过故事板做任何事情。
下一步是构建自定义单元格。作为初学者,其中一些对我来说相当复杂。这是我的一个手机:
我想将单元格拆分为多个 UIStackView,一个用于图片和名称,一个用于右侧的统计信息,后者本身将包含两个堆栈视图,其中包含两行统计信息中的每一行。然后,其中每一个都可以包含另一个嵌入式堆栈视图,其中包含两个 uiLabel、编号和描述。最重要的是一个切换按钮。
我似乎无法理解如何定义这一切。正如我所说,我定义了 Tableview 并在我的 cellForRowAt
中调用正确的单元格,如下所示:
if indexPath.row == 0 && indexPath.section == 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: StatsOverViewCell.identifier, for: indexPath) as! StatsOverViewCell
cell.configure()
return cell
} else if ...
我已经为每个单元格创建了文件,其中之一是 StatsOverViewCell。 在这个文件中,我有一个与 class 同名的标识符。 我还添加了我从我的 tableview 调用的配置函数,我用来在单元格内布置视图的 layoutSubviews 函数,我已经初始化了我需要的每个标签和图像。我已将文件缩减为几个示例以节省您一些时间:
class StatsOverViewCell: UITableViewCell {
//Set identifier to be able to call it later on
static let identifier = "StatsOverViewCell"
let myProfileStackView = UIStackView()
let myImageView = UIImageView()
let myName = UILabel()
let myGenderAndAge = UILabel()
let myStatsStackView = UIStackView()
let oneView = UIStackView()
let oneStat = UILabel()
let oneLabel = UILabel()
let twoStackView = UIStackView()
let twoStat = UILabel()
let twoLabel = UILabel()
//Do this for each of the labels I have in the stats
public func configure() {
myImageView.image = UIImage(named: "person-icon")
myName.frame = CGRect(x: 0, y: 0, width: 100, height: 100)
myImageView.contentMode = .scaleAspectFill
myName.text = "Name."
myName.frame = CGRect(x: 0, y: 0, width: 100, height: 100)
myName.textAlignment = .center
//Add the Name label to the stackview
myProfileStackView.addArrangedSubview(myName)
myProfileStackView.addArrangedSubview(myImageView)
myName.centerXAnchor.constraint(equalTo: myProfileStackView.centerXAnchor).isActive = true
oneStat.text = "5.187"
oneStat.font = UIFont(name: "montserrat", size: 18)
oneLabel.text = "Text"
oneLabel.font = UIFont(name: "montserrat", size: 14)
}
//Layout in the cell
override func layoutSubviews() {
super.layoutSubviews()
contentView.backgroundColor = Utilities.hexStringToUIColor(hex: "#3F454B")
contentView.layer.borderWidth = 1
//Stackview
contentView.addSubview(myProfileStackView)
myProfileStackView.axis = .vertical
myProfileStackView.distribution = .equalSpacing
myProfileStackView.spacing = 3.5
myProfileStackView.backgroundColor = .red
myProfileStackView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 23).isActive = true
myProfileStackView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 76).isActive = true
}
如您所见,我在 tableview 中创建单元格时调用的配置方法中将所有 arrangedsubview 添加到 stackview。然后我在布局子视图中设置堆栈视图约束。我没有收到任何错误或任何东西。但是单元格显示完全是空的。
我觉得我忘记了什么,或者我不明白如何使用 uistackviews 创建单元格。我应该在哪里创建 stackviews,我应该在哪里将 arrangedsubviews 添加到这个 stackviews 以及我在 LayoutSubviews 中做什么? 我将非常感谢任何见解。 感谢您的宝贵时间!
你做错了几件事...
- 您的 UI 元素应该在 init 中创建和配置,而不是在
configure()
或layoutSubviews()
中
- 您需要完整的约束才能为您的元素提供正确的布局
看看我对你的手机所做的更改class。它应该让你上路:
class StatsOverViewCell: UITableViewCell {
//Set identifier to be able to call it later on
static let identifier = "StatsOverViewCell"
let myProfileStackView = UIStackView()
let myImageView = UIImageView()
let myName = UILabel()
let myGenderAndAge = UILabel()
let myStatsStackView = UIStackView()
let oneView = UIStackView()
let oneStat = UILabel()
let oneLabel = UILabel()
let twoStackView = UIStackView()
let twoStat = UILabel()
let twoLabel = UILabel()
//Do this for each of the labels I have in the stats
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() {
myImageView.image = UIImage(named: "person-icon")
// frame doesn't matter - stack view arrangedSubvies automatically
// set .translatesAutoresizingMaskIntoConstraints = false
//myName.frame = CGRect(x: 0, y: 0, width: 100, height: 100)
myImageView.contentMode = .scaleAspectFill
myName.text = "Name."
myName.textAlignment = .center
//Add the Name label to the stackview
myProfileStackView.addArrangedSubview(myName)
myProfileStackView.addArrangedSubview(myImageView)
// no need for this
//myName.centerXAnchor.constraint(equalTo: myProfileStackView.centerXAnchor).isActive = true
oneStat.text = "5.187"
oneStat.font = UIFont(name: "montserrat", size: 18)
oneLabel.text = "Text"
oneLabel.font = UIFont(name: "montserrat", size: 14)
contentView.backgroundColor = Utilities.hexStringToUIColor(hex: "#3F454B")
contentView.layer.borderWidth = 1
//Stackview
contentView.addSubview(myProfileStackView)
myProfileStackView.axis = .vertical
// no need for equalSpacing if you're explicitly setting the spacing
//myProfileStackView.distribution = .equalSpacing
myProfileStackView.spacing = 3.5
myProfileStackView.backgroundColor = .red
// stack view needs .translatesAutoresizingMaskIntoConstraints = false
myProfileStackView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
// stack view leading 23-pts from contentView leading
myProfileStackView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 23),
// stack view top 76-pts from contentView top
myProfileStackView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 76),
// need something to set the contentView height
// stack view bottom 8-pts from contentView bottom
myProfileStackView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -8),
// set imageView width and height
myImageView.widthAnchor.constraint(equalToConstant: 100.0),
myImageView.heightAnchor.constraint(equalTo: myImageView.widthAnchor),
])
}
public func configure() {
// here you would set the properties of your elements, such as
// label text
// imageView image
// colors
// etc
}
}
编辑
这是一个示例单元格 class,它与您发布的图片中的布局很接近。
请注意,需要的约束很少:
NSLayoutConstraint.activate([
// role element 12-pts from top
myRoleElement.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 12.0),
// centered horizontally
myRoleElement.centerXAnchor.constraint(equalTo: contentView.centerXAnchor),
// it will probably be using intrinsic height and width, but for demo purposes
myRoleElement.widthAnchor.constraint(equalTo: contentView.widthAnchor, multiplier: 0.4),
myRoleElement.heightAnchor.constraint(equalToConstant: 40.0),
// stack view 24-pts on each side
hStack.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 24),
hStack.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -24),
// stack view 20-pts on bottom
hStack.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -20),
// stack view top 20-pts from Role element bottom
hStack.topAnchor.constraint(equalTo: myRoleElement.bottomAnchor, constant: 20),
// set imageView width and height
myImageView.widthAnchor.constraint(equalToConstant: 100.0),
myImageView.heightAnchor.constraint(equalTo: myImageView.widthAnchor),
// we want the two "column" stack views to be equal widths
hStack.arrangedSubviews[1].widthAnchor.constraint(equalTo: hStack.arrangedSubviews[2].widthAnchor),
])
这是完整的单元格 class,包括一个示例“UserStruct”……当然,您会想要调整字体/大小、间距等:
// sample struct for user data
struct UserStruct {
var profilePicName: String = ""
var name: String = ""
var gender: String = ""
var age: Int = 0
var statValues: [String] = []
}
class StatsOverViewCell: UITableViewCell {
//Set identifier to be able to call it later on
static let identifier = "StatsOverViewCell"
// whatever your "role" element is...
let myRoleElement = UILabel()
let myImageView = UIImageView()
let myName = UILabel()
let myGenderAndAge = UILabel()
var statValueLabels: [UILabel] = []
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() {
// create 6 Value and 6 text labels
// assuming you have 6 "Text" strings, but for now
// we'll use "Text A", "Text B", etc
let tmp: [String] = [
"A", "B", "C",
"D", "E", "F",
]
var statTextLabels: [UILabel] = []
for i in 0..<6 {
var lb = UILabel()
lb.font = UIFont.systemFont(ofSize: 16, weight: .regular)
lb.textAlignment = .center
lb.textColor = .white
lb.text = "0"
statValueLabels.append(lb)
lb = UILabel()
lb.font = UIFont.systemFont(ofSize: 13, weight: .regular)
lb.textAlignment = .center
lb.textColor = .lightGray
lb.text = "Text \(tmp[i])"
statTextLabels.append(lb)
}
// name and Gender/Age label properties
myName.textAlignment = .center
myGenderAndAge.textAlignment = .center
myName.font = UIFont.systemFont(ofSize: 15, weight: .regular)
myGenderAndAge.font = UIFont.systemFont(ofSize: 15, weight: .regular)
myName.textColor = .white
myGenderAndAge.textColor = .white
// placeholder text
myName.text = "Name"
myGenderAndAge.text = "(F, 26)"
myImageView.contentMode = .scaleAspectFill
// create the "Profile" stack view
let myProfileStackView = UIStackView()
myProfileStackView.axis = .vertical
myProfileStackView.spacing = 2
//Add imageView, name and gender/age labels to the profile stackview
myProfileStackView.addArrangedSubview(myImageView)
myProfileStackView.addArrangedSubview(myName)
myProfileStackView.addArrangedSubview(myGenderAndAge)
// create horizontal stack view to hold
// Profile stack + two "column" stack views
let hStack = UIStackView()
// add Profile stack view
hStack.addArrangedSubview(myProfileStackView)
var j: Int = 0
// create two "column" stack views
// each with three "label pair" stack views
for _ in 0..<2 {
let columnStack = UIStackView()
columnStack.axis = .vertical
columnStack.distribution = .equalSpacing
for _ in 0..<3 {
let pairStack = UIStackView()
pairStack.axis = .vertical
pairStack.spacing = 4
pairStack.addArrangedSubview(statValueLabels[j])
pairStack.addArrangedSubview(statTextLabels[j])
columnStack.addArrangedSubview(pairStack)
j += 1
}
hStack.addArrangedSubview(columnStack)
}
// whatever your "Roles" element is...
// here, we'll simulate it with a label
myRoleElement.text = "Role 1 / Role 2"
myRoleElement.textAlignment = .center
myRoleElement.textColor = .white
myRoleElement.backgroundColor = .systemTeal
myRoleElement.layer.cornerRadius = 8
myRoleElement.layer.borderWidth = 1
myRoleElement.layer.borderColor = UIColor.white.cgColor
myRoleElement.layer.masksToBounds = true
// add Role element and horizontal stack view to contentView
contentView.addSubview(myRoleElement)
contentView.addSubview(hStack)
myRoleElement.translatesAutoresizingMaskIntoConstraints = false
hStack.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
// role element 12-pts from top
myRoleElement.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 12.0),
// centered horizontally
myRoleElement.centerXAnchor.constraint(equalTo: contentView.centerXAnchor),
// it will probably be using intrinsic height and width, but for demo purposes
myRoleElement.widthAnchor.constraint(equalTo: contentView.widthAnchor, multiplier: 0.4),
myRoleElement.heightAnchor.constraint(equalToConstant: 40.0),
// stack view 24-pts on each side
hStack.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 24),
hStack.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -24),
// stack view 20-pts on bottom
hStack.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -20),
// stack view top 20-pts from Role element bottom
hStack.topAnchor.constraint(equalTo: myRoleElement.bottomAnchor, constant: 20),
// set imageView width and height
myImageView.widthAnchor.constraint(equalToConstant: 100.0),
myImageView.heightAnchor.constraint(equalTo: myImageView.widthAnchor),
// we want the two "column" stack views to be equal widths
hStack.arrangedSubviews[1].widthAnchor.constraint(equalTo: hStack.arrangedSubviews[2].widthAnchor),
])
//contentView.backgroundColor = Utilities.hexStringToUIColor(hex: "#3F454B")
contentView.backgroundColor = UIColor(red: 0x3f / 255.0, green: 0x45 / 255.0, blue: 0x4b / 255.0, alpha: 1.0)
contentView.layer.borderWidth = 1
contentView.layer.borderColor = UIColor.lightGray.cgColor
// since we're setting the image view to explicit 100x100 size,
// we can make it round here
myImageView.layer.cornerRadius = 50
myImageView.layer.masksToBounds = true
}
public func configure(_ user: UserStruct) {
// here you would set the properties of your elements
// however you're getting your profile image
var img: UIImage!
if !user.profilePicName.isEmpty {
img = UIImage(named: user.profilePicName)
}
if img == nil {
img = UIImage(named: "person-icon")
}
if img != nil {
myImageView.image = img
}
myName.text = user.name
myGenderAndAge.text = "(\(user.gender), \(user.age))"
// probably want error checking to make sure we have 6 values
if user.statValues.count == 6 {
for (lbl, s) in zip(statValueLabels, user.statValues) {
lbl.text = s
}
}
}
}
和示例 table 视图控制器:
class UserStatsTableViewController: UITableViewController {
var myData: [UserStruct] = []
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(StatsOverViewCell.self, forCellReuseIdentifier: StatsOverViewCell.identifier)
// generate some sample data
// I'm using Female "pro1" and Male "pro2" images
for i in 0..<10 {
var user = UserStruct(profilePicName: i % 2 == 0 ? "pro2" : "pro1",
name: "Name \(i)",
gender: i % 2 == 0 ? "F" : "M",
age: Int.random(in: 21...65))
var vals: [String] = []
for _ in 0..<6 {
let v = Int.random(in: 100..<1000)
vals.append("\(v)")
}
user.statValues = vals
myData.append(user)
}
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return myData.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: StatsOverViewCell.identifier, for: indexPath) as! StatsOverViewCell
let user = myData[indexPath.row]
cell.configure(user)
return cell
}
}
这是 运行 时间的样子: