无法在聊天表视图中创建带有聊天气泡的自定义msg lbl。 (全部以编程方式)

时间:2019-08-26 14:40:39

标签: ios swift xcode

  1. 我有1个类(创建贝塞尔曲线)(工作中)
  2. 在tableviewcell类中,我正在使用函数以编程方式创建味精标签
  3. 我在带有表视图的视图控制器中使用该功能
  4. 下面的图片来自相同的代码。
  5. 用于数据的数组为[“ a”,“ aa”,“ aaa”,“ aaaa”,“ aaaaa”]

问题=味精标签未显示/正在创建

我已经提供了所有必需的东西。代码中可见的函数直接在tableview的cellforrowat函数中调用。

图像1 = [单击按钮后的层次结构视图1](https://ibb.co/QfvsV3C) 图片2 = [点击按钮之前的层次结构视图2](https://ibb.co/L0pz2kz

msg lbl代码:

func drawMsgLbl(text: String)
{
    //var msg                 =   UILabel()
    //msg                     =   UILabel()
    //msg?.lineBreakMode      =   .byWordWrapping
    msg.numberOfLines       =   0
    msg.font                =   UIFont.init(name: "Avenir", size: 20)
    msg.text                =   text
    let txtWidth            =   text.size(OfFont: msg.font)
    msg.textColor           =   .black
    msg.backgroundColor     =   .yellow
//        msg.frame = CGRect(x: 0, y: 0, width: msg.intrinsicContentSize.width, height: msg.intrinsicContentSize.height)

    let constraintRect = CGSize(width: (0.66 * self.frame.width),
                                height: .greatestFiniteMagnitude)
    let boundingBox = text.boundingRect(with: constraintRect,
                                        options: .usesLineFragmentOrigin,
                                        attributes: [.font: msg.font],
                                        context: nil)
    msg.frame.size = CGSize(width: ceil(boundingBox.width),
                             height: ceil(boundingBox.height))

    print("Bounnding Width = \(boundingBox.width)")
    print("msgIntrinsicWidth = \(msg.intrinsicContentSize.width)")
    print("Text Width = \(txtWidth)")
    print(text)


    msg.frame.origin = CGPoint(x: (self.frame.size.width)-((msg.frame.size.width))-20, y: (self.frame.height)+10)
    //msg?.center = self.center

    let bubbleSize = CGSize(width: ((msg.frame.size.width)+30), height: ((msg.frame.size.height)+20))

    let incomingBubblee = incomingBubble()
    let outgoingBubblee = outgoingBubble()

    outgoingBubblee.frame.size = bubbleSize
    outgoingBubblee.frame.origin = CGPoint(x: (frame.size.width)-((msg.frame.size.width))-30, y: frame.size.height)
    outgoingBubblee.backgroundColor = .clear

    self.addSubview(outgoingBubblee)
    self.addSubview(msg)
    self.backgroundColor = .gray
}

我想根据使用的Bezier路径生成文本来生成带有宽度和高度的味精标签(其代码单独放置,代码工作正常)

1 个答案:

答案 0 :(得分:1)

这是一个非常简单的示例,使用自动布局约束来允许单元格/行自动调整其高度。

全部通过代码-没有@IBOutlets或原型单元格-因此只需将UITableViewController分配给此类(ChatBubbleTestTableViewController

// simple "Message" struct
struct MyMessage {
    var incoming: Bool = false
    var message: String = ""
}

class ChatBubbleView: UIView {

    let bubbleLayer = CAShapeLayer()

    let chatLabel: UILabel = {
        let v = UILabel()
        v.translatesAutoresizingMaskIntoConstraints = false
        v.numberOfLines = 0
        v.text = "Sample text"
        return v
    }()

    // if it's an incoming message, background will be gray and bubble left-aligned
    // otherwise background will be green and bubble right-alinged
    var incoming = false

    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        commonInit()
    }

    func commonInit() -> Void {

        // add the bubble layer
        layer.addSublayer(bubbleLayer)

        // add the label
        addSubview(chatLabel)

        // constrain the label with 12-pts padding on all 4 sides
        NSLayoutConstraint.activate([
            chatLabel.topAnchor.constraint(equalTo: topAnchor, constant: 12.0),
            chatLabel.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -12.0),
            chatLabel.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 12.0),
            chatLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -12.0),
            ])

    }

    override func layoutSubviews() {
        super.layoutSubviews()

        let width = bounds.size.width
        let height = bounds.size.height

        let bezierPath = UIBezierPath()

        // NOTE: this bezier path is from
        // https://medium.com/@dima_nikolaev/creating-a-chat-bubble-which-looks-like-a-chat-bubble-in-imessage-the-advanced-way-2d7497d600ba
        if incoming {
            bezierPath.move(to: CGPoint(x: 22, y: height))
            bezierPath.addLine(to: CGPoint(x: width - 17, y: height))
            bezierPath.addCurve(to: CGPoint(x: width, y: height - 17), controlPoint1: CGPoint(x: width - 7.61, y: height), controlPoint2: CGPoint(x: width, y: height - 7.61))
            bezierPath.addLine(to: CGPoint(x: width, y: 17))
            bezierPath.addCurve(to: CGPoint(x: width - 17, y: 0), controlPoint1: CGPoint(x: width, y: 7.61), controlPoint2: CGPoint(x: width - 7.61, y: 0))
            bezierPath.addLine(to: CGPoint(x: 21, y: 0))
            bezierPath.addCurve(to: CGPoint(x: 4, y: 17), controlPoint1: CGPoint(x: 11.61, y: 0), controlPoint2: CGPoint(x: 4, y: 7.61))
            bezierPath.addLine(to: CGPoint(x: 4, y: height - 11))
            bezierPath.addCurve(to: CGPoint(x: 0, y: height), controlPoint1: CGPoint(x: 4, y: height - 1), controlPoint2: CGPoint(x: 0, y: height))
            bezierPath.addLine(to: CGPoint(x: -0.05, y: height - 0.01))
            bezierPath.addCurve(to: CGPoint(x: 11.04, y: height - 4.04), controlPoint1: CGPoint(x: 4.07, y: height + 0.43), controlPoint2: CGPoint(x: 8.16, y: height - 1.06))
            bezierPath.addCurve(to: CGPoint(x: 22, y: height), controlPoint1: CGPoint(x: 16, y: height), controlPoint2: CGPoint(x: 19, y: height))
            bezierPath.close()
        } else {
            bezierPath.move(to: CGPoint(x: width - 22, y: height))
            bezierPath.addLine(to: CGPoint(x: 17, y: height))
            bezierPath.addCurve(to: CGPoint(x: 0, y: height - 17), controlPoint1: CGPoint(x: 7.61, y: height), controlPoint2: CGPoint(x: 0, y: height - 7.61))
            bezierPath.addLine(to: CGPoint(x: 0, y: 17))
            bezierPath.addCurve(to: CGPoint(x: 17, y: 0), controlPoint1: CGPoint(x: 0, y: 7.61), controlPoint2: CGPoint(x: 7.61, y: 0))
            bezierPath.addLine(to: CGPoint(x: width - 21, y: 0))
            bezierPath.addCurve(to: CGPoint(x: width - 4, y: 17), controlPoint1: CGPoint(x: width - 11.61, y: 0), controlPoint2: CGPoint(x: width - 4, y: 7.61))
            bezierPath.addLine(to: CGPoint(x: width - 4, y: height - 11))
            bezierPath.addCurve(to: CGPoint(x: width, y: height), controlPoint1: CGPoint(x: width - 4, y: height - 1), controlPoint2: CGPoint(x: width, y: height))
            bezierPath.addLine(to: CGPoint(x: width + 0.05, y: height - 0.01))
            bezierPath.addCurve(to: CGPoint(x: width - 11.04, y: height - 4.04), controlPoint1: CGPoint(x: width - 4.07, y: height + 0.43), controlPoint2: CGPoint(x: width - 8.16, y: height - 1.06))
            bezierPath.addCurve(to: CGPoint(x: width - 22, y: height), controlPoint1: CGPoint(x: width - 16, y: height), controlPoint2: CGPoint(x: width - 19, y: height))
            bezierPath.close()
        }

        bubbleLayer.fillColor = incoming ? UIColor(white: 0.90, alpha: 1.0).cgColor : UIColor.green.cgColor

        bubbleLayer.path = bezierPath.cgPath

    }
}

class ChatCell: UITableViewCell {

    let bubbleView: ChatBubbleView = {
        let v = ChatBubbleView()
        v.translatesAutoresizingMaskIntoConstraints = false
        return v
    }()

    var leadingOrTrailingConstraint = NSLayoutConstraint()

    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        commonInit()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        commonInit()
    }

    func commonInit() -> Void {

        // add the bubble view
        contentView.addSubview(bubbleView)

        // constrain top / bottom with 12-pts padding
        // constrain width to lessThanOrEqualTo 2/3rds (66%) of the width of the cell
        NSLayoutConstraint.activate([
            bubbleView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 12.0),
            bubbleView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -12.0),
            bubbleView.widthAnchor.constraint(lessThanOrEqualTo: contentView.widthAnchor, multiplier: 0.66),
            ])

    }

    func setData(_ message: MyMessage) -> Void {

        // set the label text
        bubbleView.chatLabel.text = message.message

        // tell the bubble view whether it's an incoming or outgoing message
        bubbleView.incoming = message.incoming

        // left- or right-align the bubble view, based on incoming or outgoing
        leadingOrTrailingConstraint.isActive = false
        if message.incoming {
            leadingOrTrailingConstraint = bubbleView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 12.0)
        } else {
            leadingOrTrailingConstraint = bubbleView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -12.0)
        }
        leadingOrTrailingConstraint.isActive = true
    }

}

class ChatBubbleTestTableViewController: UITableViewController {

    let theData: [MyMessage] = [
        MyMessage(incoming: false, message: "A short message."),
        MyMessage(incoming: true, message: "A medium length message, longer than short."),
        MyMessage(incoming: false, message: "A long message. This one should be long enough to wrap onto multiple lines, showing that this message bubble cell will auto-size itself to the message content."),
        MyMessage(incoming: true, message: "Another short message."),
        MyMessage(incoming: false, message: "Another medium length message, longer than short."),
        MyMessage(incoming: true, message: "Another long message. This one should be long enough to wrap onto multiple lines, showing that this message bubble cell will auto-size itself to the message content."),
    ]

    override func viewDidLoad() {
        super.viewDidLoad()

        tableView.register(ChatCell.self, forCellReuseIdentifier: "ChatCell")
    }

    override func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return theData.count
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "ChatCell", for: indexPath) as! ChatCell
        cell.setData(theData[indexPath.row])
        return cell
    }

}

结果:

enter image description here

,请注意,它会在单元格更改大小(例如旋转设备)时自动调整大小:

enter image description here


编辑

您的代码有问题...

ChatCell.swift中,您拥有:

func commonInit()
{
    contentView.addSubview(bubbleView)

    NSLayoutConstraint.activate([
        bubbleView.topAnchor.constraint(equalTo: topAnchor, constant: 12.0),
        bubbleView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -12.0),
        bubbleView.widthAnchor.constraint(lessThanOrEqualTo: contentView.widthAnchor, multiplier: 0.66)
        ])
}

bubbleView需要限制在单元格的contentView中:

func commonInit()
{
    contentView.addSubview(bubbleView)

    NSLayoutConstraint.activate([
        bubbleView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 12.0),
        bubbleView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -12.0),
        bubbleView.widthAnchor.constraint(lessThanOrEqualTo: contentView.widthAnchor, multiplier: 0.66)
        ])
}

编辑2

更改气泡背景颜色的最直接方法...

ChatBubbleView类中,删除此行(在layoutSubviews()的末尾:

bubbleLayer.fillColor = isIncoming ? UIColor(white: 0.90, alpha: 1.0).cgColor : UIColor.green.cgColor

ChatCell类中,添加两个属性:

var incomingColor: UIColor = UIColor(white: 0.90, alpha: 1.0)
var outgoingColor: UIColor = UIColor.green

这些将是您的“默认”颜色。

也在ChatCell类的setData()中,添加以下行:

    bubbleView.bubbleLayer.fillColor = message.incoming ? incomingColor.cgColor : outgoingColor.cgColor

然后,在TestVC的{​​{1}}中,您可以执行以下操作:

cellForRowAt

我将取决于您要如何实现用户的颜色选择。