Я пытаюсь воссоздать то, что все знают из UITableView с помощью SwiftUI: простое поле поиска в заголовке табличного представления:
Однако в представлении списка в SwiftUI, похоже, даже нет способа добавить верхний или нижний колонтитул. Вы можете установить заголовок с TextField для таких разделов:
@State private var searchQuery: String = ""
var body: some View {
List {
Section(header:
Group{
TextField($searchQuery, placeholder: Text("Search"))
.background(Color.white)
}) {
ListCell()
ListCell()
ListCell()
}
}
}
Однако я не уверен, что это лучший способ сделать это, потому что:
Кто-нибудь нашел хороший подход? Я не хочу возвращаться к UITableView.





Xcode 13 / SwiftUI 3
Теперь вы можете использовать .searchable, чтобы сделать список доступным для поиска!
struct ContentView: View {
@State private var searchQuery: String = ""
var body: some View {
NavigationView {
List {
ForEach(Array(1...100)
.map { "\($0)" }
.filter { searchQuery.isEmpty ? true : $0.contains(searchQuery) }
,id: \.self) { item in
Text(verbatim: item)
}
}
.navigationTitle("Fancy Numbers")
.searchable(text: $searchQuery)
}
}
}
Панель поиска появляется только в том случае, если List встроен в NavigationView.
Xcode 12, SwiftUI 1/2
Вы можете перенести UISearchBar на SwiftUI.
(Подробнее об этом можно узнать из отличного доклада на WWDC 2019 — Интеграция 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() -> Coordinator {
return Coordinator(text: $text)
}
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
}
}
И используйте это так:
struct ContentView: View {
@State private var searchQuery: String = ""
var body: some View {
List {
Section(header: SearchBar(text: self.$searchQuery)) {
ForEach(Array(1...100).filter {
self.searchQuery.isEmpty ?
true :
"\($0)".contains(self.searchQuery)
}, id: \.self) { item in
Text("\(item)")
}
}
}
}
}
Это правильная панель поиска, но она не прячется — я уверен, что в какой-то момент мы сможем сделать это через SwiftUI API.
Выглядит так:
@Daniel, пока SwiftUI не станет лучше :)
Да, я уверен, что Apple многое запланировала со SwiftUI в будущем.
У меня это приводит к сбою компилятора (попробовал на игровой площадке в Мохаве, может быть, это как-то связано?).
@cargath У меня сработало в Мохаве. Какую ошибку вы получили?
Я получаю сообщение об ошибке в инициализации координатора «Невозможно назначить свойство:« $ text »неизменен». Я использую бета-версию 4.
@Rob, то же самое здесь, в бета-версии 4, кто-нибудь знает, где я могу найти документацию о любых изменениях, внесенных в эти вещи?
Сделать Координатором: var text: Binding<String>. Затем в init сделать self.text = text. Затем, чтобы изменить текст, сделайте text.value = searchText.
Можно получше посмотреть, если не выкладывать в шапку. Вместо этого поместите его в VStack с интервалом 0, содержащий панель поиска и список: VStack(alignment: .center, spacing: 0) { SearchBar(text: self.$searchQuery); List { .... Таким образом, вы используете всю ширину, а панель поиска всегда остается вверху.
@kvaruni: Также производительность поиска намного лучше, если вы не помещаете панель поиска в заголовок. У меня есть tableView с> 1000 элементов, и решение раздела работает плохо.
Возможно, отправная точка здесь. Рассмотрите возможность использования ZStack для эффекта исчезновения при прокрутке для прокрутки.
struct ContentView: View {
@State var search: String
var body: some View {
NavigationView {
VStack(alignment: .center, spacing: 0) {
HStack {
Image(systemName: "magnifyingglass")
.padding(.leading, CGFloat(10.0))
TextField("Search", text: $search, onEditingChanged: { active in
print("Editing changed: \(active)")
}, onCommit: {
print("Commited: \(self.search)")
})
.padding(.vertical, CGFloat(4.0))
.padding(.trailing, CGFloat(10.0))
}
.overlay(
RoundedRectangle(cornerRadius: 5.0)
.stroke(Color.secondary, lineWidth: 1.0)
)
.padding()
List {
ForEach(0...100, id: \.self) { e in
Text("Item \(e)")
}
}
}
.navigationBarTitle(title(for: self.search))
}
}
private func title(for value: String?) -> String {
guard let value = value, value.count > 0 else {
return "No search"
}
return #"Searching for "\#(value)""#
}
}
Эй, было бы интересно, как запускать действие (например, вызывать функцию) для каждого изменения search. Это возможно?
@blackjacx Я ищу то же решение. Есть у тебя какой-то?
@blackjacx Просто добавьте решение для этого.
@FlorentMorin Должно быть, я что-то упускаю, но я не вижу ZStack в вашем коде?
Я реализовал свой проект выбора страны Columbus с помощью SwiftUI. Я реализовал собственный издатель CountryListViewModel и связал его с текстовым полем. Таким образом, я могу печатать, искать источник данных, отфильтровывать результаты/отклонять и обновлять табличное представление, когда все операции выполнены. Довольно хорошо работает ?
https://github.com/Blackjacx/Колумбус/дерево/swift-ui/Источник/Классы
2021 — Xcode 13 / SwiftUI 3
Собственное решение SwiftUI:
List {
ForEach(0..<5) {
index in
Text("item \(index)")
}
}
}
.searchable(text: .constant("search_value")) // should be a Binding
Спасибо, использование UISearchBar, вероятно, лучшая идея.