to be honest i dont quite understand what I'm doing here is the code that I tried to use to play the video it builds okay but when i press the button i get a "sigabrt" warning and it doesnt work.
here's the code:
import UIKit
import AVKit
import AVFoundation
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
#IBAction func VideoPlayer(_ sender: Any) {
guard let path = Bundle.main.path(forResource: "video", ofType:"mp4") else {
debugPrint("video.m4v not found")
return
}
let player = AVPlayer(url: URL(fileURLWithPath: path))
let playerController = AVPlayerViewController()
playerController.player = player
present(playerController, animated: true) {
player.play()
}
}
}
}
Try this.
import UIKit
import AVKit
class ViewController: UIViewController {
var avPlayer: AVPlayer!
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func VideoPlayer(_ sender: Any) {
playVideo()
}
func playVideo() {
let filepath: String? = Bundle.main.path(forResource: "videoplayback", ofType: "mp4")
let fileURL = URL.init(fileURLWithPath: filepath!)
avPlayer = AVPlayer(url: fileURL)
let avPlayerController = AVPlayerViewController()
avPlayerController.player = avPlayer
avPlayerController.view.frame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.size.width, height: UIScreen.main.bounds.size.height)
// Turn on video controlls
avPlayerController.showsPlaybackControls = true
// play video
avPlayerController.player?.play()
self.view.addSubview(avPlayerController.view)
self.addChild(avPlayerController)
}
}
Related
I can add MKPointAnnotations by Long Tap Gesture to my Mapkit MapView.
Now I want to remove those markers by pressing a Button in Swift UI.
My idea was to set a variable true when the button is pressed and use this variable as condition for a function in the updateUIView function. But I get the error message that i can't refer to this variable in this nested function.
Here's a snippet from my addAnnotations wrapper. It works fine. How can I implement the removeAnnotation function on my mapView?
func makeUIView(context: Context) -> MKMapView {
let map = MKMapView()
map.setRegion(region, animated: false)
map.showsUserLocation = true
map.delegate = context.coordinator
locationManager.delegate = context.coordinator
let longPress = UILongPressGestureRecognizer(target: context.coordinator, action: #selector(Coordinator.addAnnotation(gesture:)))
longPress.minimumPressDuration = 1
map.addGestureRecognizer(longPress)
return map
}
class Coordinator: NSObject, MKMapViewDelegate, CLLocationManagerDelegate {
var mapView: MapView
init(mapView: MapView) {
self.mapView = mapView
}
#objc func addAnnotation(gesture: UIGestureRecognizer) {
if gesture.state == .ended {
if let mapView = gesture.view as? MKMapView {
let point = gesture.location(in: mapView)
let locationCoordinate = mapView.convert(point, toCoordinateFrom: mapView)
let myPin = MKPointAnnotation()
myPin.title = "Latitude: \(locationCoordinate.latitude), Longitude: \(locationCoordinate.longitude)"
myPin.coordinate = locationCoordinate
mapView.addAnnotation(myPin)
}
}
}
Firstly, I don't know why you chose to use MapKit with UIKit in a SwiftUI project I assume, when you can do it all in SwiftUI unless you intend to support iOS 13 but anyways here is how you do it:
You have your imports of course:
import SwiftUI
import CoreLocation
import MapKit
Then create a viewModel like so:
class MapViewModel: ObservableObject {
#Published var didPressButton = false
}
Add an #StateObject property wrapper(avoid using #ObservevedObject if you are creating the instance in your ContentView)
And yes you can also use #EnvironmentObject
struct ContentView: View {
#StateObject var viewModel = MapViewModel()
var body: some View {
ZStack {
MapView(viewModel: viewModel)
Button("Perform Action") {
viewModel.didPressButton.toggle()
}
}
}
}
Then create an #ObservableObject which will share the same instance of MapViewModel from your ContentView
struct MapView: UIViewRepresentable {
#ObservableObject var viewModel: MapViewModel
func makeCoordinator() -> Coordinator {
return Coordinator()
}
func updateUIView(_ uiView: MKMapView, context: Context) {
if viewModel.didPressButton == true {
context.coordinator.performActionFromSwiftUI()
}
}
func makeUIView(context: Context) -> MKMapView {
let map = context.coordinator.mapView
map.setRegion(region, animated: false)
map.showsUserLocation = true
map.delegate = context.coordinator
locationManager.delegate = context.coordinator
let longPress = UILongPressGestureRecognizer(target: context.coordinator, action: #selector(Coordinator.addAnnotation(gesture:)))
longPress.minimumPressDuration = 1
map.addGestureRecognizer(longPress)
return map
}
}
In your Coordinator class, add the function which will be called in your UIViewRepresentable updateUIView method.
I also suggest when returning a mapView in the makeUIView method in your UIViewRepresentable struct, return the context.coordinator.mapView rather than creating an instance there.
class Coordinator: NSObject, MKMapViewDelegate, CLLocationManagerDelegate {
var mapView = MKMapView()
func performActionFromSwiftUI() {
print("tapped")
}
#objc func addAnnotation(gesture: UIGestureRecognizer) {
if gesture.state == .ended {
if let mapView = gesture.view as? MKMapView {
let point = gesture.location(in: mapView)
let locationCoordinate = mapView.convert(point, toCoordinateFrom: mapView)
let myPin = MKPointAnnotation()
myPin.title = "Latitude: \(locationCoordinate.latitude), Longitude: \(locationCoordinate.longitude)"
myPin.coordinate = locationCoordinate
mapView.addAnnotation(myPin)
}
}
}
}
Here's the top of the view hierarchy. There are two objects SUIScrollHistory which is a UIViewControllerRepresentable wrapper around a UIKit PageViewController. And there is a DateField which is a UIViewRepresentable wrapper around a UIKit TextField that has a custom inputView. Both those objects have #Binding var date: Date. The binding is only updating inside the UITextField code and does not propagate outside of the UIViewRepresentable and UIViewController representable. I need to call a method on both objects when the Bindind<Date> changes. Any suggestions?
import SwiftUI
struct TestUI: View
{
#State var date: Date = Date() {
didSet {
print("Tabs date didSet")
}
}
var body: some View {
NavigationView {
SUIScrollHistory(date: $date)
.edgesIgnoringSafeArea(.top)
.navigationBarTitle("History")
.navigationBarItems(trailing: DateField(date: $date))
}
}
}
Here's the ScrollView wrapped in UIViewControllerRepresentable.
import UIKit
import SwiftUI
import CoreData
final class SUIScrollHistory: UIViewControllerRepresentable
{
#Binding var date: Date {
willSet {
print("SUI scroll history willSet:")
print("scroll to appropriate pg")
print("NOT CALLED")
}
}
init(date: Binding<Date>) {
self._date = date
}
func makeUIViewController(context: Context) -> ScrollHistory {
return ScrollHistory(date: _date)
}
func updateUIViewController(_ uiViewController: ScrollHistory, context: Context) {
//test
}
}
import UIKit
import SwiftUI
import CoreData
class ScrollHistory: UIPageViewController, UIPageViewControllerDelegate, UIPageViewControllerDataSource
{
#Binding var date: Date {
willSet {
scrollTo(newDate: newValue)
print("scroll history willSet")
print("scroll to appropriate pg")
print("NOT CALLED")
}
}
init(date: Binding<Date>) {
self._date = date
super.init(transitionStyle: .scroll, navigationOrientation: .horizontal, options: nil)
self.setViewControllers( [ UIHostingController(rootView: Rectangle().overlay(Color.blue)) ], direction: .forward, animated: true, completion: nil)
self.delegate = self
self.dataSource = self
self.view.isUserInteractionEnabled = true
self.title = "Mon, Aug 17"
}
required init?(coder: NSCoder) { fatalError() }
func scrollTo(newDate: Date) {
if newDate > date { //Slide the PageVC left if date > currentDay
//let nextDay = date.nextDay()
let edibleJournalTVC = UIHostingController(rootView: Text("inject date"))
self.setViewControllers([edibleJournalTVC], direction: .forward, animated: true, completion: nil)
}
if newDate < date { //Slide the PageVC right if date < currentDay
//let previousDay = date.previousDay()
let edibleJournalTVC = UIHostingController(rootView: Text("inject date")) //self.fdPg(date: date) //FIXME: = FoodPage(date: previousDay)
self.setViewControllers([edibleJournalTVC], direction: .reverse, animated: true, completion: nil)
}
self.date = newDate //TODO: Does this loop infinitely?
}
func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
//update DateField
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
let foodPage = UIHostingController(rootView: Text("inject date")) //FIXME: Binding<Date>(previousDay)))
return foodPage
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
let foodPage = UIHostingController(rootView: Text("inject date")) //FIXME nextDay))
return foodPage
}
}
Here's the DateField which is wrapped in UIViewRepresentable.
import SwiftUI
import UIKit
struct DateField: UIViewRepresentable
{
#Binding var date: Date {
willSet {
print("DateField willSet")
print("NOT CALLED")
}
didSet {
print("DateField didSet")
print("NOT CALLED")
}
}
private let format: DateFormatter
init(date: Binding<Date>, format: DateFormatter = YMDFormat()) {
self._date = date
self.format = format
}
func makeUIView(context: UIViewRepresentableContext<DateField>) -> UITextField {
let tf = DateTextField(date: $date) //FIXME: not injected bcs it's a wrapper
print("makeUIView")
return tf
}
func updateUIView(_ uiView: UITextField, context: UIViewRepresentableContext<DateField>) {
print("updateUIView")
uiView.text = format.string(from: date)
}
}
import SwiftUI
import UIKit
class DateTextField: UITextField
{
#Binding var date: Date {
willSet {
print("DateTextField Date")
print("Is Called")
}
}
private lazy var pickDate: UIDatePicker = { return UIDatePicker() }()
init(date: Binding<Date>) {
self._date = date
super.init(frame: .zero)
self.pickDate.date = date.wrappedValue
self.pickDate.addTarget(self, action: #selector(dateChanged(_:)), for: .valueChanged)
self.inputView = pickDate
self.textColor = .black
}
required init?(coder: NSCoder) { fatalError() }
#objc func dateChanged(_ sender: UIDatePicker) {
self.date = sender.date
}
}
import Foundation
class YMDFormat: DateFormatter
{
override init() {
super.init()
dateFormat = "yyyy-MM-dd"
}
required init?(coder: NSCoder) { fatalError() }
}
I am a noob trying to create a social app through Youtube videos, and I am following instructions of this video.
https://youtu.be/GrRggN41VF0
At around 3 mins. of the video you'll see the code.
Here's the code I have.
import UIKit
import Firebase
import SwiftKeychainWrapper
class ViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var userImgView: UIImageView!
#IBOutlet weak var usernameField: UITextField!
#IBOutlet weak var emailField: UITextField!
#IBOutlet weak var passwordField: UITextField!
override func viewDidLoad() {
emailField.delegate = self
passwordField.delegate = self
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func viewDidAppear(_ animated: Bool) {
if let _ = KeychainWrapper.standard.string(forKey: "uid") {
self.performSegue(withIdentifier: "toFeed", sender: nil)
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
self.view.endEditing(true)
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
if textField == emailField {
passwordField.becomeFirstResponder()
} else if textField == passwordField {
textField.resignFirstResponder()
}
return true
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func signInPressed(_ sender: Any) {
if let email = emailField.text, let password = passwordField.text {
Auth.auth().signIn(withEmail: email, password: password) { (user, error) in
if error != nil {
// Create Account
} else {
if let userID = user?.uid {
KeychainWrapper.standard.set((userID), forKey: "uid")
self.performSegue(withIdentifier: "toFeed", sender: nil)
}
}
}
}
}
}
Screenshot of the error
What am I doing wrong here and how do I fix this?
Try using:
if let user = Auth.auth().currentUser, let uid = user?.uid {
KeychainWrapper.standard.set((uid), forKey: "uid")
self.performSegue(withIdentifier: "toFeed", sender: nil)
}
Try using with the guard statement to unwrap optionals safely. Give it a try!
guard let uid = user?.uid else { return }
KeychainWrapper.standard.set((uid), forKey: "uid")
self.performSegue(withIdentifier: "toFeed", sender: nil)
or
guard let uid = Auth.auth().currentUser?.uid else { return }
KeychainWrapper.standard.set((uid), forKey: "uid")
self.performSegue(withIdentifier: "toFeed", sender: nil)
I hope this helps you..
I am trying to create an app which will have a list of announcements, connected to the Firebase server for testing and if the Firebase has a pdf attribute i want to display it in the App.
The code for this is below:
import UIKit
import FirebaseDatabaseUI
import Firebase
import Down
import FontAwesomeIconFactory
import PDFKit
extension AnnouncementDetailViewController: PDFViewDelegate {
func pdfViewWillClick(onLink sender: PDFView, with url: URL){
print(url)
}
}
class AnnouncementDetailViewController: UIViewController {
#IBOutlet var authorLabel: UILabel!
#IBOutlet var titleLabel: UILabel!
#IBOutlet var authorImage: UIImageView!
var announcementKey = ""
let announcement: Announcement = Announcement()
lazy var ref: DatabaseReference = Database.database().reference()
var announcementRef: DatabaseReference!
var refHandle: DatabaseHandle?
var contentView: DownView?
var pdfView: PDFView!
override func viewDidLoad() {
super.viewDidLoad()
initialiseContentView()
initialseDatabaseChild()
initialiseNavbarTitle()
//Method calls for Pdf
configureUI()
loadPDF()
playWithPDF()
}
private func configureUI(){
pdfView = PDFView ()
pdfView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(pdfView)
pdfView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
pdfView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
pdfView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
pdfView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
pdfView.delegate = self
}
private func addObservers(){
NotificationCenter.default.addObserver(self, selector: #selector(handlePageChange(notification:)), name: Notification.Name.PDFViewPageChanged, object: nil)
}
#objc private func handlePageChange(notification: Notification){
print("Current page is changed")
}
private func loadPDF() {
guard
let url = URL(string:"http://www.pdf995.com/samples/pdf.pdf"),
let document = PDFDocument(url: url)
else { return }
if document.isLocked && document.unlock(withPassword: "test"){
pdfView.document = document
} else {
print("We have a problem")
}
}
private func playWithPDF(){
pdfView.displayMode = .singlePageContinuous
pdfView.autoScales = true
}
private func initialiseNavbarTitle() {
self.navigationItem.title = "Announcement Detail"
}
private func initialseDatabaseChild() {
announcementRef = ref.child("announcements").child(announcementKey)
}
private func generateDownViewHeight() -> CGFloat {
return UIScreen.main.bounds.height - authorLabel.frame.height - authorImage.frame.height - (navigationController?.navigationBar.frame.height ?? 0) - view.safeAreaInsets.top - 70
}
private func initialiseContentView() {
guard let contentView = try? DownView(frame: CGRect(x: 0, y: 0, width: 0, height: 0), markdownString: "") else { return }
view.addSubview(contentView)
contentView.scrollView.bounces = false
contentView.translatesAutoresizingMaskIntoConstraints = false
contentView.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 5).isActive = true
contentView.heightAnchor.constraint(equalToConstant: generateDownViewHeight()).isActive = true
contentView.widthAnchor.constraint(equalToConstant: UIScreen.main.bounds.width).isActive = true
self.contentView = contentView
}
#objc override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
refHandle = announcementRef.observe(DataEventType.value, with: { (snapshot) in
let announcementDict = snapshot.value as? [String : AnyObject] ?? [:]
self.announcement.setValuesForKeys(announcementDict)
do {
try self.contentView?.update(markdownString: self.announcement.content)
let factory = FontAwesomeIconFactory.button()
self.authorImage.image = factory.createImage(NIKFontAwesomeIcon.male)
self.authorLabel.text = self.announcement.author
self.titleLabel.text = self.announcement.title
}
catch {
}
})
}
I got some error when I send photo message.
I'm a beginner with swift language. I want to create undergraduate project about chat app. I need some help to fix this problem. Everything had worked smoothly till now but I am facing a small bug that is when I receive an image then it don't appears on the left side of the screen both incoming and outgoing images appear on the right side of the screen.
import UIKit
import JSQMessagesViewController
import MobileCoreServices
import AVKit
import FirebaseDatabase
import FirebaseStorage
import FirebaseAuth
import SDWebImage
class ChatViewController: JSQMessagesViewController {
var messages = [JSQMessage]()
var avatarDict = [String: JSQMessagesAvatarImage]()
var messageRef = FIRDatabase.database().reference().child("messages")
override func viewDidLoad() {
super.viewDidLoad()
if let currentUser = FIRAuth.auth()?.currentUser
{
self.senderId = currentUser.uid
if currentUser.isAnonymous == true
{
self.senderDisplayName = "anonymous"
} else
{
self.senderDisplayName = "\(currentUser.displayName!)"
}
}
observeMessages()
}
func observeUsers(_ id: String)
{
FIRDatabase.database().reference().child("users").child(id).observe(.value, with: {
snapshot in
if let dict = snapshot.value as? [String: AnyObject]
{
let avatarUrl = dict["profileUrl"] as! String
self.setupAvatar(avatarUrl, messageId: id)
}
})
}
func setupAvatar(_ url: String, messageId: String)
{
if url != "" {
let fileUrl = URL(string: url)
let data = try? Data(contentsOf: fileUrl!)
let image = UIImage(data: data!)
let userImg = JSQMessagesAvatarImageFactory.avatarImage(with: image, diameter: 30)
self.avatarDict[messageId] = userImg
self.collectionView.reloadData()
} else {
avatarDict[messageId] = JSQMessagesAvatarImageFactory.avatarImage(with: UIImage(named: "profileImage"), diameter: 30)
collectionView.reloadData()
}
}
func observeMessages() {
messageRef.observe(.childAdded, with: { snapshot in
// print(snapshot.value)
if let dict = snapshot.value as? [String: AnyObject] {
let mediaType = dict["MediaType"] as! String
let senderId = dict["senderId"] as! String
let senderName = dict["senderName"] as! String
self.observeUsers(senderId)
switch mediaType {
case "TEXT":
let text = dict["text"] as! String
self.messages.append(JSQMessage(senderId: senderId, displayName: senderName, text: text))
case "PHOTO":
let photo = JSQPhotoMediaItem(image: nil)
let fileUrl = dict["fileUrl"] as! String
let downloader = SDWebImageDownloader.shared()
downloader.downloadImage(with: URL(string: fileUrl)!, options: [], progress: nil, completed: { (image, data, error, finished) in
DispatchQueue.main.async(execute: {
photo?.image = image
self.collectionView.reloadData()
})
})
self.messages.append(JSQMessage(senderId: senderId, displayName: senderName, media: photo))
if self.senderId == senderId {
photo?.appliesMediaViewMaskAsOutgoing = true
} else {
photo?.appliesMediaViewMaskAsOutgoing = false
}
case "VIDEO":
let fileUrl = dict["fileUrl"] as! String
let video = URL(string: fileUrl)!
let videoItem = JSQVideoMediaItem(fileURL: video, isReadyToPlay: true)
self.messages.append(JSQMessage(senderId: senderId, displayName: senderName, media: videoItem))
if self.senderId == senderId {
videoItem?.appliesMediaViewMaskAsOutgoing = true
} else {
videoItem?.appliesMediaViewMaskAsOutgoing = false
}
default:
print("unknown data type")
}
self.collectionView.reloadData()
}
})
}
override func didPressSend(_ button: UIButton!, withMessageText text: String!, senderId: String!, senderDisplayName: String!, date: Date!) {
let newMessage = messageRef.childByAutoId()
let messageData = ["text": text, "senderId": senderId, "senderName": senderDisplayName, "MediaType": "TEXT"]
newMessage.setValue(messageData)
self.finishSendingMessage()
}
override func didPressAccessoryButton(_ sender: UIButton!) {
print("didPressAccessoryButton")
let sheet = UIAlertController(title: "Media Messages", message: "Please select a media", preferredStyle: UIAlertControllerStyle.actionSheet)
let cancel = UIAlertAction(title: "Cancel", style: UIAlertActionStyle.cancel) { (alert:UIAlertAction) in
}
let photoLibrary = UIAlertAction(title: "Photo Library", style: UIAlertActionStyle.default) { (alert: UIAlertAction) in
self.getMediaFrom(kUTTypeImage)
}
let videoLibrary = UIAlertAction(title: "Video Library", style: UIAlertActionStyle.default) { (alert: UIAlertAction) in
self.getMediaFrom(kUTTypeMovie)
}
sheet.addAction(photoLibrary)
sheet.addAction(videoLibrary)
sheet.addAction(cancel)
self.present(sheet, animated: true, completion: nil)
}
func getMediaFrom(_ type: CFString) {
print(type)
let mediaPicker = UIImagePickerController()
mediaPicker.delegate = self
mediaPicker.mediaTypes = [type as String]
self.present(mediaPicker, animated: true, completion: nil)
}
override func collectionView(_ collectionView: JSQMessagesCollectionView!, messageDataForItemAt indexPath: IndexPath!) -> JSQMessageData! {
return messages[indexPath.item]
}
override func collectionView(_ collectionView: JSQMessagesCollectionView!, messageBubbleImageDataForItemAt indexPath: IndexPath!) -> JSQMessageBubbleImageDataSource! {
let message = messages[indexPath.item]
let bubbleFactory = JSQMessagesBubbleImageFactory()
if message.senderId == self.senderId {
return bubbleFactory!.outgoingMessagesBubbleImage(with: .black)
} else {
return bubbleFactory!.incomingMessagesBubbleImage(with: .blue)
}
}
override func collectionView(_ collectionView: JSQMessagesCollectionView!, avatarImageDataForItemAt indexPath: IndexPath!) -> JSQMessageAvatarImageDataSource! {
let message = messages[indexPath.item]
return avatarDict[message.senderId]
//return JSQMessagesAvatarImageFactory.avatarImageWithImage(UIImage(named: "profileImage"), diameter: 30)
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
print("number of item:\(messages.count)")
return messages.count
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = super.collectionView(collectionView, cellForItemAt: indexPath) as! JSQMessagesCollectionViewCell
return cell
}
override func collectionView(_ collectionView: JSQMessagesCollectionView!, didTapMessageBubbleAt indexPath: IndexPath!) {
print("didTapMessageBubbleAtIndexPath: \(indexPath.item)")
let message = messages[indexPath.item]
if message.isMediaMessage {
if let mediaItem = message.media as? JSQVideoMediaItem {
let player = AVPlayer(url: mediaItem.fileURL)
let playerViewController = AVPlayerViewController()
playerViewController.player = player
self.present(playerViewController, animated: true, completion: nil)
}
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func logoutDidTapped(_ sender: AnyObject) {
do {
try FIRAuth.auth()?.signOut()
} catch let error {
print(error)
}
// Create a main storyboard instance
let storyboard = UIStoryboard(name: "Main", bundle: nil)
// From main storyboard instantiate a View controller
let LogInVC = storyboard.instantiateViewController(withIdentifier: "LogInVC") as! LogInViewController
// Get the app delegate
let appDelegate = UIApplication.shared.delegate as! AppDelegate
// Set LogIn View Controller as root view controller
appDelegate.window?.rootViewController = LogInVC
}
func sendMedia(_ picture: UIImage?, video: URL?) {
print(picture)
print(FIRStorage.storage().reference())
if let picture = picture {
let filePath = "\(FIRAuth.auth()!.currentUser)/\(Date.timeIntervalSinceReferenceDate)"
print(filePath)
let data = UIImageJPEGRepresentation(picture, 0.1)
let metadata = FIRStorageMetadata()
metadata.contentType = "image/jpg"
FIRStorage.storage().reference().child(filePath).put(data!, metadata: metadata) { (metadata, error)
in
if error != nil {
print(error?.localizedDescription)
return
}
let fileUrl = metadata!.downloadURLs![0].absoluteString
let newMessage = self.messageRef.childByAutoId()
let messageData = ["fileUrl": fileUrl, "senderId": self.senderId, "senderName": self.senderDisplayName, "MediaType": "PHOTO"]
newMessage.setValue(messageData)
}
} else if let video = video {
let filePath = "\(FIRAuth.auth()!.currentUser)/\(Date.timeIntervalSinceReferenceDate)"
print(filePath)
let data = try? Data(contentsOf: video)
let metadata = FIRStorageMetadata()
metadata.contentType = "video/mp4"
FIRStorage.storage().reference().child(filePath).put(data!, metadata: metadata) { (metadata, error)
in
if error != nil {
print(error?.localizedDescription)
return
}
let fileUrl = metadata!.downloadURLs![0].absoluteString
let newMessage = self.messageRef.childByAutoId()
let messageData = ["fileUrl": fileUrl, "senderId": self.senderId, "senderName": self.senderDisplayName, "MediaType": "VIDEO"]
newMessage.setValue(messageData)
}
}
}
}
extension ChatViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate {
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
print("did finish picking")
// get the image
print(info)
if let picture = info[UIImagePickerControllerOriginalImage] as? UIImage {
sendMedia(picture, video: nil)
}
else if let video = info[UIImagePickerControllerMediaURL] as? URL {
sendMedia(nil, video: video)
}
self.dismiss(animated: true, completion: nil)
collectionView.reloadData()
}
}
You just need to understand what is going on in your code , as i am looking into your code you are reloading collectionview before the JSQPhotoMediaItem or JSQVideoMediaItem objects are completely configured and added these MediaItem into your message array, so make sure you have completely configured your JSQPhotoMediaItem and JSQVideoMediaItem objects and only after that you add these object to your message array.
you can add this into your switch case condition
case "PHOTO":
let photo = JSQPhotoMediaItem(image: nil)
let fileUrl = dict["fileUrl"] as! String
let downloader = SDWebImageDownloader.shared()
downloader.downloadImage(with: URL(string: fileUrl)!, options: [], progress: nil, completed: { (image, data, error, finished) in
DispatchQueue.main.async(execute: {
photo?.image = image // you have image in your media object
if self.senderId == senderId {
photo?.appliesMediaViewMaskAsOutgoing = true
} else {
photo?.appliesMediaViewMaskAsOutgoing = false
}
// you just configured media object by using appliesMediaViewMaskAsOutgoing
self.messages.append(JSQMessage(senderId: senderId, displayName: senderName, media: photo))
// successfully added object into your message array now you should reload collectionview
self.collectionView.reloadData()
})
})
//same goes for your video condition
But still i would recommend you to follow each step one by one.
Follow these steps for adding A JSQPhotoMediaItem object
make a JSQPhotoMediaItem Object like
let photoItem = JSQPhotoMediaItem(image: UIImage(named: <your Image Object>))
make sure you have downloaded your image from server and successfully have your image object ready to add in the JSQPhotoMediaItem Object (as you are downloading it from firebase) first get the image then add it to your JSQPhotoMediaItem
now you have to tell your message type( outgoing or incoming ) so add appliesMediaViewMaskAsOutgoing property to according to you need so this would be in your code
if self.senderId == senderId {
photoItem?.appliesMediaViewMaskAsOutgoing = true
} else {
photoItem?.appliesMediaViewMaskAsOutgoing = false
}
now this is the final step for appending this object to your array
self.messages.append(JSQMessage(senderId: senderId, displayName: senderName, media: photoItem))
now reload collectionView as you have successfully configured your media item ( JSQPhotoMediaItem )
self.collectionView.reloadData()
same goes for the JSQVideoMediaItem objects.