diff --git a/Example/ChatExample.xcodeproj/project.pbxproj b/Example/ChatExample.xcodeproj/project.pbxproj index d58ccf9f..357aaf55 100644 --- a/Example/ChatExample.xcodeproj/project.pbxproj +++ b/Example/ChatExample.xcodeproj/project.pbxproj @@ -7,6 +7,8 @@ objects = { /* Begin PBXBuildFile section */ + 032A15DD25965D9A00E00FE3 /* AlertService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 032A15DC25965D9A00E00FE3 /* AlertService.swift */; }; + 032A15E225965E1100E00FE3 /* CameraInputBarAccessoryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 032A15E125965E1100E00FE3 /* CameraInputBarAccessoryView.swift */; }; 13CCA04625793058005C19BB /* MessageKit in Frameworks */ = {isa = PBXBuildFile; productRef = 13CCA04525793058005C19BB /* MessageKit */; }; 13CCA06325793E24005C19BB /* Kingfisher in Frameworks */ = {isa = PBXBuildFile; productRef = 13CCA06225793E24005C19BB /* Kingfisher */; }; 1C5433DD24C389C300A5383B /* MessagesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C5433DC24C389C300A5383B /* MessagesView.swift */; }; @@ -62,6 +64,8 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 032A15DC25965D9A00E00FE3 /* AlertService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertService.swift; sourceTree = ""; }; + 032A15E125965E1100E00FE3 /* CameraInputBarAccessoryView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CameraInputBarAccessoryView.swift; sourceTree = ""; }; 13CCA04225793002005C19BB /* MessageKit */ = {isa = PBXFileReference; lastKnownFileType = folder; path = MessageKit; sourceTree = ""; }; 1C5433DC24C389C300A5383B /* MessagesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessagesView.swift; sourceTree = ""; }; 1C5433DE24C38DBF00A5383B /* SwiftUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftUI.framework; path = System/Library/Frameworks/SwiftUI.framework; sourceTree = SDKROOT; }; @@ -147,6 +151,7 @@ 1C5433E024C3A32400A5383B /* SwiftUI */, 385C2920211FF32E0010B4BA /* CustomCell.swift */, 385C2921211FF32E0010B4BA /* TableViewCells.swift */, + 032A15E125965E1100E00FE3 /* CameraInputBarAccessoryView.swift */, ); path = Views; sourceTree = ""; @@ -175,6 +180,7 @@ 385C292A211FF3450010B4BA /* Settings+UserDefaults.swift */, 385C292F211FF3630010B4BA /* UIColor+Extensions.swift */, 385C2930211FF3630010B4BA /* UIViewController+Extensions.swift */, + 032A15DC25965D9A00E00FE3 /* AlertService.swift */, ); path = Extensions; sourceTree = ""; @@ -516,6 +522,7 @@ 385C2932211FF3630010B4BA /* UIViewController+Extensions.swift in Sources */, 385C292B211FF3450010B4BA /* Settings+UserDefaults.swift in Sources */, 385C2945211FF38F0010B4BA /* NavigationController.swift in Sources */, + 032A15DD25965D9A00E00FE3 /* AlertService.swift in Sources */, 385C2948211FF38F0010B4BA /* ChatViewController.swift in Sources */, 385C2944211FF38F0010B4BA /* MessageContainerController.swift in Sources */, 383B9EB32172A1C4008AB91A /* MockUser.swift in Sources */, @@ -525,6 +532,7 @@ 1C5433DD24C389C300A5383B /* MessagesView.swift in Sources */, 882B5E811CF7D53600B6E160 /* AppDelegate.swift in Sources */, 385C292D211FF3520010B4BA /* CustomMessageFlowLayout.swift in Sources */, + 032A15E225965E1100E00FE3 /* CameraInputBarAccessoryView.swift in Sources */, 38CCCC592258419300DD5482 /* AutocompleteExampleViewController.swift in Sources */, 63ECDAC2241889630016C349 /* MessageSubviewContainerViewController.swift in Sources */, ); diff --git a/Example/Sources/Extensions/AlertService.swift b/Example/Sources/Extensions/AlertService.swift new file mode 100644 index 00000000..80eb4a5b --- /dev/null +++ b/Example/Sources/Extensions/AlertService.swift @@ -0,0 +1,28 @@ +// +// AlertService.swift +// ChatExample +// +// Created by Mohannad on 12/25/20. +// Copyright © 2020 MessageKit. All rights reserved. +// + +import Foundation +import UIKit + + +class AlertService { + + + static func showAlert(style: UIAlertController.Style, title: String?, message: String?, actions: [UIAlertAction] = [UIAlertAction(title: "Ok", style: .cancel, handler: nil)], completion: (() -> Swift.Void)? = nil) { + let alert = UIAlertController(title: title, message: message, preferredStyle: style) + for action in actions { + alert.addAction(action) + } + + UIApplication.shared.delegate?.window??.rootViewController?.present(alert, animated: true, completion: completion) + } + + +} + + diff --git a/Example/Sources/Resources/Assets.xcassets/Contents.json b/Example/Sources/Resources/Assets.xcassets/Contents.json index da4a164c..73c00596 100644 --- a/Example/Sources/Resources/Assets.xcassets/Contents.json +++ b/Example/Sources/Resources/Assets.xcassets/Contents.json @@ -1,6 +1,6 @@ { "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 } -} \ No newline at end of file +} diff --git a/Example/Sources/View Controllers/AdvancedExampleViewController.swift b/Example/Sources/View Controllers/AdvancedExampleViewController.swift index 9e79232a..bec0d5f2 100644 --- a/Example/Sources/View Controllers/AdvancedExampleViewController.swift +++ b/Example/Sources/View Controllers/AdvancedExampleViewController.swift @@ -105,7 +105,16 @@ final class AdvancedExampleViewController: ChatViewController { } override func configureMessageInputBar() { - super.configureMessageInputBar() + //super.configureMessageInputBar() + + messageInputBar = CameraInputBarAccessoryView() + messageInputBar.delegate = self + messageInputBar.inputTextView.tintColor = .primaryColor + messageInputBar.sendButton.setTitleColor(.primaryColor, for: .normal) + messageInputBar.sendButton.setTitleColor( + UIColor.primaryColor.withAlphaComponent(0.3), + for: .highlighted) + messageInputBar.isTranslucent = true messageInputBar.separatorLine.isHidden = true @@ -439,3 +448,28 @@ extension AdvancedExampleViewController: MessagesLayoutDelegate { } } + + +extension AdvancedExampleViewController: CameraInputBarAccessoryViewDelegate { + + func inputBar(_ inputBar: InputBarAccessoryView, didPressSendButtonWith attachments: [AttachmentManager.Attachment]) { + + + for item in attachments { + if case .image(let image) = item { + + self.sendImageMessage(photo: image) + } + } + inputBar.invalidatePlugins() + } + + + func sendImageMessage( photo : UIImage) { + + let photoMessage = MockMessage(image: photo, user: self.currentSender() as! MockUser, messageId: UUID().uuidString, date: Date()) + self.insertMessage(photoMessage) + } + +} + diff --git a/Example/Sources/Views/CameraInputBarAccessoryView.swift b/Example/Sources/Views/CameraInputBarAccessoryView.swift new file mode 100644 index 00000000..32501ee3 --- /dev/null +++ b/Example/Sources/Views/CameraInputBarAccessoryView.swift @@ -0,0 +1,199 @@ +// +// CameraInput.swift +// ChatExample +// +// Created by Mohannad on 12/25/20. +// Copyright © 2020 MessageKit. All rights reserved. +// + +import UIKit +import InputBarAccessoryView + +protocol CameraInputBarAccessoryViewDelegate : InputBarAccessoryViewDelegate { + func inputBar(_ inputBar: InputBarAccessoryView, didPressSendButtonWith attachments: [AttachmentManager.Attachment]) +} + +extension CameraInputBarAccessoryViewDelegate { + + func inputBar(_ inputBar: InputBarAccessoryView, didPressSendButtonWith attachments: [AttachmentManager.Attachment]){ + + } +} + + + class CameraInputBarAccessoryView: InputBarAccessoryView { + + + override init(frame: CGRect) { + super.init(frame: frame) + configure() + } + + + + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + lazy var attachmentManager: AttachmentManager = { [unowned self] in + let manager = AttachmentManager() + manager.delegate = self + return manager + }() + + func configure(){ + let camera = makeButton(named: "ic_camera") + camera.tintColor = .darkGray + camera.onTouchUpInside { (item) in + self.showImagePickerControllerActionSheet() + } + self.setLeftStackViewWidthConstant(to: 35, animated: true) + self.setStackViewItems([camera], forStack: .left, animated: false) + self.inputPlugins = [attachmentManager] + + } + + override func didSelectSendButton() { + + if attachmentManager.attachments.count > 0 { + + (delegate as? CameraInputBarAccessoryViewDelegate)?.inputBar(self, didPressSendButtonWith: attachmentManager.attachments) + } + else { + + delegate?.inputBar(self, didPressSendButtonWith: inputTextView.text) + } + } + + + + private func makeButton(named: String) -> InputBarButtonItem { + return InputBarButtonItem() + .configure { + $0.spacing = .fixed(10) + + if #available(iOS 13.0, *) { + $0.image = UIImage(systemName: "camera.fill")?.withRenderingMode(.alwaysTemplate) + } else { + $0.image = UIImage(named: named)?.withRenderingMode(.alwaysTemplate) + } + + $0.setSize(CGSize(width: 30, height: 30), animated: false) + }.onSelected { + $0.tintColor = .systemBlue + }.onDeselected { + $0.tintColor = UIColor.lightGray + }.onTouchUpInside { _ in + print("Item Tapped") + } + } + +} + + + + +extension CameraInputBarAccessoryView : UIImagePickerControllerDelegate , UINavigationControllerDelegate { + + @objc func showImagePickerControllerActionSheet() { + + + let photoLibraryAction = UIAlertAction(title: "Choose From Library", style: .default) { (action) in + self.showImagePickerController(sourceType: .photoLibrary) + } + + let cameraAction = UIAlertAction(title: "Take From Camera", style: .default) { (action) in + self.showImagePickerController(sourceType: .camera) + } + + let cancelAction = UIAlertAction(title: "Cancel", style: .default , handler: nil) + + AlertService.showAlert(style: .actionSheet, title: "Choose Your Image", message: nil, actions: [photoLibraryAction, cameraAction , cancelAction], completion: nil) + } + + func showImagePickerController(sourceType: UIImagePickerController.SourceType){ + + let imgPicker = UIImagePickerController() + imgPicker.delegate = self + imgPicker.allowsEditing = true + imgPicker.sourceType = sourceType + inputAccessoryView?.isHidden = true + getRootViewController()?.present(imgPicker, animated: true, completion: nil) + + } + + func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) { + + if let editedImage = info[ UIImagePickerController.InfoKey.editedImage] as? UIImage { + // self.sendImageMessage(photo: editedImage) + self.inputPlugins.forEach { _ = $0.handleInput(of: editedImage)} + + } + else if let originImage = info[ UIImagePickerController.InfoKey.originalImage] as? UIImage { + + self.inputPlugins.forEach { _ = $0.handleInput(of: originImage)} + //self.sendImageMessage(photo: originImage) + } + getRootViewController()?.dismiss(animated: true, completion: nil) + inputAccessoryView?.isHidden = false + + } + + + func imagePickerControllerDidCancel(_ picker: UIImagePickerController) { + getRootViewController()?.dismiss(animated: true, completion: nil) + inputAccessoryView?.isHidden = false + } + + + func getRootViewController() -> UIViewController? { + return (UIApplication.shared.delegate as? AppDelegate)?.window?.rootViewController + } + + +} + + +extension CameraInputBarAccessoryView: AttachmentManagerDelegate { + + + // MARK: - AttachmentManagerDelegate + + func attachmentManager(_ manager: AttachmentManager, shouldBecomeVisible: Bool) { + setAttachmentManager(active: shouldBecomeVisible) + } + + func attachmentManager(_ manager: AttachmentManager, didReloadTo attachments: [AttachmentManager.Attachment]) { + self.sendButton.isEnabled = manager.attachments.count > 0 + } + + func attachmentManager(_ manager: AttachmentManager, didInsert attachment: AttachmentManager.Attachment, at index: Int) { + self.sendButton.isEnabled = manager.attachments.count > 0 + } + + func attachmentManager(_ manager: AttachmentManager, didRemove attachment: AttachmentManager.Attachment, at index: Int) { + self.sendButton.isEnabled = manager.attachments.count > 0 + } + + func attachmentManager(_ manager: AttachmentManager, didSelectAddAttachmentAt index: Int) { + self.showImagePickerControllerActionSheet() + } + + // MARK: - AttachmentManagerDelegate Helper + + func setAttachmentManager(active: Bool) { + + let topStackView = self.topStackView + if active && !topStackView.arrangedSubviews.contains(attachmentManager.attachmentView) { + topStackView.insertArrangedSubview(attachmentManager.attachmentView, at: topStackView.arrangedSubviews.count) + topStackView.layoutIfNeeded() + } else if !active && topStackView.arrangedSubviews.contains(attachmentManager.attachmentView) { + topStackView.removeArrangedSubview(attachmentManager.attachmentView) + topStackView.layoutIfNeeded() + } + } +} + + +