Layout problems after replacing UILabel with UITextView in a UITableViewCell

断了今生、忘了曾经 提交于 2020-04-18 05:43:26

问题


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:

  1. Changed var ChatMessageText = UILabel()to var ChatMessageText = UITextView()
  2. Did remove self.ChatMessageText.numberOfLines = 0 as UITextView doesn't have a numberOfLines 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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!