Swift displaying image in viewdidappear cannot be replaced by picking new image - swift

I use firebase as my back end and I have a displaying facebook photo image function in viewdidappear so the profile image will display when the user is at the viewcontroller. My problem is whenever user want to change the profile Image by image picker it will display the "picked image" for 1 sec only and turn back to facebook image.
I think the problem is the displaying facebook photo image function is in the viewdidappear? How can I solve it?
override func viewdidappear(){
super.viewdidappear(animated)
self.displayProfilePic(user)
}
func displayProfilePic(user: FIRUser?){
let photoURL = user?.photoURL
struct last {
static var photoURL: NSURL? = nil
}
last.photoURL = photoURL; later one.
if let photoURL = photoURL {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), {
let data = NSData.init(contentsOfURL: photoURL)
if let data = data {
let image = UIImage.init(data: data)
dispatch_async(dispatch_get_main_queue(), {
if (photoURL == last.photoURL) {
self.profilePic.image = image
}
})
}
})
} else {
profilePic.image = UIImage.init(named: "DefaultPic")
}
image Picker method
func openGallary(){
if UIImagePickerController.isSourceTypeAvailable(UIImagePickerControllerSourceType.SavedPhotosAlbum){
print("Pick Photo")
self.imagePicker.sourceType = UIImagePickerControllerSourceType.PhotoLibrary
self.imagePicker.allowsEditing = true
self.presentViewController(self.imagePicker, animated: true, completion: nil)
}
}
func openCamera(){
if(UIImagePickerController.isSourceTypeAvailable(UIImagePickerControllerSourceType.Camera)){
self.imagePicker.sourceType = UIImagePickerControllerSourceType.Camera
self.imagePicker.allowsEditing = true
self.presentViewController(self.imagePicker, animated: true, completion: nil)
}else{
print("you got no camara")
}
}
func imagePickerController(picker: UIImagePickerController, didFinishPickingMeddiaWithInfo info: [String :AnyObject]){
if let pickedImage = info[UIImagePickerControllerOriginalImage] as? UIImage {
profilePic.contentMode = .ScaleAspectFit
profilePic.image = pickedImage
}
dismissViewControllerAnimated(true, completion: nil)
}
I am not sure why the image picker's picked image cannot be replaced.

I think the problem is the displaying facebook photo image function is in the viewdidappear
This is correct. viewDidAppear is called every time the view is shown again after a VC that was covering it is dismissed (e.g. the image picker).
You could do this in viewDidLoad instead -- which is called just once when the VC is loaded -- and is not called again when the imagePicker is done.
If you can't, then you need to keep track that the imagePicker replaced the image and that it should not be replaced again with the FB image when viewDidAppear is called.

Related

Swift - get sender of UIImagePickerController

I have 2 UIImageViews which I have added a UITapGestureRecognizer to which opens the user's camera. Once the user has selected an image, then the UIImageView that was tapped in order to access the camera should be set with the camera image.
So here is my openCamera method:
#objc func openCamera(sender: ImagePlaceHolderView) {
let picker = UIImagePickerController()
picker.allowsEditing = true
picker.delegate = self
picker.sourceType = .camera
present(picker, animated: true)
}
My UIImageViews are declared as follows:
var left = UIImageView()
var right = UIImageView()
left = createImagePlaceholder()
right = createImagePlaceholder()
private func createImagePlaceholder() -> UIImageView {
let placeholder = ImagePlaceHolderView(frame: CGRect(x: 0, y: 0, width: 90, height: 150))
placeholder.isUserInteractionEnabled = true
return placeholder
}
And then my delegate method:
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
guard let image = info[.editedImage] as? UIImage else { return }
left.image = image // Here I want to set the sender's image to image
dismiss(animated: true, completion: nil)
}
I'm struggling to figure out how to set the image of the UIImageView that was tapped to this new image from the camera. I'm trying to pass the sender into the openCamera method but not sure how to extract this, as the sender seems in fact to be the UITapGestureRecognizer rather than the image that was tapped.
You'll have to store a reference to the sender and use that to set the image in the delegate method.
var imagePlaceholderView: ImagePlaceHolderView? // set the reference
#objc func openCamera(sender: UITapGestureRecognizer) {
imagePlaceholderView = sender.view as? ImagePlaceHolderView
//...
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
guard let image = info[.editedImage] as? UIImage else { return }
imagePlaceholderView?.image = image
dismiss(animated: true, completion: nil)
}

How to modify imagePickerController (source camera) not to dismiss after pressing "Use photo" until 4 photos are taken?

I want to take 4 photos before UIImagePickerController is dismissed. How to modify it?
I have following button action and delegate:
#IBAction func take4Photos(_ sender: Any) {
if UIImagePickerController.isSourceTypeAvailable(.camera) {
let image = UIImagePickerController()
image.delegate = self
image.sourceType = .camera;
image.allowsEditing = false
self.present(image, animated: true, completion: nil)
}
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
if let image = info[UIImagePickerController.InfoKey.originalImage] as? UIImage{
imagePicked = image
self.dismiss(animated: true, completion: nil)
}
I will recommend you to use ImagePicker, which can be easily installed through cocoaPods and it can fulfill your requirement with just one line of Code.
let imagePickerController = ImagePickerController()
imagePickerController.imageLimit = 4
You can check ImagePicker library here
Here's how you can use ImagePicker library to pick multiple images.
Create a button in Storyboard and have its outlet in the ViewController.
#IBOutlet weak var chooseImage: UIButton!
If you want to see the selected images in VC after selection, make an array of UIImages like this
var imageViews:[UIImageView] = []
Then, in viewDidLoad, add target to chooseImage button
chooseImage.addTarget(self, action: #selector(buttonTouched(button:)), for: .touchUpInside)
And declare buttonTouched function outside the viewDidLoad like this
#objc func buttonTouched(button: UIButton) {
let config = Configuration()
config.doneButtonTitle = "Done"
config.noImagesTitle = "Sorry! There are no images here!"
config.recordLocation = false
config.allowVideoSelection = false. //If you don't want video recording
let imagePicker = ImagePickerController(configuration: config)
imagePicker.delegate = self
present(imagePicker, animated: true, completion: nil)
}
Then, in the end, inside the extension, conform to ImagePicker delegate functions like this.
extension OwnerAddListingFacilitiesViewController:ImagePickerDelegate {
func cancelButtonDidPress(_ imagePicker: ImagePickerController) {
imagePicker.dismiss(animated: true, completion: nil)
}
func wrapperDidPress(_ imagePicker: ImagePickerController, images: [UIImage]) { //Don't know what exactly this function does }
func doneButtonDidPress(_ imagePicker: ImagePickerController, images: [UIImage]) {
for Count in 0..<images.count {
let imageView = UIImageView(image: images[Count])
imageView.frame = CGRect(x: (0 * (110 * Count)), y: 0, width: 50, height: 50)
imageViews.append(imageView)
}
imagePicker.dismiss(animated: true, completion: nil)
}
}
You'll be able to see small thumbnail like images after selecting them and pressing done.
Unfortunately, that is not possible. UIImagePickerController can pick only one media. If you want that behaviour, either you have to create your own picker or use a library.
If you want to create your own, use this resource
https://developer.apple.com/documentation/photokit/browsing_and_modifying_photo_albums
It has an official demo source from which you can create your own within 2-3 hours (I had to do this exact problem once, multiple image and video selection from a grid like selector)

How to set a Custom Image as ViewController Background

I have created a button that allows me to set an image from the camera roll as the ViewController background (imageView is the background view), but I need to save it and reload it when ViewController loads.
class ViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
#IBOutlet weak var backgroundSelectionView: UIView!
#IBOutlet weak var imageView: UIImageView!
#IBAction func imageButton(_ sender: UIButton) {
let imagePicker = UIImagePickerController()
imagePicker.delegate = self
imagePicker.sourceType = .photoLibrary
imagePicker.allowsEditing = false
present(imagePicker, animated: true, completion: nil)
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
if let image = info[UIImagePickerController.InfoKey.originalImage] as? UIImage {
imageView.image = image
}
self.dismiss(animated: true, completion: nil)
}
}
I think I should use paths (I read it somewhere but I don't know how to do that since I can't find anything). Could you provide advice on how to save and load a CUSTOM image (not a specific one that you manually select and import in your code, I can already do that?)
Welcome to StackOverflow!
The easiest way to do this would likely be to save the image to disk once you have picked it and restore it when ViewController loads.
To save the image, use FileManager and save to the app's document storage:
/// Saves a background image to disk
/// - Parameter image: The background image to save to disk
/// - Returns: true if successful, false otherwise
func saveBackgroundImage(image: UIImage) -> Bool {
// make sure that the image can be transformed into data (assuming PNG)
guard let data = image.pngData() ?? imageView.image?.pngData() else {
return false
}
// ensure that you can write to the document director
guard let directory = FileManager.default.urls(for: .documentDirectory,
in: .userDomainMask).first else {
return false
}
// write the file
do {
try data.write(to: directory.appendingPathComponent("backgroundImage.png")!)
return true
} catch {
print(error.localizedDescription)
return false
}
}
Now, you need a way to retrieve the file:
/// Retrieves the saved background image
/// - Returns: The background image from disk if it exists, or nil
func getSavedBackgroundImage() -> UIImage? {
if let dir = FileManager.default.urls(for: .documentDirectory,
in: .userDomainMask).first {
return UIImage(contentsOfFile: URL(fileURLWithPath: dir.absoluteString).appendingPathComponent("backgroundImage.png").path)
}
return nil
}
Inside your imagePickerController(picker: didFinishPickingMediaWithInfo:) method, when you set the image on imageView, save the background image by calling self.saveBackgroundImage(image: image). If you are not checking the boolean that is returned from saveBackgroundImage(image:), then call it as follows:
let _ = self.saveBackgroundImage(image: image)
to prevent a "Result unused" warning.
Now, implement your viewDidLoad, which is where you will attempt to load the image:
override func viewDidLoad() {
super.viewDidLoad()
if let backgroundImage = self.getSavedBackgroundImage() {
// set the background image on the `imageView`
self.imageView.image = backgroundImage
}
}
Please set backgroundImage as below,
override func viewDidLoad() {
super.viewDidLoad()
assignbackground()
}
func assignbackground(){
let background = UIImage(named: backgroundImage)
var imageView : UIImageView!
imageView = UIImageView(frame: view.bounds)
imageView.contentMode = UIViewContentMode.ScaleAspectFill
imageView.clipsToBounds = true
imageView.image = background
imageView.center = view.center
view.addSubview(imageView)
self.view.sendSubviewToBack(imageView)
}
Please user remaining code as it is as #Leejay Schmidt's code

Import image based off what UIIMageView is equal to nil

I have my app setup to import a picture. If there isn't a picture in the first UIImageView puts the imported picture there. If the first UIImageView does have something in it put picture in the second UIImageView. Finally if the first and second UIImageView put the picture in the third
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
if let image = info[UIImagePickerControllerOriginalImage] as? UIImage {
if picture.image == nil {
picture.image = image
} else if picture.image == image {
picture2.image = image
} else if picture2.image == image {
picture3.image = image
}
self.dismiss(animated: true, completion: nil)
}
}
Try to replace == image to == nil in the second and third if statement because every picture should have different images.

Show a view in front of the imagePicker

In my application the user can either open the camera roll to select a picture or open the camera to take directly one by himself.
In both cases, the picture selected/taken will also be saved locally for further reference.
The downside is that the saving operation usually freeze the screen until it is finished.
I found an animation in this post and I want to display it in front of the imagePickerController but I can't manage to do so.
class SinglePageViewController: UIViewController, UINavigationControllerDelegate, UIImagePickerControllerDelegate, UITextFieldDelegate, UINavigationBarDelegate {
var spinner: UIActivityIndicatorView?
lazy var showCameraImagePickerController: UIImagePickerController = {
let imagePicker = UIImagePickerController()
imagePicker.delegate = self
imagePicker.sourceType = .camera
imagePicker.allowsEditing = false
return imagePicker
}()
lazy var showPhotoImagePickerController: UIImagePickerController = {
let imagePicker = UIImagePickerController()
imagePicker.delegate = self
imagePicker.sourceType = .photoLibrary
imagePicker.allowsEditing = false
return imagePicker
}()
#IBOutlet weak var photoButton: UIButton!
#IBAction func onPhotoButton(_ sender: Any) {
self.present(self.showCameraImagePickerController, animated: true, completion: nil)
}
#IBOutlet weak var galleryButton: UIButton!
#IBAction func onGalleryButton(_ sender: Any) {
self.present(self.showPhotoImagePickerController, animated: true, completion: nil)
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
picker.dismiss(animated: true, completion: nil)
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String: Any]) {
//start animation
let screenSize: CGRect = UIScreen.main.bounds
spinner = UIActivityIndicatorView(frame: CGRect(x: screenSize.width / 2 - 150, y: screenSize.height / 2 - 150, width: 300, height: 300))
spinner?.isHidden = false
spinner?.startAnimating()
spinner?.color = UIColor.red
switch picker {
case showCameraImagePickerController:
// snap pic, save to doc, save to album
self.showCameraImagePickerController.view.addSubview(spinner!)
timer = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: false, block: { _ in
let image = info[UIImagePickerControllerOriginalImage] as? UIImage
if self.saveImage(imageName: "\(self.titleLabel.text!).png", image: image) {
// additionally save to photo album
UIImageWriteToSavedPhotosAlbum(image!, self, #selector(self.image(_:didFinishSavingWithError:contextInfo:)), nil)
print("saved \(self.titleLabel.text!).png")
self.imageView.image = image
}
})
case showPhotoImagePickerController:
//switch pic, save to doc. no album
self.showPhotoImagePickerController.view.addSubview(spinner!)
timer = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: false, block: { _ in
let image = info[UIImagePickerControllerOriginalImage] as? UIImage
if self.saveImage(imageName: "\(self.titleLabel.text!).png", image: image) {
print("saved new \(self.titleLabel.text!).png")
self.imageView.image = image
self.spinner?.stopAnimating()
self.spinner?.removeFromSuperview()
self.spinner = nil
self.showPhotoImagePickerController.dismiss(animated: true, completion: nil)
} else {
self.spinner?.stopAnimating()
self.spinner?.removeFromSuperview()
self.spinner = nil
self.showPhotoImagePickerController.dismiss(animated: true, completion: nil)
}
})
default:
return
}
}
#objc func image(_ image: UIImage, didFinishSavingWithError error: NSError?, contextInfo: UnsafeRawPointer) {
spinner?.stopAnimating()
spinner?.removeFromSuperview()
spinner = nil
self.showCameraImagePickerController.dismiss(animated: true, completion: nil)
}
func saveImage(imageName: String, image: UIImage?) -> Bool {
//create an instance of the FileManager
let fileManager = FileManager.default
//get the image path
let imagePath = (NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as NSString).appendingPathComponent(imgDir + imageName)
print(imagePath)
//get the image we took with camera
let image = rotateImage(image: image!)
//get the PNG data for this image
let data = UIImagePNGRepresentation(image)
//store it in the document directory
if fileManager.createFile(atPath: imagePath as String, contents: data, attributes: nil) {
newItem?.image = true
return true
} else {
print("error while saving")
return false
}
}
}
as you can see I tried playing with bringSubView(toFront:) and also with the zPosition but with no results.
following this similar question I looked into the documentation for cameraOverlayView but it says that it only works when the imagePicker is presented in camera mode, which doesn't cover the case when I open the photo library
I also recently tried to use a workaround, meaning that I dismiss the imagePickerController as soon as possible and update the image afterwards, but that is not optimal anymore because of some changes in the structure of app.
EDIT
to make myself clearer I'll state again what I need: show the spinner animation in front of the imagePicker, as soon as I tap a photo to choose it, and until I finish saving, then dismiss the imagePicker.
I do not want to first dismiss the picker and then save while showing the spinner in the main view.
EDIT2
updated the code with the new one from the answer. only problem is that if I don't put a timer the spinner shows itself only at the end of the saving process for a brief moment (checked with breakpoints).
This results in no animation during the couple of seconds of saving process and just a brief apparition of the spinner at the end before dismissing the imagePicker.
Just putting a 0.1sec delay triggers the spinner immediately and I get the expected behaviour (animation while saving).
No idea why
Please see a complete example where spinner will show while the image is being saved and once finishes saving spinner will be removed.
class ViewController: UIViewController {
/// Image picker controller
lazy var imagePickerController: UIImagePickerController = {
let imagePicker = UIImagePickerController()
imagePicker.delegate = self
imagePicker.sourceType = .photoLibrary;
imagePicker.allowsEditing = false
return imagePicker
}()
var spinner: UIActivityIndicatorView?
#IBAction func imagePickerButton(_ sender: UIButton) {
self.present(self.imagePickerController, animated: true, completion: nil)
}
}
// MARK: ImagePicker Delegate to get the image picked by the user
extension ViewController: UINavigationControllerDelegate, UIImagePickerControllerDelegate {
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
//start animation
let screenSize: CGRect = UIScreen.main.bounds
spinner = UIActivityIndicatorView(frame: CGRect(x: screenSize.width/2 - 50, y: screenSize.height/2 - 50, width: 100, height: 100))
spinner?.isHidden = false
spinner?.startAnimating()
spinner?.color = UIColor.black
self.imagePickerController.view.addSubview(spinner!)
let image = info[UIImagePickerControllerOriginalImage] as? UIImage
UIImageWriteToSavedPhotosAlbum(image!, self, #selector(image(_:didFinishSavingWithError:contextInfo:)), nil)
}
#objc func image(_ image: UIImage, didFinishSavingWithError error: NSError?, contextInfo: UnsafeRawPointer) {
spinner?.stopAnimating()
spinner?.removeFromSuperview()
spinner = nil
self.imagePickerController.dismiss(animated: true, completion: nil)
if error == nil {
let ac = UIAlertController(title: "Saved!", message: "Your altered image has been saved to your photos.", preferredStyle: .alert)
ac.addAction(UIAlertAction(title: "OK", style: .default))
present(ac, animated: true)
}
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
picker.dismiss(animated: true, completion: nil)
}
}