Auto Sizing Chat Messages
Tinder Firestore Swipe and Match
For this lesson, let's talk all about auto sizing message cells and how to shift alignment from left to right. Doing the alignment correctly within one cell makes it very easy to toggle things based on a chat message's properties later on. We'll also look at some bug fixes at the end.

Comments (10)
dhekstro
4 years ago
Hi there Brian, hope you’re doing well. Not sure if this is the right place to ask you this and don’t know if you have ever stumbled upon this necessity: I’m building an e-commerce app using Tabs to house different pages/functionalities of the app. Among other things I have a cart defined as my right BarButtonItem, thus visible all the time across all the tabs. I need to update the quantity badge on the top of the cart icon accordingly to the user actions (adding or removing products). I haven’t figured out how to access and change the state of the cart dynamically. Do you have any hack/suggestions on how to approach this situation elegantly ?! Be struggling with this for days and haven’t found any good solution for it. I’d appreciate your guidance. Thanks in advance.
Dennisvm82
4 years ago
I use the following code to update my right BarButtonItem, but I am currently working with local data to accomplish this. With an API and database connection, the update functionality will be a little bit different, since it will probably have to remember each customer's cart total until emptied. // My custom badge button class BadgeButton: UIButton { var badgeLabel = UILabel() var badge: String? { didSet { addBadgeToButton(badge: badge) } } public var badgeEdgeInsets: UIEdgeInsets? { didSet { addBadgeToButton(badge: badge) } } override init(frame: CGRect) { super.init(frame: frame) addBadgeToButton(badge: nil) } func addBadgeToButton(badge: String?) { guard let badgeTotal = badge else { return } badgeLabel.attributedText = NSMutableAttributedString().appendWith(color: .white, weight: .regular, ofSize: 15, badgeTotal) badgeLabel.backgroundColor =.red badgeLabel.sizeToFit() badgeLabel.textAlignment = .center let badgeSize = badgeLabel.frame.size let height = max(18, Double(badgeSize.height) + 5) let width = max(height, Double(badgeSize.width) + 10) var vertical: Double?, horizontal: Double? if let badgeInset = self.badgeEdgeInsets { vertical = Double(badgeInset.top) - Double(badgeInset.bottom) horizontal = Double(badgeInset.left) - Double(badgeInset.right) badgeLabel.frame = CGRect(x: (Double(bounds.size.width) - 10 + horizontal!), y: -(Double(badgeSize.height) / 2) - 10 + vertical!, width: width, height: height) } else { badgeLabel.frame = CGRect(x: self.frame.width - CGFloat((width / 2)), y: CGFloat(-(height / 2)), width: CGFloat(width), height: CGFloat(height)) } badgeLabel.layer.cornerRadius = badgeLabel.frame.height / 2 badgeLabel.layer.masksToBounds = true addSubview(badgeLabel) badgeLabel.isHidden = badge != nil ? false : true if badgeLabel.text == "0" { badgeLabel.isHidden = true } } required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) self.addBadgeToButton(badge: nil) fatalError() } } // Create the navigation bar item fileprivate func createChatIcon(itemvalue: String) { chatButton.frame = CGRect(x: 0, y: 0, width: 44, height: 44) chatButton.badgeEdgeInsets = UIEdgeInsets(top: 20, left: 0, bottom: 0, right: 15) chatButton.badge = itemvalue chatButton.tintColor = .white chatButton.setImage(UIImage(named: "chat")?.withRenderingMode(.alwaysTemplate), for: .normal) chatButton.addTarget(self, action: #selector(handleLaunchChatOverview), for: .touchUpInside) self.navigationItem.rightBarButtonItem = UIBarButtonItem(customView: chatButton) } // Itemvalue can be set to 0 if you want. I am working on a chat functionality, so I need to load the total unread messages from the server when completed. This will be my initial value :-) _ = createChatIcon(itemvalue: "\(userViewModel.first?.unreadMessages ?? 0)" var itemTotal = 0 @objc fileprivate func handleAddItem() { itemTotal += 1 refreshTotal() } @objc fileprivate func handleRemoveItem() { itemTotal -= 1 refreshTotal() } fileprivate func refreshTotal() { chatButton.badge = "\(itemTotal)" }
dhekstro
4 years ago
Hey Dennisvm82, thanks for the code snippet. Will test your solution.
Dennisvm82
4 years ago
Don't forget to create an instance at the top of your controller ;-) forgot to add this to the snippet! let chatButton = BadgeButton()
ZpecterZ80
4 years ago
Hey Brian, how could we use this custom layer to shape the bubble? https://medium.com/@dima_nikolaev/creating-a-chat-bubble-which-looks-like-a-chat-bubble-in-imessage-the-advanced-way-2d7497d600ba
Brian Voong
4 years ago
李承諴
4 years ago
Hi Brian Could you provide where you find the solution of estimated textview size?
Cinquain
4 years ago
Brian was lit in this video
Dino32
3 years ago
HI Brian, I don't understand why, but this code doesn't working. Maybe it is because of iOS 13 and Xcode 11. I've checked the source code. I did all what you did but it was vain. Messages size still have the size kind of 1000, for estimatedSize doesn't work at all. let estimatedSizeCell = MessageCell(frame: .init(x: 0, y: 0, width: view.frame.width, height: 1000)) estimatedSizeCell.item = self.items[indexPath.item] estimatedSizeCell.layoutIfNeeded() let estimatedSize = estimatedSizeCell.systemLayoutSizeFitting(.init(width: view.frame.width, height: 1000)) return .init(width: view.frame.width, height: estimatedSize.height)
Brian Voong
3 years ago
Dino32
3 years ago
Sorry:) I've found where the problem is. I've overlook that - tv.isScrollEnabled = false
Dino32
3 years ago
Sorry:) I've found where the problem is. I've overlook that - tv.isScrollEnabled = false
Dino32
3 years ago
Hi Brian, Thanks for the another good lesson! Seams like the scrollIndicatorInsets was deprecated in iOS 13. I was trying to replace to collectionView.verticalScrollIndicatorInsets.top/left/right = navBarHeight but it is not worked. I haven't found the solution so far, nothing critical.
Dino32
3 years ago
It terns out that it was easy. I use this one "collectionView.verticalScrollIndicatorInsets.top = navBarHeight" The scroll appears only if toggle keyboard.
Dino32
3 years ago
Hi Brian, The download project link is broken. Pease fix it.
Brian Voong
3 years ago
aymather
3 years ago
Damnit man, I love your courses, and this was the lesson I was probably the most excited to learn about. But you literally said something along the lines of, "This is pretty confusing if you don't know how to do it, but that's just what you need to do," soooooo many times rather than at least trying to explain what you were writing. Again, I love your content, and I appreciate you doing this so much, but just saying things just to fill empty space in talking really doesn't help. If you need to, take a minute to write something, but don't forget to go back over and explain what it is you just did, why you did it, and what exactly happened in each step. <3
aymather
3 years ago
I ended up finding a stack overflow post that simplifies the process of sizing your cells by a lot. I took the approach of initiating my collection views without LBTATools and created my own UICollectionViewFlowLayout. Anyway, if you're watching this and you took that approach, here's the solution. let layout = UICollectionViewFlowLayout() layout.scrollDirection = .vertical layout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
aymather
3 years ago
Also here's a link to the stack overflow post I got it from: https://stackoverflow.com/questions/25895311/uicollectionview-self-sizing-cells-with-auto-layout
Brian Voong
3 years ago
HELP & SUPPORT