UITableViewCell 未按预期显示约束
Posted
技术标签:
【中文标题】UITableViewCell 未按预期显示约束【英文标题】:UITableViewCell not displaying constraints as intended 【发布时间】:2020-07-25 12:27:08 【问题描述】:我正在尝试以编程方式创建一个 tableview 单元格,但在按预期布局时遇到了一些麻烦。我认为这取决于我调用事物的顺序,但我无法按预期显示内容(我尝试了 uiview 上的插图和其他约束,它似乎工作正常)。
我在 VC 中注册了 tableview 单元格,我还在 cellforrow 中将一个可重用的单元格出列,我在其中传递了活动对象来更新单元格 UI。单元格的高度由 heightforrow 回调设置为 300。
ActivityCell 代码
class ActivityCell: UITableViewCell
private var activityCard = UIView()
private var verticalStack = UIStackView()
private var nameStack = UIStackView() // top horizontal stack
private let profileImage = UIImageView()
private let profileNameLbl = UILabel()
private let dateLbl = UILabel()
private let raceVerticalStack = UIStackView() //middle
private let topRaceStack = UIStackView()
private let bottomRaceStack = UIStackView()
private let raceNameStack = UIStackView()
private let raceNameLbl = UILabel()
private let distanceStack = UIStackView()
private let distanceLbl = UILabel()
private let distanceValueLbl = UILabel()
private let timeStack = UIStackView()
private let timeLbl = UILabel()
private let timeValueLbl = UILabel()
private let paceStack = UIStackView()
private let paceLbl = UILabel()
private let paceValueLbl = UILabel()
private let positionStack = UIStackView()
private let positionLbl = UILabel()
private let positionValueLbl = UILabel()
private let pointsStack = UIStackView()
private let pointsLbl = UILabel()
private let pointsValueLbl = UILabel()
private let bottomStack = UIStackView() //bottom
private let clapStack = UIStackView()
private let clapIcon = UIImageView()
private let clapCountLbl = UILabel()
private let clapCommentStack = UIStackView()
private let commentStack = UIStackView()
private let commentIcon = UIImageView()
private let commentCountLbl = UILabel()
private let clapButton = UIButton() // interaction buttons
private let commentButton = UIButton()
private let gradientStart = UIColor(red: 24/255, green: 44/255, blue: 86/255, alpha: 1.0)
private let gradientEnd = UIColor(red: 234/255, green: 82/255, blue: 119/255, alpha: 1.0)
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?)
super.init(style: style, reuseIdentifier: reuseIdentifier)
layer.masksToBounds = true
self.contentView.layer.masksToBounds = true
activityCard.frame = self.frame.inset(by: UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10))
verticalStack.frame = activityCard.frame
setupHierarchy()
setConstraints()
configureView()
required init?(coder: NSCoder)
fatalError("init(coder:) has not been implemented")
private func configureView()
labelSetup()
buttonSetup()
stackViewSetup()
imageSetup()
stackViewSetup()
setShadow()
func updateCell(with activity: RaceActivity)
profileNameLbl.text = activity.runnerName
profileImage.image = activity.profileImage
raceNameLbl.text = activity.raceName
distanceValueLbl.text = activity.distance
timeValueLbl.text = activity.time
paceValueLbl.text = activity.pace
positionValueLbl.text = activity.position
pointsValueLbl.text = activity.points
private func labelSetup()
profileNameLbl.font = .Graphik()
raceNameLbl.font = .Graphik(.medium, size: 22)
dateLbl.font = .Graphik(.light, size: 17)
distanceLbl.font = .Graphik()
timeLbl.font = .Graphik()
paceLbl.font = .Graphik()
positionLbl.font = .Graphik()
pointsLbl.font = .Graphik()
clapCountLbl.font = .Graphik()
commentCountLbl.font = .Graphik()
paceLbl.font = .Graphik()
distanceValueLbl.font = .Graphik(.medium)
timeValueLbl.font = .Graphik(.medium)
paceValueLbl.font = .Graphik(.medium)
positionValueLbl.font = .Graphik(.medium)
timeLbl.textAlignment = .left
paceLbl.textAlignment = .center
paceValueLbl.textAlignment = .center
pointsLbl.textAlignment = .left
pointsValueLbl.textAlignment = .left
dateLbl.textAlignment = .right
clapCountLbl.text = "33"
commentCountLbl.text = "12"
positionLbl.text = "Position"
pointsLbl.text = "Points"
distanceLbl.text = "Distance"
timeLbl.text = "Time"
paceLbl.text = "Pace"
dateLbl.text = "Friday"
private func stackViewSetup()
nameStack.distribution = .fillProportionally
topRaceStack.distribution = .fillEqually
bottomRaceStack.distribution = .fillEqually
clapCommentStack.distribution = .fillEqually
bottomStack.distribution = .fillEqually
raceNameStack.axis = .horizontal
nameStack.axis = .horizontal
verticalStack.axis = .vertical
raceVerticalStack.axis = .vertical
bottomStack.axis = .horizontal
topRaceStack.axis = .horizontal
bottomRaceStack.axis = .horizontal
distanceStack.axis = .vertical
timeStack.axis = .vertical
paceStack.axis = .vertical
positionStack.axis = .vertical
pointsStack.axis = .vertical
nameStack.spacing = 10
verticalStack.spacing = 5
raceVerticalStack.spacing = 10
bottomStack.spacing = 10
nameStack.alignment = .center
distanceStack.alignment = .center
timeStack.alignment = .center
paceStack.alignment = .center
pointsStack.alignment = .center
positionStack.alignment = .center
clapCommentStack.alignment = .center
bottomStack.alignment = .center
raceVerticalStack.spacing = 5
topRaceStack.spacing = 2
clapCommentStack.spacing = 3
clapStack.spacing = 4
commentStack.spacing = 4
private func buttonSetup()
clapButton.setTitle("Clap", for: .normal)
clapButton.setTitleColor(.darkGray, for: .normal)
clapButton.titleLabel?.font = .Graphik(.regular, size: 14)
clapButton.titleEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
clapButton.imageEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 30)
clapButton.setImage(UIImage(named: "clap"), for: .normal)
commentButton.setTitle("Comment", for: .normal)
commentButton.setTitleColor(.darkGray, for: .normal)
commentButton.titleLabel?.font = .Graphik(.regular, size: 14)
commentButton.titleEdgeInsets = UIEdgeInsets(top: 0, left: 10, bottom: 0, right: 0)
commentButton.imageEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 10)
commentButton.setImage(UIImage(named: "comment"), for: .normal)
private func imageSetup()
profileImage.layer.cornerRadius = 15
profileImage.backgroundColor = .lightGray
clapIcon.image = UIImage(named: "clapGrey")
clapIcon.contentMode = .scaleAspectFit
commentIcon.image = UIImage(named: "commentGrey")
commentIcon.contentMode = .scaleAspectFit
private func setShadow()
let gradient = CAGradientLayer()
gradient.frame = CGRect(origin: CGPoint(x: 10, y: 5), size: frame.size)
gradient.colors = [gradientStart.cgColor, gradientEnd.cgColor]
let border = CAShapeLayer()
border.lineWidth = 2
border.path = UIBezierPath(roundedRect: frame.inset(by: UIEdgeInsets(top: 10, left: 2, bottom: 10, right: 22)), cornerRadius: 12).cgPath
border.strokeColor = UIColor.black.cgColor
border.fillColor = UIColor.clear.cgColor
gradient.mask = border
removeExistingGradient(from: self)
self.layer.addSublayer(gradient)
private func setConstraints()
let constraints = [
nameStack.heightAnchor.constraint(equalToConstant: 50),
raceNameStack.heightAnchor.constraint(equalToConstant: 30),
topRaceStack.heightAnchor.constraint(equalToConstant: 60),
bottomRaceStack.heightAnchor.constraint(equalToConstant: 60),
profileImage.heightAnchor.constraint(equalToConstant: 30),
profileImage.widthAnchor.constraint(equalToConstant: 30),
commentButton.widthAnchor.constraint(greaterThanOrEqualToConstant: 90),
clapButton.widthAnchor.constraint(greaterThanOrEqualToConstant: 90),
clapIcon.widthAnchor.constraint(equalToConstant: 15),
commentIcon.widthAnchor.constraint(equalToConstant: 15),
]
NSLayoutConstraint.activate(constraints)
setupMargins()
private func setupMargins()
nameStack.layoutMargins = UIEdgeInsets(top: 0, left: 10, bottom: 0, right: 25)
raceNameStack.layoutMargins = UIEdgeInsets(top:0, left: 20, bottom: 0, right: 0)
raceVerticalStack.layoutMargins = UIEdgeInsets(top: 20, left: 0, bottom: 0, right: 35)
bottomStack.layoutMargins = UIEdgeInsets(top: 0, left: 20, bottom: 10, right: 30)
nameStack.isLayoutMarginsRelativeArrangement = true
topRaceStack.isLayoutMarginsRelativeArrangement = true
bottomRaceStack.isLayoutMarginsRelativeArrangement = true
raceNameStack.isLayoutMarginsRelativeArrangement = true
bottomStack.isLayoutMarginsRelativeArrangement = true
addLines()
private func addLines()
guard bottomStack.frame.height > 0 else return
let horizontalStart = CGPoint(x: bottomStack.frame.minX, y: bottomStack.frame.minY)
let bottomLine = UIView(frame: CGRect(x: horizontalStart.x + 20, y: horizontalStart.y + 12, width: bottomStack.frame.width - 40, height: 1.0))
bottomLine.tag = 99
bottomLine.layer.borderWidth = 0.8
bottomLine.layer.borderColor = UIColor.lightGray.cgColor
let clapStackLine = UIView(frame: CGRect(x: clapCommentStack.frame.maxX, y: horizontalStart.y + 22, width: 1.0, height: clapCommentStack.frame.maxY - clapCommentStack.frame.minY))
clapStackLine.layer.borderWidth = 0.8
clapStackLine.layer.borderColor = UIColor.lightGray.cgColor
let commentLine = UIView(frame: CGRect(x: clapButton.frame.maxX, y: horizontalStart.y + 22, width: 1.0, height: clapCommentStack.frame.maxY - clapCommentStack.frame.minY))
commentLine.layer.borderWidth = 0.8
commentLine.layer.borderColor = UIColor.lightGray.cgColor
let topLine = UIView(frame: CGRect(x: horizontalStart.x + 20, y: nameStack.frame.maxY + 5, width: bottomStack.frame.width - 40, height: 1.0))
topLine.layer.borderWidth = 0.8
topLine.layer.borderColor = UIColor.lightGray.cgColor
self.activityCard.addSubview(bottomLine)
self.activityCard.addSubview(clapStackLine)
self.activityCard.addSubview(commentLine)
self.activityCard.addSubview(topLine)
private func setupHierarchy()
nameStack.addArrangedSubview(profileImage)
nameStack.addArrangedSubview(profileNameLbl)
nameStack.addArrangedSubview(dateLbl)
raceVerticalStack.addArrangedSubview(raceNameStack)
raceVerticalStack.addArrangedSubview(topRaceStack)
raceVerticalStack.addArrangedSubview(bottomRaceStack)
raceNameStack.addArrangedSubview(raceNameLbl)
topRaceStack.addArrangedSubview(distanceStack)
topRaceStack.addArrangedSubview(timeStack)
topRaceStack.addArrangedSubview(paceStack)
bottomRaceStack.addArrangedSubview(positionStack)
bottomRaceStack.addArrangedSubview(pointsStack)
bottomRaceStack.addArrangedSubview(UIView())
distanceStack.addArrangedSubview(distanceLbl)
distanceStack.addArrangedSubview(distanceValueLbl)
timeStack.addArrangedSubview(timeLbl)
timeStack.addArrangedSubview(timeValueLbl)
paceStack.addArrangedSubview(paceLbl)
paceStack.addArrangedSubview(paceValueLbl)
positionStack.addArrangedSubview(positionLbl)
positionStack.addArrangedSubview(positionValueLbl)
pointsStack.addArrangedSubview(pointsLbl)
pointsStack.addArrangedSubview(pointsValueLbl)
clapStack.addArrangedSubview(clapIcon)
clapStack.addArrangedSubview(clapCountLbl)
commentStack.addArrangedSubview(commentIcon)
commentStack.addArrangedSubview(commentCountLbl)
clapCommentStack.addArrangedSubview(clapStack)
clapCommentStack.addArrangedSubview(commentStack)
mainStackHierarchy()
private func mainStackHierarchy()
bottomStack.addArrangedSubview(clapCommentStack)
bottomStack.addArrangedSubview(clapButton)
bottomStack.addArrangedSubview(commentButton)
verticalStack.addArrangedSubview(nameStack)
verticalStack.addArrangedSubview(raceNameStack)
verticalStack.addArrangedSubview(raceVerticalStack)
verticalStack.addArrangedSubview(bottomStack)
activityCard.addSubview(verticalStack)
self.contentView.addSubview(activityCard)
当前输出截图:
预期输出的屏幕截图:
任何帮助表示赞赏:D
【问题讨论】:
【参考方案1】:您遇到了问题,因为您使用了很多显式帧而不是利用自动布局。
首先,如果您将 init()
函数更改为此并运行您的应用程序:
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?)
super.init(style: style, reuseIdentifier: reuseIdentifier)
layer.masksToBounds = true
self.contentView.layer.masksToBounds = true
// no need to set frames
//activityCard.frame = self.frame.inset(by: UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10))
//verticalStack.frame = activityCard.frame
setupHierarchy()
setConstraints()
configureView()
activityCard.translatesAutoresizingMaskIntoConstraints = false
verticalStack.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
activityCard.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 10.0),
activityCard.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 10.0),
activityCard.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -10.0),
activityCard.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -10.0),
verticalStack.topAnchor.constraint(equalTo: activityCard.topAnchor, constant: 6.0),
verticalStack.leadingAnchor.constraint(equalTo: activityCard.leadingAnchor, constant: 6.0),
verticalStack.trailingAnchor.constraint(equalTo: activityCard.trailingAnchor, constant: -6.0),
verticalStack.bottomAnchor.constraint(equalTo: activityCard.bottomAnchor, constant: -6.0),
])
你会发现你离你想要的更近了。
接下来,改变:
private var activityCard = UIView()
到:
private var activityCard = ActivityCardView()
并使用这个类:
class ActivityCardView: UIView
private let gradientStart = UIColor(red: 24/255, green: 44/255, blue: 86/255, alpha: 1.0)
private let gradientEnd = UIColor(red: 234/255, green: 82/255, blue: 119/255, alpha: 1.0)
private let gradLayer = CAGradientLayer()
override init(frame: CGRect)
super.init(frame: frame)
layer.addSublayer(gradLayer)
gradLayer.colors = [gradientStart.cgColor, gradientEnd.cgColor]
required init?(coder: NSCoder)
fatalError("init(coder:) has not been implemented")
override func layoutSubviews()
super.layoutSubviews()
gradLayer.frame = bounds
let border = CAShapeLayer()
border.lineWidth = 2
border.path = UIBezierPath(roundedRect: bounds.inset(by: UIEdgeInsets(top: 10, left: 2, bottom: 10, right: 2)), cornerRadius: 12).cgPath
border.strokeColor = UIColor.black.cgColor
border.fillColor = UIColor.clear.cgColor
gradLayer.mask = border
然后,从单元类中删除您的 private func setShadow()
。这个ActivityCardView
类会自动处理渐变边框。
对于您的“分隔符”行,您最好将它们添加为verticalStack
的排列子视图,而不是尝试使用绝对坐标定位它们:
private func mainStackHierarchy()
bottomStack.addArrangedSubview(clapCommentStack)
bottomStack.addArrangedSubview(clapButton)
bottomStack.addArrangedSubview(commentButton)
verticalStack.addArrangedSubview(nameStack)
var hLineView = UIView()
hLineView.heightAnchor.constraint(equalToConstant: 1.0).isActive = true
hLineView.backgroundColor = .lightGray
verticalStack.addArrangedSubview(hLineView)
verticalStack.addArrangedSubview(raceNameStack)
verticalStack.addArrangedSubview(raceVerticalStack)
hLineView = UIView()
hLineView.heightAnchor.constraint(equalToConstant: 1.0).isActive = true
hLineView.backgroundColor = .lightGray
verticalStack.addArrangedSubview(hLineView)
verticalStack.addArrangedSubview(bottomStack)
activityCard.addSubview(verticalStack)
self.contentView.addSubview(activityCard)
此时,您的输出应如下所示:
比赛名称上的下划线可能最好使用带有下划线样式的属性文本来处理。
我假设您会想要对间距进行一些调整——但这不应该有任何问题。
【讨论】:
太棒了!非常感谢,我不敢相信我忽略了为活动卡创建一个新课程。我想我在接近它时过多地考虑故事板设计,而不是利用以编程方式创建 UI 元素的优势以上是关于UITableViewCell 未按预期显示约束的主要内容,如果未能解决你的问题,请参考以下文章
UITableViewCell 高度内的 UITableView 未按预期工作