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
Related
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)
After selecting an image from camera roll, then placing it into an image view, I need to save it to user defaults.
All the code is working, but not reliably, it works maybe 3 out of 5 times.
I believe the problem is the code that adds the image into the array is not great.
I'm very new to Swift and I have no idea how to clean it up, can anyone help please?
Here is my code
class importImageViewController: UIViewController, UINavigationControllerDelegate, UIImagePickerControllerDelegate {
var importImage = UIImage()
let defaults = UserDefaults.standard
#IBOutlet weak var saveScreenButton: UIButton!
#IBOutlet weak var importImageView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func importButton(_ sender: Any) {
let image = UIImagePickerController()
image.delegate = self
image.sourceType = UIImagePickerController.SourceType.photoLibrary
image.allowsEditing = false
self.present(image, animated: true)
{
//on completion
}
}
// I THINK THE PROBLEM IS IN HERE BUT I CANT SEE IT
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
if let image = info[UIImagePickerController.InfoKey.originalImage] as? UIImage
{
importImageView.image = image
if case importImageView.image = image {
importImage = image
}
}
else
{
}
self.dismiss(animated: true, completion: nil)
}
func saveImage()
{
//encode image to user defaults
let imageData:NSData = importImage.pngData()! as NSData
//save image to user defaults
UserDefaults.standard.set(imageData, forKey: "screenShotImage")
}
#IBAction func saveScreenButton(_ sender: UIButton) {
saveImage()
dismiss(animated: true, completion: nil)
}
}
First of all use Data instead of NSData in saveImage()
func saveImage() {
let imageData = importImage.pngData()
UserDefaults.standard.set(imageData, forKey: "screenShotImage")
}
Now, try fetching the image from UserDefaults in viewDidLoad() and set it as importImageView's image,
override func viewDidLoad() {
super.viewDidLoad()
if let data = defaults.data(forKey: "screenShotImage"), let image = UIImage(data: data) {
importImage = image
importImageView.image = image
}
}
Also, modify the imagePickerController(_: didFinishPickingMediaWithInfo:) definition to,
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
if let image = info[UIImagePickerController.InfoKey.originalImage] as? UIImage {
importImageView.image = image
importImage = image
}
self.dismiss(animated: true, completion: nil)
}
I've been going at it trying to display a user's selected profile image in a placeholder. I was successfully able to select an image but, as mentioned, it will not display in my selected placeholder.
I repeatedly receive an error stating:
Value of type 'UIImageView' has no member 'sd_setImage'
I have written the following code.
class AddProfilePictureViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
#IBOutlet weak var userProfileImageView: UIImageView!
#IBAction func chooseFromLibraryButtonTapped(_ sender: Any) {
let profileImagePicker = UIImagePickerController()
profileImagePicker.sourceType = UIImagePickerControllerSourceType.photoLibrary
profileImagePicker.mediaTypes = [kUTTypeImage as String]
profileImagePicker.delegate = self
present(profileImagePicker, animated: true, completion: nil)
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any])
{
if let profileImage =
info[UIImagePickerControllerOriginalImage] as? UIImage, let
optimizedImageData = UIImageJPEGRepresentation(profileImage, 0.8) {
// Upload image from here
uploadProfileImageData(imgageData: optimizedImageData)
}
picker.dismiss(animated: true, completion: nil)
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController)
{
picker.dismiss(animated: true, completion: nil)
}
func uploadProfileImageData(imgageData: Data)
{
let activityIndicator = UIActivityIndicatorView.init(activityIndicatorStyle: .gray)
activityIndicator.startAnimating()
activityIndicator.center = self.view.center
self.view.addSubview(activityIndicator)
let storageReference = Storage.storage().reference()
let currentUser = Auth.auth().currentUser
let profileImageRef = storageReference.child("users").child(currentUser!.uid).child("profileImage.jpg")
let uploadMetaData = FirebaseStorage.StorageMetadata()
uploadMetaData.contentType = "image/jpeg"
profileImageRef.putData(imgageData, metadata: uploadMetaData) { (uploadedImageMeta, error) in
activityIndicator.stopAnimating()
activityIndicator.removeFromSuperview()
if error != nil
{
print("Error took place \(String(describing: error?.localizedDescription))")
return
} else {
print("Meta data of uploaded image \(String(describing: uploadedImageMeta))")
}
let storageReference = Storage.storage().reference()
// create a reference to the file you want to download
let profileImageDownloadUrlReference = storageReference.child("users/\(currentUser!.uid)/profileImage.jpg")
// UIImageView in your ViewController
// let imageView: UIImageView = self.imageView
// Placeholder image
let placeholderImage = UIImage(named: "placeholder.jpg")
// Load the image using SDWebImage
userProfileImageView.sd_setImage(with: profileImageDownloadUrlReference, placeholderImage: placeholderImage)
// fetch the download url
profileImageDownloadUrlReference.downloadURL { url, error in
if let error = error {
// Handle any errors
print("Error took place \(error.localizedDescription)")
} else {
// get the download URL for 'images/stars.jpg'
print("Profile image download URL \(String(describing: url!))")
}
}
}
}
}
Add pod 'SDWebImage', '~> 5.0' in your Podfile
Navigate to your project folder and type "pod install"
In your swift file, add "import SDWebImage"
You probably forgot to install SDWebImage library. You can check out this library here.
https://github.com/SDWebImage/SDWebImage
Add pod 'FirebaseUI/Storage' in your Podfile
Open terminal and pod install
You need to import FirebaseUI in this swift file.
import UIKit
import Firebase
import FirebaseUI
class SelectProposeViewController: UIViewController {
#IBOutlet weak var ImageView: UIImageView!
#IBOutlet weak var UserImageView: UIImageView!
var proposal_dict: ProposalShow?
let storage = Storage.storage()
override func viewDidLoad() {
super.viewDidLoad()
self.title=""
self.UserImageView.sd_setImage(with: self.storage.reference(forURL: self.proposal_dict?.proposalDict["userphoto"] as! String) , placeholderImage: UIImage())
self.ImageView.sd_setImage(with: self.storage.reference(forURL: self.proposal_dict?.proposalDict["postphoto"] as! String))
}
}
You have to import SDWebImage on top of class where you set image to your image view.
After import just build your app and error will be gone.
You can also use KingFisher pod for image loading. It is lighter than sdwebimage.
1.First create a storage reference (see Firebase doc): https://Firebase.google.com/docs/storage/ios/download-files
2.Now you can load the image directly from Firebase Storage as URL no need to store any HTTP URL Firebase will internally do everything for you.
You can save a reference name of a particular image in the Firebase Database to identify that particular image.
Use this code.
// Reference to an image file in Firebase Storage
let reference = storageRef.child("images/stars.jpg")
// UIImageView in your ViewController
let imageView: UIImageView = self.imageView
// Placeholder image
let placeholderImage = UIImage(named: "placeholder.jpg")
// Load the image using SDWebImage
imageView.sd_setImage(with: reference, placeholderImage: placeholderImage)
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)
}
}
I need help please I have a button .. and UIImage without a picture for the user can choose a picture he wants!
And it's important that the user-selected image stays the same once the user is completely signed out of the app
I want the user to select a picture from the gallery or take a picture and then the picture he chose will be displayed on the profile picture and will not disappear even after exiting the application .. The data will be saved
Can I do this please?
class ViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
#IBOutlet var imageView: UIImageView!
#IBOutlet var chooseBuuton: UIButton!
var imagePicker = UIImagePickerController()
func imagePickerController(picker: UIImagePickerController!, didFinishPickingImage image: UIImage!, editingInfo: NSDictionary!){
self.dismiss(animated: true, completion: { () -> Void in
})
imageView.image = image
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
dismiss(animated: true, completion: nil)
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
let image = info[UIImagePickerControllerOriginalImage] as! UIImage
imageView.image = image
imageView.contentMode = .scaleAspectFill
dismiss(animated: true, completion: nil)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let controller = UIImagePickerController()
controller.delegate = self
controller.sourceType = .photoLibrary
present(controller, animated: true, completion: nil)
}
}
You need to save image in Application storage like UserDefaults, Document directory or in DB if you are using in Application.
Preferably images are saving to Application document directory, you can use below method to save image:
func saveImage(image : UIImage, imageName: String){
let documentDirectoryPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let fileURL = documentDirectoryPath.appendingPathComponent(imageName)
if let data = UIImageJPEGRepresentation(image, 1.0),
!FileManager.default.fileExists(atPath: fileURL.path) {
do {
try data.write(to: fileURL)
print("Image saved")
} catch {
print("Image save error:", error)
}
}
}
You can get back image via below function:
func getImage(imageName: String) -> UIImage?{
let documentDirectoryPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let fileURL = documentDirectoryPath.appendingPathComponent(imageName)
let image = UIImage(contentsOfFile: fileURL.path)
return image
}
In your UIImagePickerControllerDelegate delegate method call function saveImage with image & imageName to save image in document directory permanently:
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
// Do your stuff here, then save image to document directory:
saveImage(image:image, imageName: "profile.jpg")
}
To show image on your imageView get image from document directory in your viewDidLoad function:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
if let image = getImage(imageName: "profile.jpg"){
imageview.image = image
}
}