.onReceive 发射两次 |用户界面 [英] .onReceive firing twice | SwiftUI

查看:33
本文介绍了.onReceive 发射两次 |用户界面的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个包含选择器的 SwiftUI 视图.我在 Picker 的 .onReceive 中使用 Switch 语句来调用函数.该函数调用外部 API.

I have a SwiftUI view that includes a Picker. I'm using a Switch statement inside .onReceive of the Picker to call a function. The function calls an external API.

问题在于,每当视图初始化即复制数据时,该函数都会被调用两次.我不明白为什么 .onReceive 会被调用两次.

The problem is that the function is being called twice whenever the view is initialised i.e duplicating the data. I'm can't figure out why .onReceive is being called twice.

我认为这可能与在我初始化 Picker 模型然后从 Picker 本身收到另一个通知时调用的 func 有关,但我不确定如何解决它.

I think it might have something to do with the func being called when I init the Picker Model and then getting another notification from the Picker itself but I'm not sure how to work around it.

这是我的代码:

选择器模型

import Foundation

class PickerModel: ObservableObject {
    
    @Published var filter = 0
    
    let pickerOptions = ["Popular", "Top Rated"]
    
}

包含选择器的视图:

import SwiftUI

struct FilteredMoviesGridView: View {
    
    @ObservedObject private var filteredMovieVM = FilteredMovieGridViewModel()
    @ObservedObject private var pickerModel = PickerModel()
    
    private var twoColumnGrid = [GridItem(.flexible()), GridItem(.flexible())]
    
    var body: some View {
        
        NavigationView {
            
            VStack {
                
                Picker(selection: $pickerModel.filter, label: Text("Select")) {
                    ForEach(0 ..< pickerModel.pickerOptions.count) {
                        Text(pickerModel.pickerOptions[$0])
                    }
                }.onReceive(pickerModel.$filter) { (value) in
                    switch value {
                    case 0:
                        filteredMovieVM.movies.removeAll()
                        filteredMovieVM.currentPage = 1
                        filteredMovieVM.fetchMovies(filter: "popularity")
                    case 1:
                        filteredMovieVM.movies.removeAll()
                        filteredMovieVM.currentPage = 1
                        filteredMovieVM.fetchMovies(filter: "vote_average")
                    default:
                        filteredMovieVM.movies.removeAll()
                        filteredMovieVM.currentPage = 1
                        filteredMovieVM.fetchMovies(filter: "popularity")
                    }
                }.pickerStyle(SegmentedPickerStyle())
                
                ScrollView {
                    
                    LazyVGrid(columns: twoColumnGrid, spacing: 10) {
                        
                        ForEach(filteredMovieVM.movies, id:\.id) { movie in
                            
                            NavigationLink(destination: MovieDetailView(movie: movie)) {
                                
                                MovieGridItemView(movies: movie)
                                
                            }.buttonStyle(PlainButtonStyle())
                            
                            .onAppear(perform: {
                                if movie == self.filteredMovieVM.movies.last {
                                    
                                    switch pickerModel.filter {
                                    case 0:
                                        self.filteredMovieVM.checkTotalMovies(filter: "popularity")
                                    case 1:
                                        self.filteredMovieVM.checkTotalMovies(filter: "vote_average")
                                    default:
                                        self.filteredMovieVM.checkTotalMovies(filter: "popularity")
                                    }
                                }
                            })
                        }
                    }
                }
                .navigationBarTitle("Movies")
            }
        }.accentColor(.white)
    }
}

包含函数的视图模型:

import Foundation

class FilteredMovieGridViewModel: ObservableObject {
    
    @Published var movies = [Movie]()
    private var filteredMovies = [MovieList]()
   
    var currentPage = 1
    
    func checkTotalMovies(filter: String) {
        
        if filteredMovies.count < 20 {
            fetchMovies(filter: filter)
        }
    }
    
    func fetchMovies(filter: String) {
        
        WebService().getMoviesByFilter(filter: filter, page: currentPage) { movie in
            
            if let movie = movie {
                self.filteredMovies.append(movie)
                for movie in movie.movies {
                    self.movies.append(movie)
                    print(self.movies.count)
                }
            }
        }
        if let totalPages = filteredMovies.first?.totalPages {
            
            if currentPage <= totalPages {
                currentPage += 1
            }
        }
    }
}

任何帮助将不胜感激.

推荐答案

很可能在重新创建 FilteredMoviesGridView 时,您正在重新创建 ObservedObject.每当 SwiftUI 的运行时认为需要重新创建它时,就会发生这种情况.所以你的视图创建应该很便宜,你应该确保不要意外地重新创建你需要的资源.幸运的是,iOS 14 等中的 SwiftUI 使解决这个问题变得更加容易.不要使用 @ObservedObject,而是使用 @StateObject,这将在重新创建视图时保持相同的实例.

Most likely you're recreating your ObservedObjects whenever your FilteredMoviesGridView is recreated. This can happen whenever SwiftUI's runtime thinks it needs to recreate it. So your view creation should be cheap and you should make sure not to accidentally recreate resources you need. Luckily SwiftUI in iOS 14, etc. has made it much easier to fix this problem. Instead of using @ObservedObject, use @StateObject, which will keep the same instance alive as your view is recreated.

这篇关于.onReceive 发射两次 |用户界面的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆