techgirl
techgirl

Reputation: 323

Aligning UIImageView to the left and right programmatically in Swift

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

Answers (1)

Maximo Lucosi
Maximo Lucosi

Reputation: 388

enter image description here

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

Related Questions