Reputation: 323
I'm working on a chat app with message bubbles. I've figured out how to get the messages conditionally formatted (color, size, etc.) based on who sent the message; however I'm having difficulty getting the chat bubbles to align to the left or right side of the view controller. Right now all items are aligned to the left. I've searched through SO and was unable to find an answer so I decided to ask.
UPDATED: Showing remaining code of setCell function which determines the format of the message based on the value of 'incoming'. Also include tableView function to show cell configuration. The last function is the setup function.
func setCell(cell: ConversationCell, incoming: [Int], indexPath: IndexPath) {
var layoutAttribute: NSLayoutAttribute
var layoutConstant: CGFloat
var smavalayoutConstant: CGFloat
for i in 0 ..< self.incoming.count {
if (self.incoming[indexPath.row] == 1) {
cell.bubbleImageView.image=#imageLiteral(resourceName: "chat_bubble_received")
cell.messageLbl.textColor = UIColor.black
cell.messageLbl.textAlignment = .left
cell.messageLbl?.numberOfLines = 0
cell.messageLbl?.lineBreakMode = .byWordWrapping
layoutAttribute = .left
layoutConstant = 0
cell.contentView.addConstraint(NSLayoutConstraint(item: cell.bubbleImageView, attribute: .left, relatedBy: .equal, toItem: cell.contentView, attribute: layoutAttribute, multiplier: 1, constant: layoutConstant))
cell.contentView.addConstraint(NSLayoutConstraint(item: cell.smavaImg, attribute: .left, relatedBy: .equal, toItem: cell.contentView, attribute: layoutAttribute, multiplier: 1, constant: layoutConstant))
cell.contentView.addConstraint(NSLayoutConstraint(item: cell.postpictureImg, attribute: .left, relatedBy: .equal, toItem: cell.contentView, attribute: layoutAttribute, multiplier: 1, constant: layoutConstant))
}
if (self.incoming[indexPath.row] == 0) {
cell.bubbleImageView.image = #imageLiteral(resourceName: "chat_bubble_sent")
cell.messageLbl.textColor = UIColor.white
cell.messageLbl.textAlignment = .right
layoutAttribute = .right
layoutConstant = -100
smavalayoutConstant = 300
cell.contentView.addConstraint(NSLayoutConstraint(item: cell.bubbleImageView, attribute: .leftMargin, relatedBy: .lessThanOrEqual, toItem: cell.contentView, attribute: layoutAttribute, multiplier: 1, constant: layoutConstant))
cell.contentView.addConstraint(NSLayoutConstraint(item: cell.bubbleImageView, attribute: .right, relatedBy: .equal, toItem: cell.contentView, attribute: layoutAttribute, multiplier: 1, constant: layoutConstant))
cell.contentView.addConstraint(NSLayoutConstraint(item: cell.smavaImg, attribute: .right, relatedBy: .equal, toItem: cell.smavaImg, attribute: layoutAttribute, multiplier: 1, constant: smavalayoutConstant))
cell.contentView.addConstraint(NSLayoutConstraint(item: cell.smavaImg, attribute: .left, relatedBy: .equal, toItem: cell.smavaImg, attribute: .left, multiplier: 1, constant: 300))
}
}
}
//cell config
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! ConversationCell
// shortcuts
let hhpost = hhmessages[indexPath.row]
let image = avas[indexPath.row]
let smimages = images[indexPath.row]
let messagetext = hhpost["messagetext"] as? String
let date = hhpost["date"] as? String
cell.messageLbl.text = messagetext
cell.dateLbl.text = date
cell.smavaImg.image = image
cell.postpictureImg.image = smimages
DispatchQueue.main.async {
tableView.transform = CGAffineTransform(rotationAngle: -CGFloat.pi)
cell.transform = CGAffineTransform(rotationAngle: CGFloat.pi)
self.setCell(cell: cell, incoming: self.incoming, indexPath: indexPath)
}
return cell
}
public func setup() {
bubbleImageView = UIImageView(image: #imageLiteral(resourceName: "chat_bubble_sent"), highlightedImage: #imageLiteral(resourceName: "chat_bubble_sent"))
bubbleImageView.isUserInteractionEnabled = true
messageLbl = UILabel(frame: CGRect.zero)
messageLbl.font = UIFont.systemFont(ofSize: 15)
messageLbl.numberOfLines = 0
messageLbl.isUserInteractionEnabled = true
selectionStyle = .none
contentView.addSubview(bubbleImageView)
contentView.addSubview(smavaImg)
bubbleImageView.addSubview(messageLbl)
messageLbl.translatesAutoresizingMaskIntoConstraints = false
bubbleImageView.translatesAutoresizingMaskIntoConstraints = false
contentView.addConstraint(NSLayoutConstraint(item: bubbleImageView, attribute: .left, relatedBy: .equal, toItem: contentView, attribute: .left, multiplier: 1, constant: 10))
contentView.addConstraint(NSLayoutConstraint(item: bubbleImageView, attribute: .top, relatedBy: .equal, toItem: contentView, attribute: .top, multiplier: 1, constant: 4.5))
bubbleImageView.addConstraint(NSLayoutConstraint(item: bubbleImageView, attribute: .width, relatedBy: .equal, toItem: messageLbl, attribute: .width, multiplier: 1, constant: 30))
contentView.addConstraint(NSLayoutConstraint(item: bubbleImageView, attribute: .bottom, relatedBy: .equal, toItem: contentView, attribute: .bottom, multiplier: 1, constant: -4.5))
bubbleImageView.addConstraint(NSLayoutConstraint(item: messageLbl, attribute: .centerX, relatedBy: .equal, toItem: bubbleImageView, attribute: .centerX, multiplier: 1, constant: -2))
bubbleImageView.addConstraint(NSLayoutConstraint(item: messageLbl, attribute: .centerY, relatedBy: .equal, toItem: bubbleImageView, attribute: .centerY, multiplier: 1, constant: -0.5))
messageLbl.preferredMaxLayoutWidth = 218
bubbleImageView.addConstraint(NSLayoutConstraint(item: messageLbl, attribute: .height, relatedBy: .equal, toItem: bubbleImageView, attribute: .height, multiplier: 1, constant: -15))
contentView.addConstraint(NSLayoutConstraint(item: smavaImg, attribute: .centerX, relatedBy: .equal, toItem: smavaImg, attribute: .centerX, multiplier: 1, constant: -2))
contentView.addConstraint(NSLayoutConstraint(item: smavaImg, attribute: .centerY, relatedBy: .equal, toItem: smavaImg, attribute: .centerY, multiplier: 1, constant: -0.5))
}
Upvotes: 0
Views: 2303
Reputation: 388
Take a look in my git hub project in your Playground // Maximo Lucosi: https://github.com/lucosi/ChatTableView You can create your tableViewCell with all constraints and change the constraints inside the cell. Open the project in your Playgournd > View > Assistant Editor > Show Assistant Editor
class MyViewController: UIViewController {
// Messages for test
var messages: [Message] = []
// Table View here + basic configuration
lazy var tableView: UITableView = {
let view = UITableView()
view.translatesAutoresizingMaskIntoConstraints = false
view.delegate = self
view.dataSource = self
view.backgroundColor = .white
view.separatorStyle = .none
return view
}()
override func viewDidLoad() {
super.viewDidLoad()
// Set the screen size befor implement layour. NOTICE: Only for playground test
self.view.frame = frame
// Messages for test //
self.messages.append(Message.init(message: "I'm working on a chat app with message bubbles.",
image: UIImage.init(),
incoming: 0))
self.messages.append(Message.init(message: "What is your dificulte.",
image: UIImage(),
incoming: 1))
self.messages.append(Message.init(message: "I'm having difficulty getting the chat bubbles to align to the left or right side of the view controller.",
image: UIImage.init(),
incoming: 0))
self.messages.append(Message.init(message: "One more for me",
image: UIImage(),
incoming: 1))
self.messages.append(Message.init(message: "I have already implemented a function that redraws UILabels with a passed ratio. So all I need to find is the text in UILabel from my view that would require the maximum ratio to redraw UILabels. So finally I need to do something like this:",
image: UIImage(), incoming: 1))
// End //
// Add the tableView
self.view.addSubview(tableView)
// Register teh cell in the tableView
self.tableView.register(ConversationCell.self, forCellReuseIdentifier: cellId)
// Criate a contraint for the table view
NSLayoutConstraint.activate(
[
tableView.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 44),
tableView.leftAnchor.constraint(equalTo: self.view.leftAnchor),
tableView.rightAnchor.constraint(equalTo: self.view.rightAnchor),
tableView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor)
]
)
}
}
extension String {
//* Calculeta the hight string Function
func calculateTextFrameRect(
objectsInPlaceHeight: CGFloat,
objectsInPlaceWidth: CGFloat,
fontSize: CGFloat,
fontWeight: CGFloat) -> CGSize
{
let bounding = CGSize(width: UIScreen.main.bounds.width - objectsInPlaceWidth, height: .infinity)
let rect = NSString(string: self).boundingRect(
with: bounding,
options: NSStringDrawingOptions.usesFontLeading.union(NSStringDrawingOptions.usesLineFragmentOrigin),
attributes: [NSAttributedStringKey.font: UIFont.systemFont(ofSize: fontSize, weight: UIFont.Weight(rawValue: fontWeight))],
context: nil)
return CGSize(width: UIScreen.main.bounds.width, height: rect.height + objectsInPlaceHeight )
}
}
// Conform table view with delegate and data source extension MyViewController: UITableViewDelegate, UITableViewDataSource {
// Change the hight of the cell
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
// Calculate the hight of the cell
let text = self.messages[indexPath.item].message
let frameSize = text.calculateTextFrameRect(
objectsInPlaceHeight: 44 + 10,
objectsInPlaceWidth: 20 + 44 + 20 + self.view.frame.width * 0.4,
fontSize: UIFont.preferredFont(forTextStyle: UIFontTextStyle.body).pointSize,
fontWeight: UIFont.Weight.medium.rawValue)
return frameSize.height
}
// Number os cells
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.messages.count
}
// Return cell to display on the tableView
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath) as! ConversationCell
cell.messageData = self.messages[indexPath.item]
return cell
}
}
//****** Custom cell class ******/// class ConversationCell: UITableViewCell {
var messageData: Message? {
didSet {
// Umrap the value incide the cell
if let message = messageData {
// Confiture the constraints for cell
if message.incoming == 0 {
// Text
self.messageTextView.textAlignment = .left
self.bubleImage.backgroundColor = .orange
// Constraints
self.lefBubleConstraint.isActive = true
self.rightBubleConstraint.isActive = false
} else {
// Text
self.messageTextView.textAlignment = .right
self.bubleImage.backgroundColor = .blue
// Constraints
self.lefBubleConstraint.isActive = false
self.rightBubleConstraint.isActive = true
}
if lefBubleConstraint.isActive == true {
self.leftMessageLable.isActive = true
self.rightMessageLable.isActive = false
} else {
self.leftMessageLable.isActive = false
self.rightMessageLable.isActive = true
}
// Set the data
self.messageTextView.text = message.message
self.bubleImage.image = message.image
}
}
}
// Create and config the image
let bubleImage: UIImageView = {
let view = UIImageView()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = .blue
view.layer.cornerRadius = 22
view.layer.masksToBounds = true
return view
}()
// Create and config the lable for the text
let messageTextView: UITextView = {
let view = UITextView()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = UIColor.lightGray.withAlphaComponent(0.5)
view.layer.cornerRadius = 10
view.textContainerInset = UIEdgeInsetsMake(5, 5, 5, 5)
view.isUserInteractionEnabled = false
return view
}()
// Constraints for configuration on didSet data
var lefBubleConstraint: NSLayoutConstraint!
var rightBubleConstraint: NSLayoutConstraint!
var leftMessageLable: NSLayoutConstraint!
var rightMessageLable: NSLayoutConstraint!
// Init the cell with local congiguration
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: nil)
self.addSubview(bubleImage)
self.addSubview(messageTextView)
// Permanent constraints
NSLayoutConstraint.activate(
[
self.bubleImage.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -10),
self.bubleImage.heightAnchor.constraint(equalToConstant: 44),
self.bubleImage.widthAnchor.constraint(equalToConstant: 44),
self.messageTextView.topAnchor.constraint(equalTo: self.topAnchor, constant: 10),
self.messageTextView.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -10),
// NOTICE: Use the frame with as parameter for mesure the hight of cell on the main view
self.messageTextView.widthAnchor.constraint(equalToConstant: self.frame.width * 0.7)
]
)
// Buble constraint for configuration
self.lefBubleConstraint = self.bubleImage.leftAnchor.constraint(equalTo: self.leftAnchor, constant: 10)
self.rightBubleConstraint = self.bubleImage.rightAnchor.constraint(equalTo: self.rightAnchor, constant: -10)
// Message constrait for congiguration
self.rightMessageLable = self.messageTextView.rightAnchor.constraint(equalTo: self.bubleImage.leftAnchor, constant: -10)
self.leftMessageLable = self.messageTextView.leftAnchor.constraint(equalTo: self.bubleImage.rightAnchor, constant: 10)
}
// requerid init
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Upvotes: 1