我使用UIKit实现了一个搜索栏,将其包装在MovieSearchView
中。当用户输入一些关键字时,它将在以下列表视图中显示搜索结果。
数据searchResults
来自API调用self.model.getMovieSearchResults()
,但是在我的代码中,从未调用过api调用。因此,我想知道当搜索栏中的搜索文本发生更改时,应将条件检查onAppear()
放在哪里以触发api调用?
/ / /搜索视图
import SwiftUI
struct MovieSearchView: View {
@ObservedObject var model = MovieListViewModel()
@State private var searchText: String = ""
var body: some View {
NavigationView {
VStack {
SearchBar(text: $searchText)
.onAppear() {
// If searchText has a value, then call api to fetch movies data.
if self.searchText.isEmpty == false {
self.model.getMovieSearchResults(for: self.searchText)
}
}
List {
ForEach(model.searchResults.filter {
self.searchText.isEmpty ? true : $0.title.lowercased().contains(self.searchText.lowercased())
}) { movie in
Text(movie.title)
}
}
}
}
}
}
struct MovieSearchView_Previews: PreviewProvider {
static var previews: some View {
MovieSearchView()
}
}
/ / /搜索栏
import SwiftUI
struct SearchBar: UIViewRepresentable {
@Binding var text: String
class Coordinator: NSObject, UISearchBarDelegate {
@Binding var text: String
init(text: Binding<String>) {
_text = text
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
text = searchText
}
}
func makeCoordinator() -> SearchBar.Coordinator {
return Coordinator(text: $text)
}
func makeUIView(context: UIViewRepresentableContext<SearchBar>) -> UISearchBar {
let searchBar = UISearchBar(frame: .zero)
searchBar.delegate = context.coordinator
searchBar.searchBarStyle = .minimal
searchBar.autocapitalizationType = .none
return searchBar
}
func updateUIView(_ uiView: UISearchBar, context: UIViewRepresentableContext<SearchBar>) {
uiView.text = text
}
}
只需意识到我不需要再次进行过滤,因为API调用已经从服务器返回了过滤后的搜索结果。更改代码以删除过滤器{}。我还添加了点击手势,以触发导航至电影详细信息视图并在您触及底部时调用第2页。
唯一剩下的问题是清除搜索文本后清除搜索结果。并且应该关闭键盘,它现在一直都在那儿。
import SwiftUI
struct MovieSearchView: View {
@ObservedObject var model = MovieListViewModel()
@State private var searchText: String = ""
@State private var selectedId = -1
@State private var showSheet = false
@State private var page = 1
var body: some View {
NavigationView {
VStack {
SearchBar(text: $searchText, onTextChanged: searchMovies)
List {
ForEach(0..<model.searchResults.count, id: \.self) { i in
MovieListRow(movie: self.model.searchResults[i])
.onTapGesture {
self.selectedId = self.model.searchResults[i].id
self.showSheet.toggle()
}
.onAppear() {
if i == self.model.searchResults.count - 1 {
self.page += 1
self.model.getMovieSearchResults(for: self.searchText, page: self.page)
}
}
}
}
.sheet(isPresented: $showSheet) {
SingleMovieView(movieId: self.selectedId)
}
}
}
}
func searchMovies(for searchText: String) {
if !searchText.isEmpty {
self.model.getMovieSearchResults(for: self.searchText, page: self.page)
}
}
}
struct MovieSearchView_Previews: PreviewProvider {
static var previews: some View {
MovieSearchView()
}
}
extension View {
func Print(_ vars: Any...) -> some View {
for v in vars { print(v) }
return EmptyView()
}
}
您可以使用闭包。在这里,我在搜索栏中添加了onTextChanged: (String) -> Void
。
import SwiftUI
struct MovieSearchView: View {
@ObservedObject var model = MovieListViewModel()
@State private var searchText: String = ""
var body: some View {
NavigationView {
VStack {
SearchBar(text: $searchText, onTextChanged: searchMovies)
List {
ForEach(model.searchResults.filter {
self.searchText.isEmpty ? true : $0.title.lowercased().contains(self.searchText.lowercased())
}) { movie in
Text(movie.title)
}
}
}
}
}
func searchMovies(for searchText: String) {
if !searchText.isEmpty {
model.getMovieSearchResults(for: searchText)
}
}
}
struct MovieSearchView_Previews: PreviewProvider {
static var previews: some View {
MovieSearchView()
}
}
import SwiftUI
struct SearchBar: UIViewRepresentable {
@Binding var text: String
var onTextChanged: (String) -> Void
class Coordinator: NSObject, UISearchBarDelegate {
var onTextChanged: (String) -> Void
@Binding var text: String
init(text: Binding<String>, onTextChanged: @escaping (String) -> Void) {
_text = text
self.onTextChanged = onTextChanged
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
text = searchText
onTextChanged(text)
}
}
func makeCoordinator() -> SearchBar.Coordinator {
return Coordinator(text: $text, onTextChanged: onTextChanged)
}
func makeUIView(context: UIViewRepresentableContext<SearchBar>) -> UISearchBar {
let searchBar = UISearchBar(frame: .zero)
searchBar.delegate = context.coordinator
searchBar.searchBarStyle = .minimal
searchBar.autocapitalizationType = .none
return searchBar
}
func updateUIView(_ uiView: UISearchBar, context: UIViewRepresentableContext<SearchBar>) {
uiView.text = text
}
}
您需要使用如下所示的searchBarDelegate方法:
func searchBar(UISearchBar, shouldChangeTextIn: NSRange, replacementText: String) -> Bool
每次输入时,都会从用户那里收到回调,并且可以在此函数内将api调用激发到服务器。