My question is, how can I deselect(or empty the selection) after finishing uploading.
Right now, image upload successfully and display at HomeView as well. But when I try just to click share button it upload again the last selection. How can I fix that?
PhotoPicker:
struct PhotoPicker: UIViewControllerRepresentable {
#Binding var image: UIImage
func makeUIViewController(context: Context) -> UIImagePickerController {
let picker = UIImagePickerController()
picker.delegate = context.coordinator
picker.allowsEditing = true
return picker
}
func updateUIViewController(_ uiViewController: UIImagePickerController, context: Context) { }
func makeCoordinator() -> Coordinator {
return Coordinator(photoPicker: self)
}
final class Coordinator:NSObject, UINavigationControllerDelegate, UIImagePickerControllerDelegate {
let photoPicker: PhotoPicker
init(photoPicker: PhotoPicker){
self.photoPicker = photoPicker
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
if let image = info[.editedImage] as? UIImage {
guard let data = image.jpegData(compressionQuality: 1), let compressedImage = UIImage(data: data) else {
return
}
photoPicker.image = compressedImage
} else {
}
picker.dismiss(animated: true)
}
}
}
Function for persistImage:
func persistUserInformation(){
guard let uid = Auth.auth().currentUser?.uid else
{ return }
let ref = Storage.storage().reference(withPath: uid)
guard let data = self.image.jpegData(compressionQuality: 0.5) else { return }
ref.putData(data, metadata: nil) { metadata, err in
if let err = err {
print("There was an error while putting data \(err) " )
return
}
ref.downloadURL { url, error in
if let error = error {
print("There was an error while downloading the data.")
return
}
print("Successfully uploaded image to storage! \(url?.absoluteString ?? "")")
quoteVM.addAQuote(quote: self.quoteField, imageUrl: url!)
}
}
}
I was expecting to refresh the last selection, and leave it empty until another one.
Related
Below is my Picker code. It works great, but the full res image is too much -- causing smaller Image views be grainy. How can I add compression to the final result? Thanks!
import PhotosUI
import SwiftUI
struct ImagePicker: UIViewControllerRepresentable {
#Binding var image: UIImage?
func makeUIViewController(context: Context) -> PHPickerViewController {
var config = PHPickerConfiguration()
config.filter = .images
let picker = PHPickerViewController(configuration: config)
picker.delegate = context.coordinator
return picker
}
func updateUIViewController(_ uiViewController: PHPickerViewController, context: Context) {
}
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
class Coordinator: NSObject, PHPickerViewControllerDelegate {
let parent: ImagePicker
init(_ parent: ImagePicker) {
self.parent = parent
}
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
picker.dismiss(animated: true)
guard let provider = results.first?.itemProvider else { return }
if provider.canLoadObject(ofClass: UIImage.self) {
provider.loadObject(ofClass: UIImage.self) { image, _ in
self.parent.image = image as? UIImage
}
}
}
}
}
You can try "jpegData(compressionQuality: 1)"
if provider.canLoadObject(ofClass: UIImage.self) {
provider.loadObject(ofClass: UIImage.self) { image, _ in
if let uiImage = selectedImage as? UIImage {
uiImage.jpegData(compressionQuality: 1)
self.parent.image = uiImage
}
}
}
The code here allows me to upload and download one photo to Firebase and save it to user defaults but I'm trying to figure out how to do it within a collectionView cell and display as many photos wanted, adding on new items
import UIKit
import FirebaseStorage
class ViewController: UIViewController, UINavigationControllerDelegate, UIImagePickerControllerDelegate {
private let storage = Storage.storage().reference()
#IBOutlet var imageView: UIImageView!
#IBOutlet var label: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
label.numberOfLines = 0
guard let urlString = UserDefaults.standard.value(forKey: "url") as? String, let url = URL(string: urlString) else {
return
}
label.text = urlString
let task = URLSession.shared.dataTask(with: url, completionHandler: { data,_,error in
guard let data = data, error == nil else {
return
}
DispatchQueue.main.async {
let image = UIImage(data: data)
self.imageView.image = image
}
})
task.resume()
}
#IBAction func didTapButton() {
let picker = UIImagePickerController()
picker.sourceType = .photoLibrary
picker.delegate = self
picker.allowsEditing = true
present(picker, animated: true, completion: nil)
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
picker.dismiss(animated: true, completion: nil)
guard let image = info[UIImagePickerController.InfoKey.editedImage] as? UIImage else {
return
}
guard let imageData = image.pngData() else {
return
}
storage.child("Images/Photo.png").putData(imageData, metadata: nil) { (_, error) in
guard error == nil else {
print("Failed to Upload Data")
return
}
self.storage.child("Images/Photo.png").downloadURL(completion: {url, error in
guard let url = url, error == nil else {
return
}
let urlString = url.absoluteString
DispatchQueue.main.async {
self.label.text = urlString
self.imageView.image = image
}
print("Download URL: \(urlString)")
UserDefaults.standard.set(urlString, forKey: "url")
})
}
// Upload Image Data
// Get Download URL
// Save Download URL to userDefaults
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
picker.dismiss(animated: true, completion: nil)
}
}
To upload images to Firebase storage and show them in a collection view, you can use the following steps;
Set up collection view with an array of URLs (or Strings) as its
data source. You can use your custom models if required.
Keep a reference to your Firebase storage and upload the image. After successful upload, get the URL for the uploaded image using the image reference.
Save the url in Firebase Database(or Cloud Firestore). This is required only if you want to sync the collection view with the database and update it when new images are uploaded.
Add a listener to your Firebase database reference where you have
saved the image URLs. Update the local URLs array inside the listener and reload the collection view.
If you don't want to use Firebase database, omit steps 3 and 4, save the image URL to the array and reload the collection view right away.
I'm not adding the code for collection view setup here as it's not the objective of this answer.
let storageRef = Storage.storage().reference(withPath: "images")
let databaseRef = Database.database().reference(withPath:"images")
var images: [String] = []
override func viewDidLoad() {
super.viewDidLoad()
addDatabaseListener()
}
private func addDatabaseListener() {
databaseRef.observe(.childAdded) { (snapshot) in
guard let value = snapshot.value as? [String: Any], let url = value["url"] as? String else { return }
self.images.append(url)
DispatchQueue.main.async {
self.collectionView.reloadData()
}
}
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
picker.dismiss(animated: true)
guard let image = info[UIImagePickerController.InfoKey.editedImage] as? UIImage, let data = image.jpegData(compressionQuality: 0.1) else { return }
let fileName = "\(Date.timeIntervalSinceReferenceDate).jpeg"
let newImageRef = storageRef.child(fileName)
newImageRef.putData(data, metadata: nil) { (_, error) in
if let error = error {
print("upload failed: ", error.localizedDescription)
return
}
newImageRef.downloadURL { (url, error) in
if let error = error {
print("error: ", error.localizedDescription)
return
}
self.databaseRef.childByAutoId().setValue(["url": url?.absoluteString])
}
}
}
I have an image picker and after the image got picked, it should get uploaded into my Firebase Storage.
Now I have one problem: Somehow all the images dont get cropped correctly.
Did I misplace some Code, because that's the only thing I could think of, since I already got
picker.allowsEditing = true
The picture gets uploaded correctly, but not how I intended to crop it. The "cropper-window" shows up, but it doesnt get saved as intended.
Here is my code:
import UIKit
import Firebase
class editViewController: UIViewController {
#IBOutlet var pfp: UIImageView!
var fireImage: UIImage? = nil
override func viewDidLoad() {
super.viewDidLoad()
setupPfp()
}
func setupPfp(){
pfp.layer.cornerRadius = pfp.frame.height/2
pfp.clipsToBounds = true
pfp.isUserInteractionEnabled = true
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(presentPicker))
pfp.addGestureRecognizer(tapGesture)
}
#objc func presentPicker(){
let picker = UIImagePickerController()
picker.sourceType = .photoLibrary
picker.allowsEditing = true
picker.delegate = self
self.present(picker, animated: true, completion: nil)
}
func uploadToFirebase(){
guard let imageSelected = self.fireImage else {
print("Image is nil")
return
}
guard let imageData = imageSelected.jpegData(compressionQuality: 0.5) else {
return
}
let storageRef = Storage.storage().reference(forURL: "gs://lidjd-9dad5.appspot.com")
let storageProfileRef = storageRef.child("profileImages").child(Auth.auth().currentUser!.uid)
let metadata = StorageMetadata()
let db = Firestore.firestore()
let userID = Auth.auth().currentUser!.uid
let docRef = db.collection("users").document(userID)
metadata.contentType = "image/jpeg"
storageProfileRef.putData(imageData, metadata: metadata) { (storageMetaData, error) in
if error != nil{
print(error!.localizedDescription)
return
}
storageProfileRef.downloadURL { (url, error) in
if let metaImageUrl = url?.absoluteString{
print(metaImageUrl)
docRef.updateData([
"profileimage": metaImageUrl
]){ err in
if let err = err {
print("Error updating document: \(err)")
} else {
print("Profile Image successfully updated")
}}
}
}
}
}
}
extension editViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate{
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
if let imageSelected = info[UIImagePickerController.InfoKey.editedImage] as? UIImage {
fireImage = imageSelected
pfp.image = imageSelected
}
if let imageOriginal = info[UIImagePickerController.InfoKey.originalImage] as? UIImage {
fireImage = imageOriginal
pfp.image = imageOriginal
}
picker.dismiss(animated: true, completion: nil)
uploadToFirebase()
}
}
Choose one of them .. in your current code you are always using orignal image
extension editViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate{
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
if let imageSelected = info[UIImagePickerController.InfoKey.editedImage] as? UIImage {
fireImage = imageSelected
pfp.image = imageSelected
}
else if let imageOriginal = info[UIImagePickerController.InfoKey.originalImage] as? UIImage {
fireImage = imageOriginal
pfp.image = imageOriginal
}
picker.dismiss(animated: true, completion: nil)
uploadToFirebase()
}
}
So I have an edit profile page where the user can change their profile picture and cover photo. I have it to where once the users selects their image, they click the saveButton and the photo is uploaded to Firebase Database and Firebase Storage and then the edit profile viewcontroller is dismissed and returns to profile view. Only problem I am having is that although the photo uploads to firebase, it doesn't save the image on the app if I close out and reopen it. I'm not sure what I am missing to make that happen. Here is the code below:
import UIKit
import Foundation
import Firebase
import FirebaseDatabase
class NewEditProfileViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
#IBOutlet weak var imageView1: UIImageView!
#IBOutlet weak var imageView2: UIImageView!
var ref = DatabaseReference.init()
var imagePicker = UIImagePickerController()
var imagePicked = 0
var selectedImage1: UIImage?
override func viewDidLoad() {
super.viewDidLoad()
self.ref = Database.database().reference()
self.saveFIRData()
imagePicker.delegate = self
imagePicker.sourceType = .photoLibrary
imagePicker.allowsEditing = true
}
#IBAction func chooseImage1(_ sender: Any) {
if UIImagePickerController.isSourceTypeAvailable(UIImagePickerController.SourceType.photoLibrary){
imagePicked = (sender as AnyObject).tag
present(imagePicker, animated: true)
}
}
#IBAction func chooseImage2(_ sender: Any) {
if UIImagePickerController.isSourceTypeAvailable(UIImagePickerController.SourceType.photoLibrary){
imagePicked = (sender as AnyObject).tag
present(imagePicker, animated: true)
}
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
let pickedImage = info[UIImagePickerController.InfoKey.editedImage] as? UIImage
let pickedImage2 = info[UIImagePickerController.InfoKey.editedImage] as? UIImage
if imagePicked == 1 {
self.imageView1.image = pickedImage
} else if imagePicked == 2 {
self.imageView2.image = pickedImage2
}
dismiss(animated: true)
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
dismiss(animated: true)
}
#IBAction func saveButton(_ sender: Any) {
self.saveFIRData()
self.dismiss(animated: true, completion: nil)
}
#IBAction func backButton(_ sender: Any) {
self.dismiss(animated: true, completion: nil)
}
#objc func saveFIRData() {
guard let image = imageView1.image else { return }
self.uploadProfileImage(image){ url in
self.saveProfileImage(profileURL: url!){ success in
if success != nil{
print("yes")
}
}
}
}
}
extension NewEditProfileViewController {
func uploadProfileImage(_ image:UIImage, completion: #escaping (_ url: URL?)->()) {
guard let uid = Auth.auth().currentUser?.uid else { return }
let storageRef = Storage.storage().reference().child("users/\(uid)")
let imageData = imageView1.image?.jpegData(compressionQuality: 0.8)
let metaData = StorageMetadata()
metaData.contentType = "image/jpeg"
storageRef.putData(imageData!, metadata: metaData) { (metaData, error) in
if error == nil{
print("success")
storageRef.downloadURL(completion: { (url, error) in
completion(url)
})
}else{
print("error in save image")
completion(nil)
}
}
}
func saveProfileImage(profileURL:URL, completion: #escaping ((_ url: URL?) -> ())){
guard let uid = Auth.auth().currentUser?.uid else { return }
let databaseRef = Database.database().reference().child("users/profilePhotoURL/\(uid)")
let userObject = [
"photoURL": profileURL.absoluteString
] as [String:Any]
self.ref.child("users").child(uid).setValue(userObject)
}
}
As I can see in your code in function
func saveProfileImage(profileURL:URL, completion: #escaping ((_ url: URL?) -> ()))
code to download image is missing you have to update your saveProfileImageto support download & then save to local app directory.
func saveProfileImage(profileURL:URL, completion: #escaping ((_ url: URL?) -> ())){
guard let uid = Auth.auth().currentUser?.uid else { return }
let databaseRef = Storage.storage().reference().child("users/profilePhotoURL/\(uid)")
// Download in memory with a maximum allowed size of 1MB (1 * 1024 * 1024 bytes), Change it according to your need
storageRef.getData(maxSize: 1 * 1024 * 1024) { data, error in
if let error = error {
// Uh-oh, an error occurred!
} else {
// Save `data` to your local directory here
let imageFilename = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] + "/" + "\(uid).jpg"
data.write(toFile: imageFilename, atomically: true)
}
}
let userObject = [
"photoURL": profileURL.absoluteString
] as [String:Any]
self.ref.child("users").child(uid).setValue(userObject)
}
}
Refer following sample code taken from Firebase guide Download to local section on file "Download File on iOS" page.
// Create a reference to the file you want to download
let islandRef = storageRef.child("images/island.jpg")
// Create local filesystem URL
let localURL = URL(string: "path/to/image")!
// Download to the local filesystem
let downloadTask = islandRef.write(toFile: localURL) { url, error in
if let error = error {
// Uh-oh, an error occurred!
} else {
// Local file URL for "images/island.jpg" is returned
}
}
I am making a project very similiar to Instagram's multiple image post feature, and I have 2 errors that are connected to each other in my capturePhotoController because of my ImageUploadManager. The errors are:
Cannot assign to immutable expression of type 'ImageUploadManager.Type'
Type 'ImageUploadManager?' has no member 'uploadImage'
It is happening in this view controller: *The errors are marked with a comment
import UIKit
class takePicViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
#IBOutlet weak var imageView: UIImageView!
let imagePicker = UIImagePickerController()
override func viewDidLoad() {
super.viewDidLoad()
imagePicker.delegate = self
imagePicker.sourceType = .camera
imagePicker.allowsEditing = false
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
if let userPickedImage = info[UIImagePickerControllerOriginalImage] as? UIImage {
// Error ImageUploadManager = ImageUploadManager()
// Error ImageUploadManager?.uploadImage(userPickedImage, progressBlock: { (percentage) in
print(percentage)
}, completionBlock: { (fileURL, errorMessage) in
print(fileURL)
print(errorMessage)
})
PhotoArray.sharedInstance.photosArray.append(userPickedImage) //append converted data in array
imageView.image = userPickedImage
}
imagePicker.dismiss(animated: true, completion: nil)
}
#IBAction func cameraButtonPressed(_ sender: UIButton) {
present(imagePicker, animated: true, completion: nil)
}
}
Here is the code for the ImageUploadManager:
import UIKit
import FirebaseStorage
import Firebase
struct Constants {
struct Car {
static let imagesFolder: String = "carImages"
}
}
class ImageUploadManager: NSObject {
func uploadImage(_ image: UIImage, /**/ progressBlock: #escaping (_ percentage: Double) -> Void, /**/ completionBlock: #escaping (_ url: String?, _ errorMessage: String?) -> Void) {
let data = Data()
let storage = Storage.storage()
let storageRef = storage.reference()
let imageName = "\(Date().timeIntervalSince1970).jpg"
let imagesReference = storageRef.child(Constants.Car.imagesFolder).child(imageName)
if let imageData = UIImageJPEGRepresentation(image, 0.8) {
let metadata = storageRef.child("image/jpg")
let uploadTask = metadata.putData(data, metadata: nil) { (metadata, error) in
guard let metadata = metadata else {
// Uh-oh, an error occurred!
return
}
storageRef.downloadURL { (url, error) in
guard let downloadURL = url else {
// Uh-oh, an error occurred!
return
}
}
}
uploadTask.observe(.progress, handler: { (snapshot) in
guard let progress = snapshot.progress else {
return
}
let percentage = (Double(progress.completedUnitCount) / Double(progress.totalUnitCount)) * 100
progressBlock(percentage)
})
} else {
completionBlock(nil, "Image could not be converted to Data.")
}
}
}
Is the commented code:
// Error ImageUploadManager = ImageUploadManager()
your declaration of ImageUploadManager? if so that is totally wrong. It should be declared as below:
if let userPickedImage = info[UIImagePickerControllerOriginalImage] as? UIImage {
let imageUploadManager = ImageUploadManager()
imageUploadManager.uploadImage(userPickedImage, prog.....
Reading your ImageUploadManager it looks like it's an stateless object and you can declare your function as a class function.
`class func uploadImage(_ image: UIImage`......`
You can then call it:
if let userPickedImage = info[UIImagePickerControllerOriginalImage] as? UIImage {
ImageUploadManager.uploadImage(userPickedImage, prog.....