问题
I've got basic chat functionality as part of an App I'm building. It is basically a UITable View where the UITableViewCell only contains a UILabel (the chat message text) and a UIView (serving as a speech bubble, surrounding the text. Here's the code:
class ChatMessageViewCellController: UITableViewCell {
var ChatMessageText = UILabel()
var ChatBubble = UIView()
var leadingConstraint: NSLayoutConstraint!
var trailingConstraint: NSLayoutConstraint!
var isIncoming: Bool! {
didSet {
if self.isIncoming {
self.ChatBubble.backgroundColor = UIColor(named: "customGrey")
self.leadingConstraint.isActive = true
self.trailingConstraint.isActive = false
} else {
self.ChatBubble.backgroundColor = UIColor(named: "customGreen")
self.leadingConstraint.isActive = false
self.trailingConstraint.isActive = true
}
}
}
override func awakeFromNib() {
super.awakeFromNib()
addSubview(ChatBubble)
addSubview(ChatMessageText)
self.ChatBubble.translatesAutoresizingMaskIntoConstraints = false
self.ChatMessageText.translatesAutoresizingMaskIntoConstraints = false
self.ChatBubble.backgroundColor = UIColor(named: "customGreen")
self.ChatBubble.layer.cornerRadius = 10
self.ChatMessageText.numberOfLines = 0
self.ChatMessageText.textColor = .white
self.ChatMessageText.font = UIFont.systemFont(ofSize: 15, weight: UIFont.Weight.light)
let constraints = [
self.ChatMessageText.topAnchor.constraint(equalTo: topAnchor, constant: 16),
self.ChatMessageText.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -32),
self.ChatMessageText.widthAnchor.constraint(lessThanOrEqualToConstant: 220),
self.ChatBubble.topAnchor.constraint(equalTo: ChatMessageText.topAnchor, constant: -16),
self.ChatBubble.trailingAnchor.constraint(equalTo: ChatMessageText.trailingAnchor, constant: 16),
self.ChatBubble.bottomAnchor.constraint(equalTo: ChatMessageText.bottomAnchor, constant: 16),
self.ChatBubble.leadingAnchor.constraint(equalTo: ChatMessageText.leadingAnchor, constant: -16),
]
NSLayoutConstraint.activate(constraints)
self.leadingConstraint = self.ChatMessageText.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 32)
self.trailingConstraint = self.ChatMessageText.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -32)
}
My problem is this: I'm not feeding the UILabel with standard strings but with NSAttributedStrings, as I'd like to get some of the links in there clickable and parts of the text selectable by the user. So I've been told to use a UITextView instead of the UILabel. I've thus made the following 2 changes:
- Changed
var ChatMessageText = UILabel()
tovar ChatMessageText = UITextView()
- Did remove
self.ChatMessageText.numberOfLines = 0
as UITextView doesn't have anumberOfLines
member
Xcode doesn't complain and the app compiles and runs but it completely messes with my layout and I just can't figure out why. All the constraints from the UILabel should also work for the UITextView - at least I thought so. But here's how the screen looks like.
What am I doing wrong? Do I need to add / alter constraints?
回答1:
By default, a UITextView
has scrolling enabled.
While this seems obvious, that allows the user to enter more lines of text than will fit in the frame, and the user can scroll the text up and down.
In order for this to happen, UIKit
has to know the frame of the text view. If the frame is not set, UIKit
has no way to know how many lines to display, or how wide the view should be. So unless we have given the text view a full set of constraints, auto-layout will give it a size of .zero
. Even if given a width (or max width) constraint, auto-layout still doesn't know how many scrollable lines of text we want displayed.
Setting .isScrollEnabled = false
on the text view changes all of that.
Now, if we only constrained the position and width of the text view, UIKit
will calculate the height based on the content size of the .text
property.
This can be easily demonstrated. We'll create two text views, give them each top, leading and max-width (lessThanOrEqualTo) constraints, and the same text... but set .isScrollEnabled = false
on one of them:
class TextViewTestViewController: UIViewController {
let nonScrollingTextView = UITextView()
let scrollingTextView = UITextView()
override func viewDidLoad() {
super.viewDidLoad()
let s = "This is a test string to demonstrate UITextView size behavior."
[nonScrollingTextView, scrollingTextView].forEach {
tv in
tv.translatesAutoresizingMaskIntoConstraints = false
tv.font = UIFont.systemFont(ofSize: 17.0)
tv.text = s
view.addSubview(tv)
}
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
nonScrollingTextView.topAnchor.constraint(equalTo: g.topAnchor, constant: 40.0),
nonScrollingTextView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 40.0),
nonScrollingTextView.widthAnchor.constraint(lessThanOrEqualToConstant: 300.0),
scrollingTextView.topAnchor.constraint(equalTo: nonScrollingTextView.bottomAnchor, constant: 40.0),
scrollingTextView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 40.0),
scrollingTextView.widthAnchor.constraint(lessThanOrEqualToConstant: 300.0),
])
// disable scrolling on the "top" text view
nonScrollingTextView.isScrollEnabled = false
// top text view is cyan
nonScrollingTextView.backgroundColor = .cyan
// bottom text view is green (although we won't see it)
scrollingTextView.backgroundColor = .green
}
}
Result:
We've added two text views, but only disabled scrolling on the "top" one (cyan background). We don't even see the second one (green background), because auto-layout gives it a height of Zero.
Worth noting... if the text view has scrolling disabled and has editing enabled, it will automatically grow / shrink as the user adds / deletes text.
来源:https://stackoverflow.com/questions/60890289/layout-problems-after-replacing-uilabel-with-uitextview-in-a-uitableviewcell