From 7a1e566d528c4d14947b469628924032234840ac Mon Sep 17 00:00:00 2001 From: Benoit Martins Date: Fri, 17 Mar 2023 16:26:08 +0100 Subject: [PATCH] Display multiple images in Bubble Chat Message --- .../ChatConversationViewModel.swift | 5 +- .../ChatConversationTableViewSwift.swift | 5 - .../Views/ChatConversationViewSwift.swift | 4 +- .../Chat/Views/MultilineMessageCell.swift | 201 ++++++++++++++---- 4 files changed, 165 insertions(+), 50 deletions(-) diff --git a/Classes/Swift/Chat/ViewModels/ChatConversationViewModel.swift b/Classes/Swift/Chat/ViewModels/ChatConversationViewModel.swift index d1685a8d2..0ee272eac 100644 --- a/Classes/Swift/Chat/ViewModels/ChatConversationViewModel.swift +++ b/Classes/Swift/Chat/ViewModels/ChatConversationViewModel.swift @@ -283,10 +283,7 @@ class ChatConversationViewModel: ControlsViewModel { if type == "video" { image = UIChatBubbleTextCell.getImageFromVideoUrl(URL(fileURLWithPath: filePath ?? "")) } else if type == "image" { - let data = NSData(contentsOfFile: filePath ?? "") as Data? - if let data { - image = UIImage(data: data) - } + image = UIImage(named: filePath ?? "") } if let image { return image diff --git a/Classes/Swift/Chat/Views/ChatConversationTableViewSwift.swift b/Classes/Swift/Chat/Views/ChatConversationTableViewSwift.swift index 39bd56ec2..9855b8ff8 100644 --- a/Classes/Swift/Chat/Views/ChatConversationTableViewSwift.swift +++ b/Classes/Swift/Chat/Views/ChatConversationTableViewSwift.swift @@ -94,7 +94,6 @@ class ChatConversationTableViewSwift: UIViewController, UICollectionViewDataSour func scrollToMessage(message: ChatMessage){ let messageIndex = ChatConversationTableViewModel.sharedModel.getIndexMessage(message: message) - print("ChatConversationTableViewSwift collectionview \(messageIndex)") collectionView.reloadData() collectionView.layoutIfNeeded() @@ -121,15 +120,11 @@ class ChatConversationTableViewSwift: UIViewController, UICollectionViewDataSour if (!cell.replyContent.isHidden && event.chatMessage?.replyMessage != nil){ cell.replyContent.onClick { - print("\n\nChatConversationTableViewSwift collectionview new") - print("ChatConversationTableViewSwift collectionview \(indexPath.row+1)") self.scrollToMessage(message: (event.chatMessage?.replyMessage)!) } } } - - cell.contentView.transform = CGAffineTransform(scaleX: 1, y: -1) return cell } diff --git a/Classes/Swift/Chat/Views/ChatConversationViewSwift.swift b/Classes/Swift/Chat/Views/ChatConversationViewSwift.swift index e4348b28e..29be41e90 100644 --- a/Classes/Swift/Chat/Views/ChatConversationViewSwift.swift +++ b/Classes/Swift/Chat/Views/ChatConversationViewSwift.swift @@ -227,7 +227,7 @@ class ChatConversationViewSwift: BackActionsNavigationView, PHPickerViewControll ChatConversationViewModel.sharedModel.createChatConversation() topBar.backgroundColor = VoipTheme.voipToolbarBackgroundColor.get() - self.contentView.addSubview(tableController.tableView) + //self.contentView.addSubview(tableController.tableView) self.contentView.addSubview(tableControllerSwift.view) // Setup Autolayout constraints @@ -237,7 +237,7 @@ class ChatConversationViewSwift: BackActionsNavigationView, PHPickerViewControll tableControllerSwift.view.topAnchor.constraint(equalTo: self.contentView.topAnchor, constant: 0).isActive = true tableControllerSwift.view.rightAnchor.constraint(equalTo: self.contentView.rightAnchor, constant: 0).isActive = true - tableController.chatRoom = ChatConversationViewModel.sharedModel.chatRoom?.getCobject + //tableController.chatRoom = ChatConversationViewModel.sharedModel.chatRoom?.getCobject ChatConversationTableViewModel.sharedModel.chatRoom = ChatConversationViewModel.sharedModel.chatRoom refreshControl.addTarget(self, action: #selector(refreshData), for: .valueChanged) diff --git a/Classes/Swift/Chat/Views/MultilineMessageCell.swift b/Classes/Swift/Chat/Views/MultilineMessageCell.swift index 4142a2ac0..8faa19883 100644 --- a/Classes/Swift/Chat/Views/MultilineMessageCell.swift +++ b/Classes/Swift/Chat/Views/MultilineMessageCell.swift @@ -38,6 +38,7 @@ class MultilineMessageCell: UICollectionViewCell, UICollectionViewDataSource, UI var labelConstraints: [NSLayoutConstraint] = [] var labelTopConstraints: [NSLayoutConstraint] = [] var labelHiddenConstraints: [NSLayoutConstraint] = [] + var imagesGridConstraints : [NSLayoutConstraint] = [] var imageConstraints: [NSLayoutConstraint] = [] var videoConstraints: [NSLayoutConstraint] = [] var playButtonConstraints: [NSLayoutConstraint] = [] @@ -68,6 +69,7 @@ class MultilineMessageCell: UICollectionViewCell, UICollectionViewDataSource, UI let replyContentForMeetingSpacing = UIView() let replyMeetingSchedule = UIImageView() let mediaSelectorReply = UIView() + var collectionViewReply: UICollectionView = { let collection_view_reply_height = 60.0 let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout() @@ -82,10 +84,26 @@ class MultilineMessageCell: UICollectionViewCell, UICollectionViewDataSource, UI collectionViewReply.backgroundColor = .clear return collectionViewReply }() + var collectionViewImagesGrid: DynamicHeightCollectionView = { + let collection_view_reply_height = 138.0 + let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout() + layout.itemSize = CGSize(width: collection_view_reply_height, height: collection_view_reply_height) + layout.scrollDirection = .vertical + layout.minimumLineSpacing = 4 + layout.minimumInteritemSpacing = 4 + + let collectionViewImagesGrid = DynamicHeightCollectionView(frame: .zero, collectionViewLayout: layout) + collectionViewImagesGrid.translatesAutoresizingMaskIntoConstraints = false + collectionViewImagesGrid.backgroundColor = .clear + return collectionViewImagesGrid + }() var replyCollectionView : [UIImage] = [] var replyURLCollection : [URL] = [] + var imagesGridCollectionView : [UIImage] = [] + var imagesGridURLCollection : [URL] = [] + let imageViewBubble = UIImageView(image: UIImage(named: "chat_error")) let imageVideoViewBubble = UIImageView(image: UIImage(named: "file_video_default")) let imagePlayViewBubble = UIImageView(image: UIImage(named: "vr_play")) @@ -96,7 +114,6 @@ class MultilineMessageCell: UICollectionViewCell, UICollectionViewDataSource, UI var isPlayingVoiceRecording = false - override init(frame: CGRect) { super.init(frame: frame) @@ -271,7 +288,6 @@ class MultilineMessageCell: UICollectionViewCell, UICollectionViewDataSource, UI bubble.addSubview(contentViewBubble) contentViewBubble.translatesAutoresizingMaskIntoConstraints = false contentViewBubbleConstraints = [ - //contentViewBubble.topAnchor.constraint(equalTo: contentView.topAnchor), contentViewBubble.topAnchor.constraint(equalTo: preContentViewBubble.bottomAnchor), contentViewBubble.bottomAnchor.constraint(equalTo: contentView.bottomAnchor), contentViewBubble.leadingAnchor.constraint(equalTo: contentBubble.leadingAnchor), @@ -283,12 +299,28 @@ class MultilineMessageCell: UICollectionViewCell, UICollectionViewDataSource, UI contentViewBubble.addSubview(contentMediaViewBubble) contentMediaViewBubble.translatesAutoresizingMaskIntoConstraints = false contentMediaViewBubbleConstraints = [ - //contentViewBubble.topAnchor.constraint(equalTo: contentView.topAnchor), contentMediaViewBubble.topAnchor.constraint(equalTo: contentViewBubble.topAnchor), contentMediaViewBubble.leadingAnchor.constraint(equalTo: contentViewBubble.leadingAnchor), contentMediaViewBubble.trailingAnchor.constraint(equalTo: contentViewBubble.trailingAnchor) ] NSLayoutConstraint.activate(contentMediaViewBubbleConstraints) + + //Images Grid + contentMediaViewBubble.addSubview(collectionViewImagesGrid) + collectionViewImagesGrid.translatesAutoresizingMaskIntoConstraints = false + imagesGridConstraints = [ + collectionViewImagesGrid.topAnchor.constraint(equalTo: contentMediaViewBubble.topAnchor, constant: labelInset.top), + collectionViewImagesGrid.bottomAnchor.constraint(equalTo: contentMediaViewBubble.bottomAnchor, constant: labelInset.bottom), + collectionViewImagesGrid.leadingAnchor.constraint(equalTo: contentMediaViewBubble.leadingAnchor, constant: labelInset.left), + collectionViewImagesGrid.trailingAnchor.constraint(equalTo: contentMediaViewBubble.trailingAnchor, constant: labelInset.right), + ] + + collectionViewImagesGrid.dataSource = self + collectionViewImagesGrid.delegate = self + collectionViewImagesGrid.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "cellImagesGridMessage") + //collectionViewImagesGrid.size(w: 280, h: 500).done() + collectionViewImagesGrid.width(280).done() + collectionViewImagesGrid.isHidden = true //Image contentMediaViewBubble.addSubview(imageViewBubble) @@ -625,6 +657,7 @@ class MultilineMessageCell: UICollectionViewCell, UICollectionViewDataSource, UI NSLayoutConstraint.deactivate(labelConstraints) NSLayoutConstraint.deactivate(labelTopConstraints) NSLayoutConstraint.activate(labelHiddenConstraints) + NSLayoutConstraint.deactivate(imagesGridConstraints) NSLayoutConstraint.deactivate(imageConstraints) NSLayoutConstraint.deactivate(videoConstraints) NSLayoutConstraint.deactivate(playButtonConstraints) @@ -632,6 +665,7 @@ class MultilineMessageCell: UICollectionViewCell, UICollectionViewDataSource, UI NSLayoutConstraint.deactivate(recordingWaveConstraints) NSLayoutConstraint.deactivate(meetingConstraints) label.isHidden = true + collectionViewImagesGrid.isHidden = true imageViewBubble.isHidden = true imageVideoViewBubble.isHidden = true recordingView.isHidden = true @@ -656,12 +690,41 @@ class MultilineMessageCell: UICollectionViewCell, UICollectionViewDataSource, UI }else if content.type == "image"{ - if let imageMessage = UIImage(named: content.filePath){ - imageViewBubble.image = resizeImage(image: imageMessage, targetSize: CGSizeMake(UIScreen.main.bounds.size.width*3/4, 300.0)) + + if imagesGridCollectionView.count > 1 { + if(content.isFile){ + let indexPath = IndexPath(row: imagesGridCollectionView.count, section: 0) + imagesGridURLCollection.append(URL(string: content.filePath)!) + imagesGridCollectionView.append(getImageFrom(content.getCobject, filePath: content.filePath, forReplyBubble: true)!) + collectionViewImagesGrid.insertItems(at: [indexPath]) + } + + collectionViewImagesGrid.isHidden = false + NSLayoutConstraint.activate(imagesGridConstraints) + imageViewBubble.image = nil + NSLayoutConstraint.deactivate(imageConstraints) + imageViewBubble.isHidden = true + + }else{ + if let imageMessage = UIImage(named: content.filePath){ + imageViewBubble.image = resizeImage(image: imageMessage, targetSize: CGSizeMake(UIScreen.main.bounds.size.width*3/4, 300.0)) + } + + if(content.isFile){ + let indexPath = IndexPath(row: imagesGridCollectionView.count, section: 0) + imagesGridURLCollection.append(URL(string: content.filePath)!) + imagesGridCollectionView.append(getImageFrom(content.getCobject, filePath: content.filePath, forReplyBubble: true)!) + collectionViewImagesGrid.insertItems(at: [indexPath]) + } + + NSLayoutConstraint.activate(imageConstraints) + imageViewBubble.isHidden = false } - NSLayoutConstraint.activate(imageConstraints) - imageViewBubble.isHidden = false + + + + }else if content.type == "video"{ if let imageMessage = createThumbnailOfVideoFromFileURL(videoURL: content.filePath){ @@ -682,6 +745,9 @@ class MultilineMessageCell: UICollectionViewCell, UICollectionViewDataSource, UI }else{ //createBubbleOther() }} + if imagesGridCollectionView.count > 0 { + self.collectionViewImagesGrid.layoutIfNeeded() + } } }else{ contentBubble.isHidden = true @@ -817,43 +883,83 @@ class MultilineMessageCell: UICollectionViewCell, UICollectionViewDataSource, UI } func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { - return replyCollectionView.count + if(collectionView == collectionViewReply){ + return replyCollectionView.count + }else{ + return imagesGridCollectionView.count + } } @objc(collectionView:cellForItemAtIndexPath:) func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { - let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cellReplyMessage", for: indexPath) - let viewCell: UIView = UIView(frame: cell.contentView.frame) - cell.addSubview(viewCell) - let imageCell = replyCollectionView[indexPath.row] - var myImageView = UIImageView() - - if(FileType.init(replyURLCollection[indexPath.row].pathExtension)?.getGroupTypeFromFile() == FileType.file_picture_default.rawValue || FileType.init(replyURLCollection[indexPath.row].pathExtension)?.getGroupTypeFromFile() == FileType.file_video_default.rawValue){ - myImageView = UIImageView(image: imageCell) - }else{ - let fileNameText = replyURLCollection[indexPath.row].lastPathComponent - let fileName = SwiftUtil.textToImage(drawText:fileNameText, inImage:imageCell, forReplyBubble:false) - myImageView = UIImageView(image: fileName) - } - - myImageView.size(w: (viewCell.frame.width), h: (viewCell.frame.height)).done() - viewCell.addSubview(myImageView) - - if(FileType.init(replyURLCollection[indexPath.row].pathExtension)?.getGroupTypeFromFile() == FileType.file_video_default.rawValue){ - var imagePlay = UIImage() - if #available(iOS 13.0, *) { - imagePlay = (UIImage(named: "vr_play")!.withTintColor(.white)) - } else { - imagePlay = UIImage(named: "vr_play")! + if(collectionView == collectionViewReply){ + let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cellReplyMessage", for: indexPath) + let viewCell: UIView = UIView(frame: cell.contentView.frame) + cell.addSubview(viewCell) + let imageCell = replyCollectionView[indexPath.row] + var myImageView = UIImageView() + + if(FileType.init(replyURLCollection[indexPath.row].pathExtension)?.getGroupTypeFromFile() == FileType.file_picture_default.rawValue || FileType.init(replyURLCollection[indexPath.row].pathExtension)?.getGroupTypeFromFile() == FileType.file_video_default.rawValue){ + myImageView = UIImageView(image: imageCell) + }else{ + let fileNameText = replyURLCollection[indexPath.row].lastPathComponent + let fileName = SwiftUtil.textToImage(drawText:fileNameText, inImage:imageCell, forReplyBubble:false) + myImageView = UIImageView(image: fileName) } - let myImagePlayView = UIImageView(image: imagePlay) - viewCell.addSubview(myImagePlayView) - myImagePlayView.size(w: viewCell.frame.width/4, h: viewCell.frame.height/4).done() - myImagePlayView.alignHorizontalCenterWith(viewCell).alignVerticalCenterWith(viewCell).done() + + myImageView.size(w: (viewCell.frame.width), h: (viewCell.frame.height)).done() + viewCell.addSubview(myImageView) + + if(FileType.init(replyURLCollection[indexPath.row].pathExtension)?.getGroupTypeFromFile() == FileType.file_video_default.rawValue){ + var imagePlay = UIImage() + if #available(iOS 13.0, *) { + imagePlay = (UIImage(named: "vr_play")!.withTintColor(.white)) + } else { + imagePlay = UIImage(named: "vr_play")! + } + let myImagePlayView = UIImageView(image: imagePlay) + viewCell.addSubview(myImagePlayView) + myImagePlayView.size(w: viewCell.frame.width/4, h: viewCell.frame.height/4).done() + myImagePlayView.alignHorizontalCenterWith(viewCell).alignVerticalCenterWith(viewCell).done() + } + myImageView.contentMode = .scaleAspectFill + myImageView.clipsToBounds = true + + return cell + }else{ + let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cellImagesGridMessage", for: indexPath) + let viewCell: UIView = UIView(frame: cell.contentView.frame) + cell.addSubview(viewCell) + let imageCell = imagesGridCollectionView[indexPath.row] + let myImageView = UIImageView() + + if(FileType.init(imagesGridURLCollection[indexPath.row].pathExtension)?.getGroupTypeFromFile() == FileType.file_picture_default.rawValue || FileType.init(imagesGridURLCollection[indexPath.row].pathExtension)?.getGroupTypeFromFile() == FileType.file_video_default.rawValue){ + myImageView.image = imageCell + }else{ + let fileNameText = imagesGridURLCollection[indexPath.row].lastPathComponent + let fileName = SwiftUtil.textToImage(drawText:fileNameText, inImage:imageCell, forReplyBubble:false) + myImageView.image = fileName + } + + myImageView.size(w: (viewCell.frame.width), h: (viewCell.frame.height)).done() + viewCell.addSubview(myImageView) + + if(FileType.init(imagesGridURLCollection[indexPath.row].pathExtension)?.getGroupTypeFromFile() == FileType.file_video_default.rawValue){ + var imagePlay = UIImage() + if #available(iOS 13.0, *) { + imagePlay = (UIImage(named: "vr_play")!.withTintColor(.white)) + } else { + imagePlay = UIImage(named: "vr_play")! + } + let myImagePlayView = UIImageView(image: imagePlay) + viewCell.addSubview(myImagePlayView) + myImagePlayView.size(w: viewCell.frame.width/4, h: viewCell.frame.height/4).done() + myImagePlayView.alignHorizontalCenterWith(viewCell).alignVerticalCenterWith(viewCell).done() + } + myImageView.contentMode = .scaleAspectFill + myImageView.clipsToBounds = true + + return cell } - myImageView.contentMode = .scaleAspectFill - myImageView.clipsToBounds = true - - return cell } func getImageFrom(_ content: OpaquePointer?, filePath: String?, forReplyBubble: Bool) -> UIImage? { @@ -868,10 +974,14 @@ class MultilineMessageCell: UICollectionViewCell, UICollectionViewDataSource, UI if type == "video" { image = UIChatBubbleTextCell.getImageFromVideoUrl(URL(fileURLWithPath: filePath ?? "")) } else if type == "image" { + /* let data = NSData(contentsOfFile: filePath ?? "") as Data? if let data { image = UIImage(data: data) } + */ + + image = UIImage(named: filePath ?? "") } if let image { return image @@ -988,3 +1098,16 @@ class MultilineMessageCell: UICollectionViewCell, UICollectionViewDataSource, UI } } } + +class DynamicHeightCollectionView: UICollectionView { + override func layoutSubviews() { + super.layoutSubviews() + if !__CGSizeEqualToSize(bounds.size, self.intrinsicContentSize) { + self.invalidateIntrinsicContentSize() + } + } + + override var intrinsicContentSize: CGSize { + return contentSize + } +}