#mvvm #swiftui #searchbar
#mvvm #swiftui #панель поиска
Вопрос:
В проекте я вызываю API электронных книг Google для составления списка книг. По умолчанию в нем перечислены книги о Гарри Поттере. Помимо этого у меня есть строка поиска для поиска книги по другой теме, такой как «java». И это работает нормально, когда я начинаю писать в поле поиска, моя модель просмотра обновляет массив книг в модели. Однако это вообще не обновляет мой взгляд. Я привел все приведенные ниже коды.
Модель:
import Foundation
struct BookModel {
var books: [Book] = []
struct Book: Identifiable {
var id: String
var title: String
var authors: String
var desc: String
var imurl: String
var url: String
}
}
ViewModel:
import Foundation
import SwiftyJSON
class BookViewModel: ObservableObject {
@Published var model = BookModel()
init(searchText: String) {
let url = "https://www.googleapis.com/books/v1/volumes?q=(searchText)"
print(url)
let session = URLSession(configuration: .default)
session.dataTask(with: URL(string: url)!) { (resp, _, error) in
if error != nil {
print(error?.localizedDescription ?? "Error")
return
}
let json = try! JSON(data: resp!)
let items = json["items"].array!
for item in items {
let id = item["id"].stringValue
let title = item["volumeInfo"]["title"].stringValue
let authors = item["volumeInfo"]["authors"].array ?? []
var author: String = ""
for name in authors {
author = "(name.stringValue)"
}
let description = item["volumeInfo"]["description"].stringValue
let imurl = item["volumeInfo"]["imageLinks"]["thumbnail"].stringValue
let webReaderLink = item["volumeInfo"]["previewLink"].stringValue
print(title)
DispatchQueue.main.async {
self.model.books.append(BookModel.Book(id: id, title: title, authors: author, desc: description, imurl: imurl, url: webReaderLink))
}
}
}
.resume()
// For testing
for i in model.books {
print(i.title)
}
}
//MARK:- Access to the model
var books: [BookModel.Book] {
model.books
}
}
contentView:
import SwiftUI
struct ContentView: View {
@ObservedObject var viewModel = BookViewModel(searchText: "harry potter")
var body: some View {
CustomNavigationView(view: AnyView(Home(viewModel: viewModel)), placeHolder: "Search", largeTitle: true, title: "Books") { (text) in
if text != "" {
BookViewModel(searchText: text.lowercased().replacingOccurrences(of: " ", with: " "))
}
} onCancel: {
BookViewModel(searchText: "harry potter")
}
.edgesIgnoringSafeArea(.all)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Главная:
import SwiftUI
import SDWebImageSwiftUI
struct Home: View {
@ObservedObject var viewModel: BookViewModel
@State private var show: Bool = false
@State var previewURL = ""
var body: some View {
List {
ForEach(viewModel.books) { book in
HStack {
if book.imurl != "" {
WebImage(url: URL(string: book.imurl))
.resizable()
.frame(width: 120, height: 170)
} else {
Image(systemName: "character.book.closed.fill")
.font(.system(size: 60))
.frame(width: 120, height: 170)
}
VStack(alignment: .leading, spacing: 10) {
Text(book.title)
.fontWeight(.bold)
Text(book.authors)
Text(book.desc)
.font(.system(size: 13))
.lineLimit(4)
.multilineTextAlignment(.leading)
}
}
.onTapGesture {
self.previewURL = book.url
show.toggle()
}
}
}
.sheet(isPresented: $show) {
NavigationView {
WebView(url: $previewURL)
.navigationBarTitle("Book Preview")
}
}
}
}
CustomNavigationView:
import SwiftUI
struct CustomNavigationView: UIViewControllerRepresentable {
func makeCoordinator() -> Coordinator {
return CustomNavigationView.Coordinator(parent: self)
}
var view: AnyView
//onSearch and onCancel Closures
var onSearch: (String) -> ()
var onCancel: () -> ()
var title: String
var largeTitle: Bool
var placeHolder: String
init(view: AnyView, placeHolder: String? = "Search", largeTitle: Bool? = false, title: String, onSearch: @escaping (String) -> (), onCancel: @escaping () -> ()) {
self.title = title
self.largeTitle = largeTitle!
self.placeHolder = placeHolder!
self.view = view
self.onSearch = onSearch
self.onCancel = onCancel
}
func makeUIViewController(context: Context) -> UINavigationController {
let childView = UIHostingController(rootView: view)
let controller = UINavigationController(rootViewController: childView)
controller.navigationBar.topItem?.title = title
controller.navigationBar.prefersLargeTitles = largeTitle
let searchController = UISearchController()
searchController.searchBar.placeholder = placeHolder
searchController.obscuresBackgroundDuringPresentation = false
searchController.searchBar.delegate = context.coordinator
controller.navigationBar.topItem?.hidesSearchBarWhenScrolling = false
controller.navigationBar.topItem?.searchController = searchController
return controller
}
func updateUIViewController(_ uiViewController: UINavigationController, context: Context) {
uiViewController.navigationBar.topItem?.title = title
uiViewController.navigationBar.topItem?.searchController?.searchBar.placeholder = placeHolder
uiViewController.navigationBar.prefersLargeTitles = largeTitle
}
//Search Bar Delegate
class Coordinator: NSObject, UISearchBarDelegate {
var parent: CustomNavigationView
init(parent: CustomNavigationView) {
self.parent = parent
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
self.parent.onSearch(searchText)
}
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
self.parent.onCancel()
}
}
}
Полная ссылка на проект:
https://github.com/shawkathSrijon/eBook-Reader.git
Ответ №1:
Возможно, вам придется сообщать свои представления о новых изменениях с помощью ObservableObjectPublisher
objectWillChange
.
DispatchQueue.main.async {
self.model.books.append(BookModel.Book(id: id, title: title, authors: author, desc: description, imurl: imurl, url: webReaderLink))
self.objectWillChange.send() // <-- Here
}
Комментарии:
1. Кажется, все то же самое. Понятия не имею, почему это так!