Bear with me as I’m a novice programmer. I'm building out a project where a user goes into their photo library, selects a photo and displays the image's name in a tableView. They can select that image in the tableView and it will then load a full size of that image.
Problem: When I click the row item in the tableView to load the image, the new view controller is just a white screen and not the image previously selected.
Here is the view controller that displays the image:
class DetailViewController: UIViewController {
var selectedImage: String?
#IBOutlet var image: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
if let imageToLoad = selectedImage {
image.image = UIImage(named: imageToLoad)
}
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
navigationController?.hidesBarsOnTap = true
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillAppear(animated)
}
}
Here is where the new view is loaded:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if let dvc = storyboard?.instantiateViewController(identifier: "Detail") as? DetailViewController {
dvc.selectedImage = imageArray[indexPath.row].image
navigationController?.pushViewController(dvc, animated: true)
}
}
The photos are picked using a navBar button item and loaded into the imageArray of type Photo here:
#objc func loadImage() {
let picker = UIImagePickerController()
picker.allowsEditing = true
picker.delegate = self
present(picker, animated: true)
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
guard let image = info[.editedImage] as? UIImage else { return }
let imageName = UUID().uuidString
let imagePath = getDocumentsDirectory().appendingPathComponent(imageName)
// Convert to JPEG data
if let jpegData = image.jpegData(compressionQuality: 0.8) {
// We have a valid photo
try? jpegData.write(to: imagePath)
}
let photo = Photo(name: "Unkown", image: imageName)
imageArray.append(photo)
tableView.reloadData()
dismiss(animated: true)
}
func getDocumentsDirectory() -> URL {
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
return paths[0]
}
And finally here is the Photo class that holds the photo data
import UIKit
class Photo: NSObject {
var name: String
var image: String
init(name: String, image: String) {
self.name = name
self.image = image
}
}
I can see the image name string populated in the debugger when the new view controller is loading, but for some reason it’s just a white screen displayed.
New Detail View Controller
#IBOutlet var image: UIImageView!
var url = URL(string: "")
override func viewDidLoad() {
super.viewDidLoad()
if let data = try? Data(contentsOf: url!) {
image.image = UIImage(data: data)
}
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
navigationController?.hidesBarsOnTap = true
}
override func viewWillDisappear(_ animated: Bool) {
//super.viewWillAppear(animated)
}
New didSelectRowAt method
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if let dvc = storyboard?.instantiateViewController(identifier: "Detail") as? DetailViewController {
let imageName = UUID().uuidString
let urlString = getDocumentsDirectory().appendingPathComponent(imageName)
dvc.url = urlString
navigationController?.pushViewController(dvc, animated: true)
}
}
The problem is this line:
image.image = UIImage(named: imageToLoad)
You won’t find the image that way. That method looks in the app bundle. That’s not where the image is.
You need to fetch the image from where you put it, in the Documents directory, using essentially the reverse of the way you saved it. Get the Documents directory url, form the image file url, and load the image data from that url using the Data struct and then create the UIImage from the data.
So once you have calculated the url for this image, exactly the way you calculated it to save it in the first place, you will say
if let data = try? Data(contentsOf: url) {
image.image = UIImage(data: data)
}
Related
In my application, I'm using the QuickLook framework to view the document files such as pdf, ppt, doc, etc. etc. But due to privacy concerns, I don't want that the user can share this document with others so please let me know how to disable/hide the share button and also the copy-paste option.
I know this question can be asked by a number of times and tried many solutions but nothing works for me
hide share button from QLPreviewController
UIDocumentInteractionController remove Actions Menu
How to hide share button in QLPreviewController using swift?
Hide right button n QLPreviewController?
Please suggest to me to achieve this.
Here is my demo code:
import UIKit
import QuickLook
class ViewController: UIViewController {
lazy var previewItem = NSURL()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
#IBAction func displayLocalFile(_ sender: UIButton){
let previewController = QLPreviewController()
// Set the preview item to display
self.previewItem = self.getPreviewItem(withName: "samplePDf.pdf")
previewController.dataSource = self
self.present(previewController, animated: true, completion: nil)
}
#IBAction func displayFileFromUrl(_ sender: UIButton){
// Download file
self.downloadfile(completion: {(success, fileLocationURL) in
if success {
// Set the preview item to display======
self.previewItem = fileLocationURL! as NSURL
// Display file
let previewController = QLPreviewController()
previewController.dataSource = self
self.present(previewController, animated: true, completion: nil)
}else{
debugPrint("File can't be downloaded")
}
})
}
func getPreviewItem(withName name: String) -> NSURL{
// Code to diplay file from the app bundle
let file = name.components(separatedBy: ".")
let path = Bundle.main.path(forResource: file.first!, ofType: file.last!)
let url = NSURL(fileURLWithPath: path!)
return url
}
func downloadfile(completion: #escaping (_ success: Bool,_ fileLocation: URL?) -> Void){
let itemUrl = URL(string: "https://images.apple.com/environment/pdf/Apple_Environmental_Responsibility_Report_2017.pdf")
// then lets create your document folder url
let documentsDirectoryURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
// lets create your destination file url
let destinationUrl = documentsDirectoryURL.appendingPathComponent("filename.pdf")
// to check if it exists before downloading it
if FileManager.default.fileExists(atPath: destinationUrl.path) {
debugPrint("The file already exists at path")
completion(true, destinationUrl)
// if the file doesn't exist
} else {
// you can use NSURLSession.sharedSession to download the data asynchronously
URLSession.shared.downloadTask(with: itemUrl!, completionHandler: { (location, response, error) -> Void in
guard let tempLocation = location, error == nil else { return }
do {
// after downloading your file you need to move it to your destination url
try FileManager.default.moveItem(at: tempLocation, to: destinationUrl)
print("File moved to documents folder")
completion(true, destinationUrl)
} catch let error as NSError {
print(error.localizedDescription)
completion(false, nil)
}
}).resume()
}
}
}
//MARK:- QLPreviewController Datasource
extension ViewController: QLPreviewControllerDataSource {
func numberOfPreviewItems(in controller: QLPreviewController) -> Int {
return 1
}
func previewController(_ controller: QLPreviewController, previewItemAt index: Int) -> QLPreviewItem {
controller.navigationItem.rightBarButtonItem = nil
return self.previewItem as QLPreviewItem
}
}
Please provide your suggestion to do so or any other framework to view different file formats.
Here is the image
Find below adopted my approach to your code (with modifications to test locally, but the code should be clear). The idea is
a) to override, which is completely allowed by API, needed classes to intercept modification
b) to use intentionally own UINavigationController, as only one navigation controller can be in stack
So here is code:
// Custom navigation item that just blocks adding right items
class MyUINavigationItem: UINavigationItem {
override func setRightBarButtonItems(_ items: [UIBarButtonItem]?, animated: Bool) {
// forbidden to add anything to right
}
}
// custom preview controller that provides own navigation item
class MyQLPreviewController: QLPreviewController {
private let item = MyUINavigationItem(title: "")
override var navigationItem: UINavigationItem {
get { return item }
}
}
class MyViewController : UIViewController, QLPreviewControllerDataSource {
lazy var previewItem = NSURL()
override func loadView() {
let view = UIView()
view.backgroundColor = .white
// just stub testing code
let button = UIButton(type: .roundedRect)
button.frame = CGRect(x: 150, y: 200, width: 200, height: 20)
button.setTitle("Show", for: .normal)
button.addTarget(self, action:
#selector(displayLocalFile(_:)), for: .touchDown)
view.addSubview(button)
self.view = view
}
#objc func displayLocalFile(_ sender: UIButton){
let previewController = MyQLPreviewController() // << custom preview
// now navigation item is fully customizable
previewController.navigationItem.title = "samplePDF.pdf"
previewController.navigationItem.leftBarButtonItem =
UIBarButtonItem(barButtonSystemItem: .done, target: self,
action: #selector(closePreview(_:)))
// wrap it into navigation controller
let navigationController = UINavigationController(rootViewController: previewController)
// Set the preview item to display
self.previewItem = self.getPreviewItem(withName: "samplePDF.pdf")
previewController.dataSource = self
// present navigation controller with preview
self.present(navigationController, animated: true, completion: nil)
}
#objc func closePreview(_ sender: Any?) {
self.dismiss(animated: true) // << dismiss preview
}
func getPreviewItem(withName name: String) -> NSURL{
// Code to diplay file from the app bundle
let file = name.components(separatedBy: ".")
let path = Bundle(for: type(of: self)).path(forResource: file.first!, ofType: file.last!)
let url = NSURL(fileURLWithPath: path!)
return url
}
func numberOfPreviewItems(in controller: QLPreviewController) -> Int {
return 1
}
func previewController(_ controller: QLPreviewController, previewItemAt index: Int) -> QLPreviewItem {
return self.previewItem as QLPreviewItem
}
}
Hello everyone and thanks in advance to everyone who helps me :)
I want to let people choose a picture from the gallery (like a profile picture)
and the selected image will remain even after exiting the app ...
And when I go back into the app I want see picture I chose
How can I do this please?
USE-Swift 4
class ViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
#IBOutlet weak var imageView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
imageView.backgroundColor = UIColor.lightGray
}
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 can use saveImage() method to save image in document directory when you pick any image. An call getImage() method to get image in viewDidLoad and set it on imageView.
func saveImage(_ image: UIImage) {
let url = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first?.appendingPathComponent("Profile.jpg")
let imageData = UIImageJPEGRepresentation(image, 0.8)
try? imageData?.write(to: url!)
}
func getImage() -> UIImage? {
let url = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first?.appendingPathComponent("Profile.jpg")
if let data = try? Data(contentsOf: url!) {
return UIImage(data: data)
}
return nil
}
Use user default and check user default value in ViewDidLoad method
class ViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
#IBOutlet weak var imageView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let possibleOldImagePath = UserDefaults.standard.object(forKey: "path") as! String?
if let oldImagePath = possibleOldImagePath {
let oldFullPath = self.documentsPathForFileName(oldImagePath)
let oldImageData = NSData(contentsOfFile: oldFullPath)
// here is your saved image:
imageView = UIImage(data: oldImageData)
}
imageView.backgroundColor = UIColor.lightGray
}
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
// save to userDefaults
let imageData = UIImageJPEGRepresentation(image, 1)
let relativePath = "image_\(NSDate.timeIntervalSinceReferenceDate).jpg"
let path = self.documentsPathForFileName(relativePath)
imageData.writeToFile(path, atomically: true)
UserDefaults.standard.set(relativePath, forKey: "path")
UserDefaults.standard.synchronize()
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)
}
}
Method for path
func documentsPathForFileName(name: String) -> String {
let paths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true);
let path = paths[0] as String;
let fullPath = path.stringByAppendingPathComponent(name)
return fullPath
}
This question already has answers here:
how to load image from local path ios swift (by path)
(8 answers)
Closed 5 years ago.
I am attempting to create a application in xcode 8 swift 3 where the user adds an image using a imagePickerController then when the user clicks either save or exits the imagePickerController I want the image to be saved locally, so next time the app is loaded (for example after a device restart) the image is there. Just to be clear I Do Not want to save the image to the camera roll. Here is my code thus far without any save or load methods, (just the save button reference) any help would be very appreciated as I have been attempting to do this for a long while.
import UIKit
class timetable: UIViewController, UINavigationControllerDelegate, UIImagePickerControllerDelegate {
#IBOutlet var imageviewtimetable: UIImageView!
#IBAction func save(_ sender: Any) {
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func selectImageFromPhotoLibrary(_ sender: Any) {
let imagePickerController = UIImagePickerController()
imagePickerController.sourceType = .photoLibrary
imagePickerController.delegate = self
present(imagePickerController, animated: true, completion: nil)
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController){
dismiss(animated: true, completion:nil)
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]) {
let selectedImage = info[UIImagePickerControllerOriginalImage] as! UIImage
imageviewtimetable.image = selectedImage
dismiss(animated: true,completion:nil)
}
}
For info : click here
Save image in Document Directory
func saveImageDocumentDirectory() {
let fileManager = NSFileManager.defaultManager()
let paths = (getDirectoryPath() as NSString).stringByAppendingPathComponent("apple.jpg")
let image = UIImage(named: "apple.jpg")
print(paths)
let imageData = UIImageJPEGRepresentation(image!, 0.5)
fileManager.createFileAtPath(paths as String, contents: imageData, attributes: nil)
}
Get Document Directory Path
func getDirectoryPath() -> String {
let paths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)
let documentsDirectory = paths[0]
return documentsDirectory
}
get Image from document directory
func getImage(){
let fileManager = NSFileManager.defaultManager()
let imagePath = (self.getDirectoryPath() as NSString).stringByAppendingPathComponent("apple.jpg")
if fileManager.fileExistsAtPath(imagePath){
self.imageView.image = UIImage(contentsOfFile: imagePath)
} else {
print("No Image")
}
}
create Directory
func createDirectory(){
let fileManager = NSFileManager.defaultManager()
let paths = getDirectoryPath() as NSString).stringByAppendingPathComponent("customDirectory")
if !fileManager.fileExistsAtPath(paths){
try! fileManager.createDirectoryAtPath(paths, withIntermediateDirectories: true, attributes: nil)
} else {
print("Already dictionary created.")
}
}
I am using Swift 3 and have been following apples https://developer.apple.com/library/content/referencelibrary/GettingStarted/DevelopiOSAppsSwift/Lesson4.html have created 20 different UIImage views on 20 different UIViewcontroller
They are called photoImageView1 photoImageView2 etc. currently you are able to click on each generic image and input your own image.
I would like the user to be able to input their image but also save it and when they re-open that same viewcontroller the image they inputed is there.
I have looked at many different methods however have been unsuccessful i have attempted following this question Save images in NSUserDefaults? and was once again unsuccessful. Any help would be much appreciated, Thanks.
i ended up using this method it may not be the best but its easy and it works.
import UIKit
class timetable: UIViewController, UINavigationControllerDelegate, UIImagePickerControllerDelegate {
#IBOutlet var imageviewtimetable: UIImageView!
#IBAction func saveMyImage(_ sender: UIBarButtonItem) {
let myTimeTableImage = imageviewtimetable.image
let theImageData:NSData = UIImagePNGRepresentation(myTimeTableImage!)! as NSData
UserDefaults.standard.set(theImageData, forKey: "mySavedImage")
let data = UserDefaults.standard.object(forKey: "mySavedImage")
imageviewtimetable.image = UIImage(data: data as! Data)
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let data = UserDefaults.standard.object(forKey: "mySavedImage")
imageviewtimetable.image = UIImage(data: data as! Data)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func selectImageFromPhotoLibrary(_ sender: Any) {
let imagePickerController = UIImagePickerController()
imagePickerController.sourceType = .photoLibrary
imagePickerController.delegate = self
present(imagePickerController, animated: true, completion: nil)
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController){
dismiss(animated: true, completion:nil)
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]) {
let selectedImage = info[UIImagePickerControllerOriginalImage] as! UIImage
imageviewtimetable.image = selectedImage
dismiss(animated: true,completion:nil)
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
}
You can store the image name in a database, iCloud, a plist or even Userdefaults. To save an image to disk, but not the photo library, use:
import PlaygroundSupport
import UIKit
func save(image: UIImage, name: String) -> Bool {
guard var path = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first,
let imageData = UIImagePNGRepresentation(image) else {
return false
}
path = path.appendingPathComponent(name)
do {
try imageData.write(to: path)
} catch {
return false
}
return true
}
func loadImage(name: String) -> UIImage? {
guard var path = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else {
return nil
}
path = path.appendingPathComponent(name)
return UIImage(contentsOfFile: path.relativePath)
}
let image = UIImage(named: "test.png")!
save(image: image, name: "test2.png")
let loadedImage = loadImage(name: "test2.png")
PlaygroundPage.current.liveView = UIImageView(image: loadedImage)
EDIT: I changed the source code to a full playground so you can copy and paste it into a playground and run it. The only thing you need to do is drag a file named test.png into the Resources folder.
I am trying to make a simple "Profile" view that allows the user to change his picture. the image picker loads the gallery successfully and i choose the new image then write it to documents directory to load it in next launch, the problem is the imageview is not refreshing with the new image until i exit the app and relaunch again (only viewDidLoad works but viewWillAppear is loading the old image although it is overwritten!) any ideas?
import UIKit
class Profile: UIViewController,UIImagePickerControllerDelegate,UINavigationControllerDelegate {
var imagePicker:UIImagePickerController=UIImagePickerController()
var pickedImage:UIImage?
let filemgr = NSFileManager.defaultManager()
#IBOutlet weak var profilepic: UIImageView!
#IBOutlet weak var lblheight: UILabel!
#IBOutlet weak var lblwidth: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
profilepic.layer.borderWidth=1
profilepic.layer.borderColor=UIColor.blackColor().CGColor
let tapGestureRecognizer = UITapGestureRecognizer(target:self, action:Selector("imageTapped:"))
profilepic.userInteractionEnabled = true
profilepic.addGestureRecognizer(tapGestureRecognizer)
imagePicker.delegate=self
if profileImageExists()
{
pickedImage=UIImage(named: Operations.getDocumentsDirectory().stringByAppendingPathComponent("profile.png"))!
} else {
pickedImage=UIImage(named:"camera.png")!
}
profilepic.image=pickedImage
}
func imageTapped(img: AnyObject)
{
imagePicker.allowsEditing = false
imagePicker.sourceType = .PhotoLibrary
presentViewController(imagePicker, animated: true, completion:
{
self.lblheight.text="completed"
print ("completed image tab")
}
)
}
override func viewWillAppear(animated: Bool) {
pickedImage=UIImage(named: Operations.getDocumentsDirectory().stringByAppendingPathComponent("profile.png"))!
profilepic.image=pickedImage
}
// MARK: - UIImagePickerControllerDelegate Methods
func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]) {
if let pickedImage = info[UIImagePickerControllerOriginalImage] as? UIImage {
profilepic.contentMode = .ScaleToFill
profilepic.image = pickedImage
if let data = UIImagePNGRepresentation(pickedImage) {
let filename = Operations.getDocumentsDirectory().stringByAppendingPathComponent("profile.png")
data.writeToFile(filename, atomically: true)
}
}
dismissViewControllerAnimated(true, completion: nil)
}
func profileImageExists() -> Bool
{
if let profileImage=UIImage(named: Operations.getDocumentsDirectory().stringByAppendingPathComponent("profile.png"))
{
return true
}
else
{
return false
}
}
}
Loading an UIImage with init(named:) caches the image. So as long as the image name does not change or the system is emptying the cache (for example when you restart the app) the image will be used from the cache and not loaded again.
Try to use init(contentsOfFile:) instead to load the image.
Try updating your imagePickerController method. You have used pickedImage as an instance variable and an local optional variable. I have used newImage instead.
func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]) {
if let newImage = info[UIImagePickerControllerOriginalImage] as? UIImage {
profilepic.contentMode = .ScaleToFill
profilepic.image = newImage
pickedImage = newImage
if let data = UIImagePNGRepresentation(newImage) {
let filename = Operations.getDocumentsDirectory().stringByAppendingPathComponent("profile.png")
data.writeToFile(filename, atomically: true)
}
}
dismissViewControllerAnimated(true, completion: nil)
}