'SwiftUI : How I can delete the UIimage in the View saved as Codable?
I tried to save the image in my memo with as UIImage as Codable protocol.
My code can be a little bit longer, but please refer to it.
1. first one is the NewMemoView that I can make my memo with ImagePicker.
import SwiftUI
struct NewMemoView: View {
let folder : FolderModel
let memo : MemoModel?
@State private var items : [Any] = []
@EnvironmentObject var vm : MemoViewModel
@Environment(\.presentationMode) var presentationMode
@State private var title : String = ""
@State private var content : String = ""
@State private var color : Color = .mint
@State private var showAlert : Bool = false
@State private var pickedImportance : String = "Anytime 🐳"
@State private var isEditMode : Bool = false
@State private var isSecret : Bool = false
@State private var password : String = ""
@State private var showShareSheet : Bool = false
@State private var showImagePicker : Bool = false
@State private var selectedImage : UIImage?
@State private var tappedImage : Bool = false
@State private var showDeleteImageOption : Bool = false
let importances = ["Anytime 🐳", "Normal ☀️", "Important 🔥"]
var someImage : SomeImage? {
guard let image = selectedImage else {return nil}
return SomeImage(image: image)
}
init(memo : MemoModel?, folder : FolderModel) {
self.folder = folder
self.memo = memo
if let memo = memo {
_title = State(initialValue: memo.title)
_content = State(initialValue: memo.content)
_color = State(initialValue: memo.color)
_pickedImportance = State(initialValue: memo.isImportant)
_isEditMode = State(initialValue: true)
_isSecret = State(initialValue: memo.isSecret)
_password = State(initialValue: memo.password ?? "")
_selectedImage = State(initialValue: memo.image?.image)
}
}
var body: some View {
Form {
Section(header : Text("Title 🔖")) {
TextField("Input the title", text: $title)
.autocapitalization(.none)
.disableAutocorrection(true)
}
Section(header : Text("Importance ✅")) {
Picker("", selection: $pickedImportance) {
ForEach(importances, id: \.self) {
Text($0)
}
}
.pickerStyle(.segmented)
}
Section(header : Text("Content ✏️(총 \(content.count) 자)")) {
TextEditor(text: $content)
.autocapitalization(.none)
.disableAutocorrection(true)
.frame(maxWidth : .infinity)
.frame(height : UIScreen.main.bounds.height * 0.25)
}
.onTapGesture {
UIApplication.shared.closeKeyboard()
}
Section(header : Text("Image List 🌅")) {
HStack {
if let selectedImage = selectedImage {
Image(uiImage: selectedImage)
.resizable()
.scaledToFill()
.frame(width : 50, height : 50)
.clipShape(RoundedRectangle(cornerRadius: 12))
.onTapGesture {
tappedImage.toggle()
}
.onLongPressGesture(perform: {
showDeleteImageOption.toggle()
})
.confirmationDialog(Text("Delete Image"), isPresented: $showDeleteImageOption, actions: {
Button(role : .destructive, action: {
vm.deleteImage(folder: folder, memo: memo!, title: title, content: content, color: color, isImportant: pickedImportance, isSecret: isSecret, password: password)
}, label: {
Text("Delete Image")
})
})
.sheet(isPresented: $tappedImage) {
ImageView(image: self.selectedImage!)
}
} else {
Text("You can upload only 'one' Image")
.foregroundColor(.gray)
}
}
.padding(8)
}
Section(header : Text("Memo Settings ⚙️")) {
ColorPicker("Memo Color", selection: $color, supportsOpacity: false)
Toggle(isOn: $isSecret, label: {
Text("Enable Secret Mode")
})
if isSecret {
HStack {
Text("Password : ")
Spacer()
TextField("Input", text: $password)
}
}
}
Button(action: {
if isEditMode {
if let someImage = someImage {
if vm.checkMemoStatus(title: title, content: content) {
vm.editMemo(folder: folder, memo: memo!, title: title, content: content, color: color, isImportant: pickedImportance, isSecret: isSecret, password: password, image : someImage)
presentationMode.wrappedValue.dismiss()
} else {
vm.editMemo(folder: folder, memo: memo!, title: title, content: content, color: color, isImportant: pickedImportance, isSecret: isSecret, password: password, image : nil)
presentationMode.wrappedValue.dismiss()
}
} else {
self.showAlert.toggle()
}
} else {
if vm.checkMemoStatus(title: title, content: content) {
if let someImage = someImage {
vm.addMemo(folder: folder, title: title, content: content, color: color, isImportant: pickedImportance, isSecret: isSecret, password: password, image: someImage)
presentationMode.wrappedValue.dismiss()
} else {
vm.addMemo(folder: folder, title: title, content: content, color: color, isImportant: pickedImportance, isSecret: isSecret, password: password, image: nil)
presentationMode.wrappedValue.dismiss()
}
} else {
self.showAlert.toggle()
}
}
}, label: {
Text(isEditMode ? "Edit".uppercased() : "save".uppercased())
.fontWeight(.bold)
})
.alert(isPresented : $showAlert) {
Alert(title: Text("Warning 🚫"), message: Text("Check your title and content, please."), dismissButton: .cancel())
}
}//form
.navigationTitle("Add Memo 📒")
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button(action: {
share()
}, label: {
Image(systemName: "square.and.arrow.up")
})
.disabled(!isEditMode)
}
ToolbarItem(placement: .navigationBarTrailing) {
Button(action: {
showImagePicker.toggle()
}, label: {
Image(systemName: "photo")
})
.sheet(isPresented: $showImagePicker) {
ImagePicker(image: $selectedImage)
}
}
}
}
}
extension NewMemoView {
func share() {
let content = memo?.content
let image = memo?.image?.image
let vc = UIActivityViewController(activityItems: [image, content], applicationActivities: nil)
UIApplication.shared.windows.first?.rootViewController?.present(vc, animated: true)
}
}
2. Second one is the viewModel for memos.
import SwiftUI
import Foundation
class MemoViewModel : ObservableObject {
@Published var sortedMode : SortedMode?
@Published var folders : [FolderModel] = [] {
didSet {
saveData()
}
}
init() {
getData()
}
let foldersKey = "Folders_key"
func saveData() {
if let encodedData = try? JSONEncoder().encode(folders) {
UserDefaults.standard.set(encodedData, forKey: foldersKey)
}
}
func getData() {
guard let data = UserDefaults.standard.data(forKey: foldersKey),
let savedData = try? JSONDecoder().decode([FolderModel].self, from: data)
else {return}
self.folders = savedData
}
func addNewFolder(folderName : String) {
let newFolder = FolderModel(folderName: folderName)
folders.append(newFolder)
}
func deleteFolder(folder : FolderModel) {
if let index = folders.firstIndex(where: {$0.id == folder.id}) {
folders.remove(at: index)
}
}
func deleteFolder(indexSet : IndexSet) {
folders.remove(atOffsets: indexSet)
}
func checkFolderNameCount(folderName : String) -> Bool {
if folderName.count < 3 {
return false
} else {
return true
}
}
func checkMemoStatus(title : String, content : String) -> Bool {
if title.count == 0 || content.count == 0 {
return false
} else {
return true
}
}
func addMemo(folder : FolderModel, title : String, content : String, color : Color, isImportant : String, isSecret : Bool, password : String, image : SomeImage?) {
if let index = folders.firstIndex(where: {$0.id == folder.id}) {
let newMemo = MemoModel(title : title, content : content, color : color, isImportant: isImportant, isSecret: isSecret, password: password, image: image)
folders[index].memo.append(newMemo)
}
}
func editMemo(folder : FolderModel, memo : MemoModel, title : String, content : String, color : Color, isImportant : String, isSecret : Bool, password : String, image : SomeImage?) {
if let index = folders.firstIndex(where: {$0.id == folder.id}) {
if let index1 = folders[index].memo.firstIndex(where: {$0.id == memo.id}) {
folders[index].memo[index1] = memo.editMemo(title: title, content: content, color: color, isImportant: isImportant, isSecret: isSecret, password: password, image : image)
}
}
}
func deleteMemo(folder : FolderModel, memo : MemoModel) {
if let index = folders.firstIndex(where: {$0.id == folder.id}) {
if let index1 = folders[index].memo.firstIndex(where: {$0.id == memo.id}) {
folders[index].memo.remove(at: index1)
}
}
}
func deleteImage(folder : FolderModel, memo : MemoModel, title : String, content : String, color : Color, isImportant : String, isSecret : Bool, password : String) {
if let index = folders.firstIndex(where: {$0.id == folder.id}) {
if let index1 = folders[index].memo.firstIndex(where: {$0.id == memo.id}) {
folders[index].memo[index1] = memo.deleteImage(title: title, content: content, color: color, isImportant: isImportant, isSecret: isSecret, password: password)
}
}
}
func checkPassword(memo : MemoModel, password : String) -> Bool {
if memo.password == password {
return true
} else {
return false
}
}
func sortedMemos(folder : FolderModel) {
if let sortedMode = sortedMode {
switch sortedMode {
case .moreImportant:
if let index = folders.firstIndex(where: {$0.id == folder.id}) {
folders[index].memo.sort(by: {$0.isImportant > $1.isImportant})
}
case .faster:
if let index = folders.firstIndex(where: {$0.id == folder.id}) {
folders[index].memo.sort(by: {$0.timeStamp < $1.timeStamp})
}
case .later:
if let index = folders.firstIndex(where: {$0.id == folder.id}) {
folders[index].memo.sort(by: {$0.timeStamp > $1.timeStamp})
}
case .lessImportant:
if let index = folders.firstIndex(where: {$0.id == folder.id}) {
folders[index].memo.sort(by: {$0.isImportant > $1.isImportant})
}
}
}
}
}
enum SortedMode {
case moreImportant, faster, later, lessImportant
}
3. Last one is the codable model for this memo App
import SwiftUI
import Foundation
struct FolderModel : Identifiable, Codable {
var id = UUID()
var folderName : String
var memo : [MemoModel] = []
}
struct MemoModel : Identifiable, Codable {
var id = UUID()
var title : String = "제목 없음"
var content : String
var color : Color = .gray
var timeStamp : String = Date().formatted(date: .numeric, time: .omitted)
var isImportant : String
var isSecret : Bool = false
var password : String?
var image : SomeImage?
func editMemo(title : String, content : String, color : Color, isImportant : String, isSecret : Bool, password : String, image : SomeImage?) -> MemoModel {
return MemoModel(id: id, title: title, content: content, color: color, isImportant: isImportant, isSecret: isSecret, password: password, image: image)
}
func deleteImage(title : String, content : String, color : Color, isImportant : String, isSecret : Bool, password : String) -> MemoModel {
return MemoModel(id: id, title: title, content: content, color: color, isImportant: isImportant, isSecret: isSecret, password: password, image: nil)
}
}
struct SomeImage : Identifiable, Codable {
var id = UUID()
var image: UIImage?
init(image: UIImage) {
self.image = image
}
enum CodingKeys: CodingKey {
case data
case scale
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let scale = try container.decode(CGFloat.self, forKey: .scale)
let data = try container.decode(Data.self, forKey: .data)
self.image = UIImage(data: data, scale: scale)
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
if let image = self.image {
try container.encode(image.pngData(), forKey: .data)
try container.encode(image.scale, forKey: .scale)
}
}
}
I will summarize my point!
When I try to delete the image by deleteMemo, giving nil to image, the NewMemoView is shut down. I don't know why, but it it doing.
When I tap the save button with image, there is some lagging, how I can handle this situation? I think maybe that most of image has bigger capacity than text.
Thanks!
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|
