简体   繁体   English

SwiftUI 从列表中删除项目时奇怪的列表行为

[英]SwiftUI weird List behavior when removing item from list

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://imgur.com/CbzCSiQ https://imgur.com/CbzCSiQ

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")
}

调试视图层次结构

调试视图

Here are a few ideas:这里有一些想法:

  1. 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. moviesVM - 我们不在 SwiftUI 中使用视图 model 对象。对于瞬态视图数据,我们使用带有@State@Binding的视图结构,这使得结构值类型实际上表现得像 object,因为当结构再次初始化时,它会自动具有与上次初始化时相同的属性值。 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 .我们确实使用 object 来管理我们的 model 数据结构或 arrays 的生命周期,在我们使用environmentObject传递给视图的ObservableObject@Published属性中。
  2. 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 . Eg ForEach($model.movies) $movie in when we want write access. ForEach(Array( - ForEach View 需要提供您的 model 数据才能正常工作,这通常是一个符合Identifiable的结构数组。例如ForEach($model.movies) $movie in我们想要写入访问权限时。
  3. 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. MovieListRow(moviesVM MoviesViewModel( - 像这样在body中初始化对象是错误的,这会减慢 SwiftUI 并导致 memory 泄漏。在 body 中,我们只初始化超快速值类型的视图数据结构。body func 被反复调用,之后SwiftUI 已经区分并更新了屏幕上的标签等所有这些视图结构都被丢弃了。
  4. Your body is too large, we are supposed to break it up into small Views with a small number of let s or @State s.你的body太大了,我们应该用少量的let@State将它分解成小的视图。 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.原因是 SwiftUI 具有依赖性和更改跟踪功能,如果正文太大,则效率不高。 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, eg some name Views based on the properties.命名结构可能会很棘手,因为它们是基于数据而不是屏幕区域,从ContentView1ContentView2等开始,直到您找到一个好的命名方案,例如一些基于属性的命名视图。

I recommend watching WWDC 2020 Data Essentials in SwiftUI to learn proper use of SwiftUI.推荐观看SwiftUI中的WWDC 2020 Data Essentials,学习正确使用SwiftUI。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM