I get the error "Value of optional type 'Photo?' must be unwrapped to refer to member 'name' of wrapped base type 'Photo'" when I try to send a optional struct on a binding of a TextField.
The content view code example:
import SwiftUI
struct ContentView: View {
@StateObject var viewModel = ContentViewViewModel()
private var photos = [
Photo(id: UUID(), name: "Exclamation", data: (UIImage(systemName: "exclamationmark.triangle")?.jpegData(compressionQuality: 1))!),
Photo(id: UUID(), name: "Circle", data: (UIImage(systemName: "circle.fill")?.jpegData(compressionQuality: 1))!)
]
var body: some View {
VStack {
DetailView(viewModel: viewModel)
}
.onAppear {
viewModel.selectedPhoto = photos[0]
}
}
}
The view model code example:
import Foundation
@MainActor
final class ContentViewViewModel: ObservableObject {
@Published var photos = [Photo]()
@Published var selectedPhoto: Photo?
}
The detail view code example (that uses the content view's view model):
import SwiftUI
struct DetailView: View {
@ObservedObject var viewModel: ContentViewViewModel
var body: some View {
TextField("Photo name here...", text: $viewModel.selectedPhoto.name)
}
}
Note that for some reasons I need the selectedPhoto property be optional.
You can create your own custom @Binding
, so you can handle the process between getting data and set the updated here is an example:
If what you want is to select the photo in a List
or Foreach
you can bind directly to the array.
import SwiftUI
struct Photo {
let id: UUID
var name: String
let data: Data
}
@MainActor
final class ContentViewViewModel: ObservableObject {
@Published var photos = [Photo]()
@Published var selectedPhoto: Photo?
}
struct optionalBinding: View {
@StateObject var viewModel = ContentViewViewModel()
private var photos = [
Photo(id: UUID(), name: "Exclamation", data: (UIImage(systemName: "exclamationmark.triangle")?.jpegData(compressionQuality: 1))!),
Photo(id: UUID(), name: "Circle", data: (UIImage(systemName: "circle.fill")?.jpegData(compressionQuality: 1))!)
]
var body: some View {
VStack {
DetailView2(viewModel: viewModel)
Button("Select Photo") {
viewModel.selectedPhoto = photos[0]
}
}
}
}
struct DetailView2: View {
@ObservedObject var viewModel: ContentViewViewModel
var body: some View {
Text(viewModel.selectedPhoto?.name ?? "No photo selected")
TextField("Photo name here...", text: optionalBinding())
}
func optionalBinding() -> Binding<String> {
return Binding<String>(
get: {
guard let photo = viewModel.selectedPhoto else {
return ""
}
return photo.name
},
set: {
guard let _ = viewModel.selectedPhoto else {
return
}
viewModel.selectedPhoto?.name = $0
//Todo: also update the array
}
)
}
}