I add a photo to the view, but after closing the application, the photo is removed, how can I save the photo?
Is there some kind of property wrapper?
Thank you in advance!
ContentView (This is where the photos that the user has selected are stored):
struct ContentView: View {
@State private var showSheet: Bool = false
@State private var showImagePicker: Bool = false
@State private var sourceType: UIImagePickerController.SourceType = .camera
@State private var image: UIImage?
var body: some View {
VStack {
Image(uiImage: image ?? UIImage(named: "unknown")!)
.resizable()
.frame(width: 300, height: 300)
Button("change your avatar") {
self.showSheet = true
}.padding()
.actionSheet(isPresented: $showSheet) {
ActionSheet(title: Text("Changing the avatar"), message: Text("Choosing a photo"), buttons: [
.default(Text("Gallery")) {
self.showImagePicker = true
self.sourceType = .photoLibrary
},
.default(Text("Camera")) {
self.showImagePicker = true
self.sourceType = .camera
},
.cancel()
])
}
}
.padding(.bottom, 70)
.sheet(isPresented: $showImagePicker) {
ImagePicker(image: self.$image, isShown: self.$showImagePicker, sourceType: self.sourceType)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
ImagePicker (The functions of the camera and gallery are spelled out here):
import Foundation
import SwiftUI
class ImagePickerCoordinator: NSObject, UINavigationControllerDelegate, UIImagePickerControllerDelegate {
@Binding var image: UIImage?
@Binding var isShown: Bool
init(image: Binding<UIImage?>, isShown: Binding<Bool>) {
_image = image
_isShown = isShown
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
if let uiImage = info[UIImagePickerController.InfoKey.originalImage] as? UIImage {
image = uiImage
isShown = false
}
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
isShown = false
}
}
struct ImagePicker: UIViewControllerRepresentable {
typealias UIViewControllerType = UIImagePickerController
typealias Coordinator = ImagePickerCoordinator
@Binding var image: UIImage?
@Binding var isShown: Bool
var sourceType: UIImagePickerController.SourceType = .camera
func updateUIViewController(_ uiViewController: UIImagePickerController, context: UIViewControllerRepresentableContext<ImagePicker>) {
}
func makeCoordinator() -> ImagePicker.Coordinator {
return ImagePickerCoordinator(image: $image, isShown: $isShown)
}
func makeUIViewController(context: UIViewControllerRepresentableContext<ImagePicker>) -> UIImagePickerController {
let picker = UIImagePickerController()
picker.sourceType = sourceType
picker.delegate = context.coordinator
return picker
}
}
3
Answers
You can store image data using default and get back image from the image data.
Save here
Get here
You can store files in caches and documents directories.
First you have to retrieve their urls (here
cachesDirectory
):You cannot write an UIImage, you must first retrieve its data.
Then build a path, where this data will be written.
And create the file.
To read, the principle is the same.
We get the path and
try
to read the data.The
Data
constructor can throw an error.Here I am not dealing with the error.
Then all you have to do is use this data to build an UIImage (the UIImage constructor does not throw an error but returns nil if it fails).
Let’s put all of this in a
class
that will help us to handle these reads / writes.Warning: if the images are large, or if you need to access a lot of images at the same time, it can be useful to start these reads / writes asynchronously. But it’s another problem…
You could’ve use
@AppStorage("key")
instead of@State
for something simpler likeString
orInt
, it’ll save it toUserDefaults
But
UserDefaults
cannot work directly withUIImage
(and so@AppStorage
cannot too)Sadly
AppStorage
cannot be successfully extended to add support forUIImage
AppStorage
to iOS 13, and as it has open source code it can be easily extended to storeUIImage
by converting toData
:UserDefaults
will give you better performance. You can create your own@propertyWrapper
, like this: