Flo*_*lle 2 rest ios swift swift5 swiftui
我正在试验 SwiftUI,并希望使用搜索字符串从我的 REST API 获取更新。
但是,我现在不确定如何将这两个组件组合在一起。
我希望你有一个想法。
这是我的代码:
struct ContentView: View {
@State private var searchTerm: String = ""
@ObservedObject var gameData: GameListViewModel = GameListViewModel(searchString: ### SEARCH STRING ???? ###)
var body: some View {
NavigationView{
Group{
// Games werden geladen...
if(self.gameData.isLoading) {
LoadingView()
}
// Games sind geladen:
else{
VStack{
// Suche:
searchBarView(text: self.$searchTerm)
// Ergebnisse:
List(self.gameData.games){ game in
NavigationLink(destination: GameDetailView(gameName: game.name ?? "0", gameId: 0)){
HStack {
VStack(alignment: .leading, spacing: 2) {
Text(game.name ?? "Kein Name gefunden")
.font(.headline)
Text("Cover: \(game.cover?.toString() ?? "0")")
.font(.subheadline)
.foregroundColor(.gray)
}
}
}
}
}
}
}
.navigationBarTitle(Text("Games"))
}
}
}
Run Code Online (Sandbox Code Playgroud)
和搜索栏实现:
import Foundation
import SwiftUI
struct searchBarView: 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) {
print(searchText)
text = searchText
}
}
func makeCoordinator() -> searchBarView.Coordinator {
return Coordinator(text: $text)
}
func makeUIView(context: UIViewRepresentableContext<searchBarView>) -> UISearchBar {
let searchBar = UISearchBar(frame: .zero)
searchBar.delegate = context.coordinator
return searchBar
}
func updateUIView(_ uiView: UISearchBar, context: UIViewRepresentableContext<searchBarView>) {
uiView.text = text
}
}
Run Code Online (Sandbox Code Playgroud)
不需要涉及 UIKit,你可以像这样声明一个简单的搜索栏:
struct SearchBar: View {
@State var searchString: String = ""
var body: some View {
HStack {
TextField(
"Start typing",
text: $searchString,
onCommit: performSearch)
.textFieldStyle(RoundedBorderTextFieldStyle())
Button(action: performSearch) {
Image(systemName: "magnifyingglass")
}
} .padding()
}
func performSearch() {
}
}
Run Code Online (Sandbox Code Playgroud)
然后将搜索逻辑放在performSearch() 中。
搜索文本应该在视图模型内。
final class GameListViewModel: ObservableObject {
@Published var isLoading: Bool = false
@Published var games: [Game] = []
var searchTerm: String = ""
private let searchTappedSubject = PassthroughSubject<Void, Error>()
private var disposeBag = Set<AnyCancellable>()
init() {
searchTappedSubject
.flatMap {
self.requestGames(searchTerm: self.searchTerm)
.handleEvents(receiveSubscription: { _ in
DispatchQueue.main.async {
self.isLoading = true
}
},
receiveCompletion: { comp in
DispatchQueue.main.async {
self.isLoading = false
}
})
.eraseToAnyPublisher()
}
.replaceError(with: [])
.receive(on: DispatchQueue.main)
.assign(to: \.games, on: self)
.store(in: &disposeBag)
}
func onSearchTapped() {
searchTappedSubject.send(())
}
private func requestGames(searchTerm: String) -> AnyPublisher<[Game], Error> {
guard let url = URL(string: "https://jsonplaceholder.typicode.com/posts") else {
return Fail(error: URLError(.badURL))
.mapError { $0 as Error }
.eraseToAnyPublisher()
}
return URLSession.shared.dataTaskPublisher(for: url)
.map { $0.data }
.mapError { $0 as Error }
.decode(type: [Game].self, decoder: JSONDecoder())
.map { searchTerm.isEmpty ? $0 : $0.filter { $0.title.contains(searchTerm) } }
.eraseToAnyPublisher()
}
}
Run Code Online (Sandbox Code Playgroud)
每次onSearchTapped调用时,它都会触发对新游戏的请求。
这里发生了很多事情——让我们从requestGames.
我正在使用JSONPlaceholder免费 API 来获取一些数据并将其显示在列表中。
requestGames执行网络请求,[Game]从接收到的Data. 除此之外,返回的数组使用搜索字符串进行过滤(由于免费的 API 限制 - 在现实世界中,您将在请求 URL 中使用查询参数)。
现在让我们看看视图模型构造函数。
事件的顺序是:
flatMap)flatMap,处理加载逻辑(在主队列上调度作为isLoading使用Publisher底层,如果在后台线程上发布值,则会发出警告)。replaceError将发布者的错误类型更改为Never,这是对assign运营商的要求。receiveOn 是必要的,因为我们可能仍在后台队列中,多亏了网络请求 - 我们想在主队列上发布结果。assign更新games视图模型上的数组。store保存Cancellable在disposeBag这是视图代码(为了演示,没有加载):
struct ContentView: View {
@ObservedObject var viewModel = GameListViewModel()
var body: some View {
NavigationView {
Group {
VStack {
SearchBar(text: $viewModel.searchTerm,
onSearchButtonClicked: viewModel.onSearchTapped)
List(viewModel.games, id: \.title) { game in
Text(verbatim: game.title)
}
}
}
.navigationBarTitle(Text("Games"))
}
}
}
Run Code Online (Sandbox Code Playgroud)
搜索栏实现:
struct SearchBar: UIViewRepresentable {
@Binding var text: String
var onSearchButtonClicked: (() -> Void)? = nil
class Coordinator: NSObject, UISearchBarDelegate {
let control: SearchBar
init(_ control: SearchBar) {
self.control = control
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
control.text = searchText
}
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
control.onSearchButtonClicked?()
}
}
func makeCoordinator() -> Coordinator {
return Coordinator(self)
}
func makeUIView(context: UIViewRepresentableContext<SearchBar>) -> UISearchBar {
let searchBar = UISearchBar(frame: .zero)
searchBar.delegate = context.coordinator
return searchBar
}
func updateUIView(_ uiView: UISearchBar, context: UIViewRepresentableContext<SearchBar>) {
uiView.text = text
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
4466 次 |
| 最近记录: |