Reputation: 1299
I've been having an issue with my list where after removing an item, it gets removed correctly, but if I toggle between two branches on the same view, my list items get this weird leading padding.
Not sure how to explain it exactly, so I'm attaching a give of what happens.
https://i.sstatic.net/Y0npU.jpg
Here's my list row code:
if (/** condition **/) {
EmptyView()
} else {
ZStack(alignment: .center) {
NavigationLink(
destination: MovieDetailsView(movie: moviesVM.movie)) {
EmptyView()
}.opacity(0)
WebImage(url: moviesVM.movie.backdropUrl)
.resizable()
.placeholder {
Image("tile_placeholder")
.resizable()
.aspectRatio(aspectRatioW780Poster, contentMode: .fill)
.frame(maxHeight: 170)
.clipped()
}
.transition(.fade(duration: 0.5))
.aspectRatio(aspectRatioW780Poster, contentMode: .fill)
.frame(maxHeight: 170)
.clipped()
.overlay(TextOverlay(movieOrShow: moviesVM.movie))
}
.swipeActions(edge: .trailing, allowsFullSwipe: false) {
// buttons that invoke removal
}
}
And the list rows are instantiated inside the "profile" view, which looks like this:
NavigationView {
VStack {
if profileVM.profileRepository.profile == nil {
HStack(alignment: .center) {
Text("Please log in or sign up to use your watchlist. ☺️")
}
}
if profileVM.profileRepository.profile != nil {
HStack {
Text("Watched")
Toggle("Watched", isOn: $profileVM.defaultTab)
.onChange(of: profileVM.defaultTab, perform: { _ in
profileVM.updateDefaultTab()
})
.labelsHidden()
.tint(Constants.MUSH_BLUE)
Text("Watchlist")
}
// this is where my view "branches" depending on the toggle
HStack {
if !profileVM.defaultTab {
ZStack(alignment: .center) {
List {
ForEach(Array(profileVM.profileRepository.watchedMovies), id: \.id) { movie in
MovieListRow(moviesVM: MoviesViewModel(profileRepository: profileVM.profileRepository, movie: movie, fromProfile: true))
.id(movie.id)
.listRowInsets(EdgeInsets())
.listRowSeparator(.hidden)
}
}
.listStyle(InsetListStyle())
if profileVM.profileRepository.watchedMovies.count == 0 {
Text("Nothing here yet :)")
}
}
} else {
ZStack(alignment: .center) {
List {
ForEach(Array(profileVM.profileRepository.watchlistedMovies), id: \.id) { movie in
MovieListRow(moviesVM: MoviesViewModel(profileRepository: profileVM.profileRepository, movie: movie, fromProfile: true))
.id(movie.id)
.listRowInsets(EdgeInsets())
.listRowSeparator(.hidden)
}
}
.listStyle(InsetListStyle())
if profileVM.profileRepository.watchlistedMovies.count == 0 {
Text("Nothing here yet :)")
}
}
}
}
}
}
.navigationTitle("Watchlist")
}
Upvotes: 2
Views: 404
Reputation: 30746
Here are a few ideas:
moviesVM
- we don't use view model objects in SwiftUI. For transient view data we use the View struct with @State
and @Binding
which makes the struct value type actually behave like an object because when the struct is init again it automatically has the same property values as last time it was init. We do use an object for managing the lifetime of our model data structs or arrays, in an @Published
property in an ObservableObject
which we pass into Views using environmentObject
.ForEach(Array(
- The ForEach
View needs to be given your model data to work correctly, which is usually an array of structs conforming to Identifiable
. E.g. ForEach($model.movies) $movie in
when we want write access.MovieListRow(moviesVM MoviesViewModel(
- it's a mistake to init objects in body
like that, that will slow down SwiftUI and cause memory leaks. In body we only init View data structs that are super fast value types. The body func is called repeatedly and after SwiftUI has diffed and updated the labels etc on screen all of these View structs are discarded.body
is too large, we are supposed to break it up into small Views with a small number of let
s or @State
s. This is called having a tight invalidation. The reason is SwiftUI features dependency and change tracking and if the body is too large then it won't be efficient. Naming the structs can be tricky given these are based on data and not areas of the screen, start with ContentView1
, ContentView2
, etc. til you figure out a good naming scheme, e.g. some name Views based on the properties.I recommend watching WWDC 2020 Data Essentials in SwiftUI to learn proper use of SwiftUI.
Upvotes: 1