Hello world! I have a little bit of struggling with updating my main view wich is ContentView. Structure of app is little another then I found on forum, so here is my files. Please help with that <_<
P.S. I tried several hints for manual update but ContentView not react to changing of @State-like objects at all. When app is manually reopens - new positions in List is appeared
ContentView.swift
import SwiftUI
struct ContentView: View {
@StateObject var viewModel = ViewModel()
@State var refresh: Bool = false
@State private var showingAddView = false
var body: some View {
NavigationView {
List {
ForEach(viewModel.meetup) { person in
PersonRowView(person: person)
.padding(-15)
}
.onDelete { index in
viewModel.removeRows(at: index)
}
}
.navigationTitle("Meetup")
.navigationBarTitleDisplayMode(.large)
.toolbar {
ToolbarItem(placement: .bottomBar) {
Button {
showingAddView = true
} label: {
ZStack {
Circle()
.fill(.black.opacity(0.65))
.frame(width: 64, height: 64)
Image(systemName: "plus")
.foregroundColor(.white)
.font(.title)
}
.padding(.bottom)
}
}
}
.sheet(isPresented: $showingAddView) {
AddPersonView()
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
PersonRowView.swift
import SwiftUI
import UIKit
struct PersonRowView: View {
let person: Person
@State private var image: Image?
var body: some View {
HStack {
image?
.resizable()
.scaledToFit()
.clipShape(Circle())
.frame(width: 100, height: 100)
.shadow(radius: 2)
Text(person.name)
.font(.title2)
Spacer()
}
.onAppear(perform: loadImage)
}
func loadImage() {
image = person.convert()
}
}
struct PersonRowView_Previews: PreviewProvider {
static var previews: some View {
PersonRowView(person: Person.example)
.previewLayout(.sizeThatFits)
.padding()
}
}
AddPersonView.swift
import SwiftUI
struct AddPersonView: View {
@Environment(\.dismiss) var dismiss
@ObservedObject var viewModel = ViewModel()
@State private var image: Image?
@State private var inputUIImage: UIImage?
@State private var savingUIImage: UIImage?
@State private var showingImagePicker = false
@State private var personName = ""
var body: some View {
NavigationView {
VStack {
TextField("Enter the name", text: $personName)
.textFieldStyle(.roundedBorder)
.padding()
ZStack {
Rectangle()
.fill(.secondary)
.frame(width: 300, height: 300)
Text("Tap to select photo")
.foregroundColor(.white)
.font(.headline)
image?
.resizable()
.scaledToFit()
.frame(width: 300, height: 300)
}
.onTapGesture {
showingImagePicker = true
}
}
.onChange(of: inputUIImage) { _ in loadImage() }
.sheet(isPresented: $showingImagePicker) {
ImagePicker(image: $inputUIImage)
}
.toolbar {
ToolbarItem(placement: .automatic) {
Button("Save") {
viewModel.addNewPerson(name: personName, inputUIImage: savingUIImage)
viewModel.updateView()
dismiss()
}
.disabled(inputUIImage == nil)
.disabled(personName == "")
}
ToolbarItem(placement: .cancellationAction) {
Button("Cancel", role: .cancel) {
dismiss()
}
}
}
}
}
func loadImage() {
guard let inputUIImage = inputUIImage else { return }
image = Image(uiImage: inputUIImage)
savingUIImage = inputUIImage
}
}
struct AddPersonView_Previews: PreviewProvider {
static var previews: some View {
AddPersonView()
}
}
Person.swift
import Foundation
import UIKit
import SwiftUI
struct Person: Equatable, Identifiable, Codable {
var id: UUID
var name: String
var avatar: Data
init(name: String, avatar: Data) {
self.id = UUID()
self.name = name
self.avatar = avatar
}
func convert() -> Image {
let uiImageData = avatar
if let uiImage = UIImage(data: uiImageData) {
return Image(uiImage: uiImage)
}
return Image(systemName: "questionmark.square.dashed")
}
static let exampleAvatarData = UIImage(named: "Steve_Jobs")!.jpegData(compressionQuality: 0.8)
static let example = Person(name: "Steve Jobs", avatar: exampleAvatarData!)
static func <(lhs: Person, rhs: Person) -> Bool {
lhs.name < rhs.name
}
}
ViewModel.swift
import Foundation
import UIKit
@MainActor class ViewModel: ObservableObject {
@Published var isUnlocked = true
@Published var authError = ""
@Published private(set) var meetup: [Person]
let savePathMeetup = FileManager.documentsDirectory.appendingPathComponent("SavedPersons")
init() {
do {
let personsData = try Data(contentsOf: savePathMeetup)
meetup = try JSONDecoder().decode([Person].self, from: personsData)
} catch {
meetup = []
}
}
func addNewPerson(name: String, inputUIImage: UIImage?) {
guard let imageData = inputUIImage?.jpegData(compressionQuality: 0.8) else { return }
let newPerson = Person(name: name, avatar: imageData)
self.meetup.append(newPerson)
saveMeetup()
}
func saveMeetup() {
do {
let personsData = try JSONEncoder().encode(self.meetup)
try personsData.write(to: savePathMeetup, options: [.atomic, .completeFileProtection])
print("saved meetup")
} catch {
print("unable to save data")
}
}
func updateView() {
self.objectWillChange.send()
}
func removeRows(at offsets: IndexSet) {
self.meetup.remove(atOffsets: offsets)
saveMeetup()
print("deleted")
}
}
ImagePicker and FileManager-DocumentsDirectory are pretty standart