I have a simple view that shows some photos, through a list. Clicking on any row should display a detailed view of that photo. I'm using the MVVM pattern. However, an infinite loop occurs when I try to set the “selectedPhoto” property of the view model. Is there any way to avoid this loop without having to create a property in the detailed view itself?
Here is the Photo struct:
struct Photo: Identifiable {
var id = UUID()
var name: String
}
Here is the ContentView with an extension (the “updatePhoto” method is causing the infinite loop):
struct ContentView: View {
@StateObject private var viewModel = ViewModel()
var body: some View {
NavigationView {
List {
ForEach(viewModel.photos) { selectedPhoto in
showDetailView(with: selectedPhoto)
}
}
.navigationTitle("Favorite Photo")
}
}
}
extension ContentView {
func showDetailView(with selectedPhoto: Photo?) -> some View {
if let selectedPhoto = selectedPhoto {
viewModel.updatePhoto(selectedPhoto)
}
return DetailView(viewModel: viewModel)
}
}
Here is the view model:
class ViewModel: ObservableObject {
@Published var photos = [
Photo(name: "Photo 1"),
Photo(name: "Photo 2"),
Photo(name: "Photo 3")
]
@Published var selectedPhoto: Photo?
func updatePhoto(_ selectedPhoto: Photo?) {
self.selectedPhoto = selectedPhoto
}
}
And here is the DetailView:
struct DetailView: View {
@ObservedObject private var viewModel: ViewModel
init(viewModel: ViewModel) {
self.viewModel = viewModel
}
var body: some View {
Text(viewModel.selectedPhoto?.name ?? "Unknown photo name")
}
}
Try this approach, using a NavigationLink
to present the DetailView
,
and passing the selectedPhoto
to it using @State var selectedPhoto: Photo
.
struct Photo: Identifiable {
let id = UUID()
var name: String
}
class ViewModel: ObservableObject {
@Published var photos = [Photo(name: "Photo 1"),Photo(name: "Photo 2"),Photo(name: "Photo 3")]
}
struct DetailView: View {
@State var selectedPhoto: Photo
var body: some View {
Text(selectedPhoto.name)
}
}
struct ContentView: View {
@StateObject var viewModel = ViewModel()
var body: some View {
NavigationView {
List {
ForEach(viewModel.photos) { selectedPhoto in
NavigationLink(selectedPhoto.name, destination: DetailView(selectedPhoto: selectedPhoto))
}
}
.navigationTitle("Favorite Photo")
}
}
}
Note that NavigationView
is being deprecated and you will have to use NavigationStack
instead.