UITableView 滚动后,以编程方式创建的 UITableViewCell 未正确显示
Programmatically created UITableViewCell is not correctly displayed after UITableView scrolling
这是我第一次尝试以编程方式创建的表格视图单元格。所以也许我正在监督一些基础知识。
我接手并改编了这段代码,以使用 tableview 实现聊天功能。它涵盖了 tableviewcell 的 ChatMessageCell 和 tableview 的 ChatViewController。 ChatMessageCell 检查每条聊天消息,无论是传入消息(用户不同于当前用户)还是传出消息。如果它是传入单元格,则单元格固定在白色背景的左侧,如果它是传出单元格,则固定在绿色背景的右侧。单元格大小会根据聊天消息的文本进行调整,以便它显示在屏幕的左侧或右侧(就像在 WhatsApp 中一样)。
现在问题:
当最初构建 tableview 时,一切都以正确的方式显示,单元格被调整到左侧或右侧。一旦用户滚动表格视图,单元格就会被拉伸以占据屏幕的整个宽度,无论它们是传入还是传出。任何提示,有什么问题吗?
下面是在 ChatViewController 中创建单元格的片段:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath) as! ChatMessageCell
cell.configure(with: messages[indexPath.row])
return cell
}
和 ChatMessageCell class:
picture shows the last cells after the scroll. the first ones are correctly displayed
import Foundation
import UIKit
import FirebaseAuth
class ChatMessageCell: UITableViewCell {
let messageLabel = UILabel()
let messageBgView = UIView()
// change background view colour accordingly
var isIncoming: Bool = false {
didSet {
messageBgView.backgroundColor = isIncoming ? UIColor.white : #colorLiteral(red: 0.8823529412, green: 0.968627451, blue: 0.7921568627, alpha: 1)
}
}
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
addSubview(messageBgView)
addSubview(messageLabel)
messageBgView.translatesAutoresizingMaskIntoConstraints = false
messageBgView.layer.cornerRadius = 7
messageLabel.numberOfLines = 0
messageLabel.translatesAutoresizingMaskIntoConstraints = false
// set constraints for the message and the background view
let constraints = [
messageLabel.topAnchor.constraint(equalTo: topAnchor, constant: 24),
messageLabel.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -24),
messageBgView.topAnchor.constraint(equalTo: messageLabel.topAnchor, constant: -16),
messageBgView.leadingAnchor.constraint(equalTo: messageLabel.leadingAnchor, constant: -16),
messageBgView.bottomAnchor.constraint(equalTo: messageLabel.bottomAnchor, constant: 16),
messageBgView.trailingAnchor.constraint(equalTo: messageLabel.trailingAnchor, constant: 16)
]
NSLayoutConstraint.activate(constraints)
selectionStyle = .none
backgroundColor = .clear
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
// what we will call from our tableview method
func configure(with model: Chat) {
isIncoming = (model.senderId != Auth.auth().currentUser?.uid)
if isIncoming {
let sender = model.senderNameInApp
// align to the left
let nameAttributes = [
NSAttributedString.Key.foregroundColor : UIColor.orange,
NSAttributedString.Key.font : UIFont.boldSystemFont(ofSize: 16)
] as [NSAttributedString.Key : Any]
// sender name at top, message at the next line
let senderName = NSMutableAttributedString(string: sender + "\n", attributes: nameAttributes)
let message = NSMutableAttributedString(string: model.message)
senderName.append(message)
messageLabel.attributedText = senderName
messageLabel.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 32).isActive = true
messageLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -32).isActive = false
}
else {
// align to the right
messageLabel.text = model.message
messageLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -32).isActive = true
messageLabel.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 32).isActive = false
}
}
}
另一种方法 - 您可能会发现它更容易 - 是同时创建前导和尾随约束 - 一个用于 incoming 一个用于 outgoing,但给他们不同的Priority
值。要更改对齐方式,您可以交换优先级。
例如,像这样更新您的单元格 class(数字对应于我添加的评论):
- 声明用于对齐的约束变量
- 设置标签的 ContentHuggingPriority 以防止其填满宽度
- 创建前导/尾随约束
- 如果需要,限制消息标签的最大宽度
- 激活传入和传出约束
- 根据需要更新约束的优先级
您应该能够直接将其放入以替换您当前的 ChatMessageCell
class:
class ChatMessageCell: UITableViewCell {
let messageLabel = UILabel()
let messageBgView = UIView()
// (1) constraints for messsage aligmment
var incomingConstraint: NSLayoutConstraint!
var outgoingConstraint: NSLayoutConstraint!
// change background view colour accordingly
var isIncoming: Bool = false {
didSet {
messageBgView.backgroundColor = isIncoming ? UIColor.white : #colorLiteral(red: 0.8823529412, green: 0.968627451, blue: 0.7921568627, alpha: 1)
}
}
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
addSubview(messageBgView)
addSubview(messageLabel)
messageBgView.translatesAutoresizingMaskIntoConstraints = false
messageBgView.layer.cornerRadius = 7
messageLabel.numberOfLines = 0
messageLabel.translatesAutoresizingMaskIntoConstraints = false
// (2) "hug" the message content
messageLabel.setContentHuggingPriority(.defaultHigh, for: .horizontal)
// (3) create Leading / Trailing constraints so we can update their Priority later
incomingConstraint = messageLabel.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 32.0)
outgoingConstraint = messageLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -32.0)
// set constraints for the message and the background view
NSLayoutConstraint.activate([
messageLabel.topAnchor.constraint(equalTo: topAnchor, constant: 24),
messageLabel.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -24),
// (4) limit message label to 75% of width (if desired)
messageLabel.widthAnchor.constraint(lessThanOrEqualTo: widthAnchor, multiplier: 0.75),
// (5) activate both leading / trailing constraints
incomingConstraint,
outgoingConstraint,
messageBgView.topAnchor.constraint(equalTo: messageLabel.topAnchor, constant: -16),
messageBgView.leadingAnchor.constraint(equalTo: messageLabel.leadingAnchor, constant: -16),
messageBgView.bottomAnchor.constraint(equalTo: messageLabel.bottomAnchor, constant: 16),
messageBgView.trailingAnchor.constraint(equalTo: messageLabel.trailingAnchor, constant: 16),
])
selectionStyle = .none
backgroundColor = .clear
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
// what we will call from our tableview method
func configure(with model: Chat) {
isIncoming = (model.senderId != Auth.auth().currentUser?.uid)
if isIncoming {
let sender = model.senderNameInApp
// align to the left
let nameAttributes = [
NSAttributedString.Key.foregroundColor : UIColor.orange,
NSAttributedString.Key.font : UIFont.boldSystemFont(ofSize: 16)
] as [NSAttributedString.Key : Any]
// sender name at top, message at the next line
let senderName = NSMutableAttributedString(string: sender + "\n", attributes: nameAttributes)
let message = NSMutableAttributedString(string: model.message)
senderName.append(message)
messageLabel.attributedText = senderName
// (6) update leading / trailing constraint priorities
outgoingConstraint.priority = .defaultLow
incomingConstraint.priority = .defaultHigh
// don't do this here
//messageLabel.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 32).isActive = true
//messageLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -32).isActive = false
}
else {
// align to the right
messageLabel.text = model.message
// (6) update leading / trailing constraint priorities
incomingConstraint.priority = .defaultLow
outgoingConstraint.priority = .defaultHigh
// don't do this here
//messageLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -32).isActive = true
//messageLabel.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 32).isActive = false
}
}
}
这是我第一次尝试以编程方式创建的表格视图单元格。所以也许我正在监督一些基础知识。 我接手并改编了这段代码,以使用 tableview 实现聊天功能。它涵盖了 tableviewcell 的 ChatMessageCell 和 tableview 的 ChatViewController。 ChatMessageCell 检查每条聊天消息,无论是传入消息(用户不同于当前用户)还是传出消息。如果它是传入单元格,则单元格固定在白色背景的左侧,如果它是传出单元格,则固定在绿色背景的右侧。单元格大小会根据聊天消息的文本进行调整,以便它显示在屏幕的左侧或右侧(就像在 WhatsApp 中一样)。
现在问题: 当最初构建 tableview 时,一切都以正确的方式显示,单元格被调整到左侧或右侧。一旦用户滚动表格视图,单元格就会被拉伸以占据屏幕的整个宽度,无论它们是传入还是传出。任何提示,有什么问题吗?
下面是在 ChatViewController 中创建单元格的片段:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath) as! ChatMessageCell
cell.configure(with: messages[indexPath.row])
return cell
}
和 ChatMessageCell class:
picture shows the last cells after the scroll. the first ones are correctly displayed
import Foundation
import UIKit
import FirebaseAuth
class ChatMessageCell: UITableViewCell {
let messageLabel = UILabel()
let messageBgView = UIView()
// change background view colour accordingly
var isIncoming: Bool = false {
didSet {
messageBgView.backgroundColor = isIncoming ? UIColor.white : #colorLiteral(red: 0.8823529412, green: 0.968627451, blue: 0.7921568627, alpha: 1)
}
}
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
addSubview(messageBgView)
addSubview(messageLabel)
messageBgView.translatesAutoresizingMaskIntoConstraints = false
messageBgView.layer.cornerRadius = 7
messageLabel.numberOfLines = 0
messageLabel.translatesAutoresizingMaskIntoConstraints = false
// set constraints for the message and the background view
let constraints = [
messageLabel.topAnchor.constraint(equalTo: topAnchor, constant: 24),
messageLabel.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -24),
messageBgView.topAnchor.constraint(equalTo: messageLabel.topAnchor, constant: -16),
messageBgView.leadingAnchor.constraint(equalTo: messageLabel.leadingAnchor, constant: -16),
messageBgView.bottomAnchor.constraint(equalTo: messageLabel.bottomAnchor, constant: 16),
messageBgView.trailingAnchor.constraint(equalTo: messageLabel.trailingAnchor, constant: 16)
]
NSLayoutConstraint.activate(constraints)
selectionStyle = .none
backgroundColor = .clear
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
// what we will call from our tableview method
func configure(with model: Chat) {
isIncoming = (model.senderId != Auth.auth().currentUser?.uid)
if isIncoming {
let sender = model.senderNameInApp
// align to the left
let nameAttributes = [
NSAttributedString.Key.foregroundColor : UIColor.orange,
NSAttributedString.Key.font : UIFont.boldSystemFont(ofSize: 16)
] as [NSAttributedString.Key : Any]
// sender name at top, message at the next line
let senderName = NSMutableAttributedString(string: sender + "\n", attributes: nameAttributes)
let message = NSMutableAttributedString(string: model.message)
senderName.append(message)
messageLabel.attributedText = senderName
messageLabel.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 32).isActive = true
messageLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -32).isActive = false
}
else {
// align to the right
messageLabel.text = model.message
messageLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -32).isActive = true
messageLabel.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 32).isActive = false
}
}
}
另一种方法 - 您可能会发现它更容易 - 是同时创建前导和尾随约束 - 一个用于 incoming 一个用于 outgoing,但给他们不同的Priority
值。要更改对齐方式,您可以交换优先级。
例如,像这样更新您的单元格 class(数字对应于我添加的评论):
- 声明用于对齐的约束变量
- 设置标签的 ContentHuggingPriority 以防止其填满宽度
- 创建前导/尾随约束
- 如果需要,限制消息标签的最大宽度
- 激活传入和传出约束
- 根据需要更新约束的优先级
您应该能够直接将其放入以替换您当前的 ChatMessageCell
class:
class ChatMessageCell: UITableViewCell {
let messageLabel = UILabel()
let messageBgView = UIView()
// (1) constraints for messsage aligmment
var incomingConstraint: NSLayoutConstraint!
var outgoingConstraint: NSLayoutConstraint!
// change background view colour accordingly
var isIncoming: Bool = false {
didSet {
messageBgView.backgroundColor = isIncoming ? UIColor.white : #colorLiteral(red: 0.8823529412, green: 0.968627451, blue: 0.7921568627, alpha: 1)
}
}
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
addSubview(messageBgView)
addSubview(messageLabel)
messageBgView.translatesAutoresizingMaskIntoConstraints = false
messageBgView.layer.cornerRadius = 7
messageLabel.numberOfLines = 0
messageLabel.translatesAutoresizingMaskIntoConstraints = false
// (2) "hug" the message content
messageLabel.setContentHuggingPriority(.defaultHigh, for: .horizontal)
// (3) create Leading / Trailing constraints so we can update their Priority later
incomingConstraint = messageLabel.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 32.0)
outgoingConstraint = messageLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -32.0)
// set constraints for the message and the background view
NSLayoutConstraint.activate([
messageLabel.topAnchor.constraint(equalTo: topAnchor, constant: 24),
messageLabel.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -24),
// (4) limit message label to 75% of width (if desired)
messageLabel.widthAnchor.constraint(lessThanOrEqualTo: widthAnchor, multiplier: 0.75),
// (5) activate both leading / trailing constraints
incomingConstraint,
outgoingConstraint,
messageBgView.topAnchor.constraint(equalTo: messageLabel.topAnchor, constant: -16),
messageBgView.leadingAnchor.constraint(equalTo: messageLabel.leadingAnchor, constant: -16),
messageBgView.bottomAnchor.constraint(equalTo: messageLabel.bottomAnchor, constant: 16),
messageBgView.trailingAnchor.constraint(equalTo: messageLabel.trailingAnchor, constant: 16),
])
selectionStyle = .none
backgroundColor = .clear
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
// what we will call from our tableview method
func configure(with model: Chat) {
isIncoming = (model.senderId != Auth.auth().currentUser?.uid)
if isIncoming {
let sender = model.senderNameInApp
// align to the left
let nameAttributes = [
NSAttributedString.Key.foregroundColor : UIColor.orange,
NSAttributedString.Key.font : UIFont.boldSystemFont(ofSize: 16)
] as [NSAttributedString.Key : Any]
// sender name at top, message at the next line
let senderName = NSMutableAttributedString(string: sender + "\n", attributes: nameAttributes)
let message = NSMutableAttributedString(string: model.message)
senderName.append(message)
messageLabel.attributedText = senderName
// (6) update leading / trailing constraint priorities
outgoingConstraint.priority = .defaultLow
incomingConstraint.priority = .defaultHigh
// don't do this here
//messageLabel.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 32).isActive = true
//messageLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -32).isActive = false
}
else {
// align to the right
messageLabel.text = model.message
// (6) update leading / trailing constraint priorities
incomingConstraint.priority = .defaultLow
outgoingConstraint.priority = .defaultHigh
// don't do this here
//messageLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -32).isActive = true
//messageLabel.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 32).isActive = false
}
}
}