.onReceive 触发两次 | 斯威夫特用户界面

Boo*_*rry 1 xcode ios swift swiftui

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

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

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

这是我的代码:

拣货机型号

import Foundation

class PickerModel: ObservableObject {
    
    @Published var filter = 0
    
    let pickerOptions = ["Popular", "Top Rated"]
    
}
Run Code Online (Sandbox Code Playgroud)

包含选择器的视图:

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)
    }
}
Run Code Online (Sandbox Code Playgroud)

包含以下功能的视图模型:

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
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

任何帮助将不胜感激。

Jon*_*ier 5

ObservedObject每当重新创建您的 s 时,您很可能都会重新创建您的 s FilteredMoviesGridView。每当 SwiftUI 的运行时认为需要重新创建它时,就会发生这种情况。因此,您的视图创建应该很便宜,并且您应该确保不会意外地重新创建您需要的资源。幸运的是,iOS 14 等中的 SwiftUI 使解决这个问题变得更加容易。不要使用 using @ObservedObject,而是使用 use @StateObject,这将在重新创建视图时使同一实例保持活动状态。