你做错了一些事情......
- 您的 UI 元素应该在 init 中创建和配置,而不是在
configure() 或 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
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
}
}
编辑
这是一个与您发布的图片中的布局非常接近的示例单元格类。
请注意,需要的约束很少:
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),
])
这是完整的单元格类,包括一个示例“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
}
}
}
}
和一个示例表视图控制器:
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
}
}
这是它在运行时的样子: