SwiftUI — поиск в заголовке списка

Я пытаюсь воссоздать то, что все знают из UITableView с помощью SwiftUI: простое поле поиска в заголовке табличного представления:

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()
                }

    }
}

Однако я не уверен, что это лучший способ сделать это, потому что:

  1. Заголовок не скрывается при прокрутке вниз, как вы знаете из UITableView.
  2. SearchField не похож на поле поиска, которое мы знаем и любим.

Кто-нибудь нашел хороший подход? Я не хочу возвращаться к UITableView.

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
16
0
13 421
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Ответ принят как подходящий

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.

Выглядит так:

Спасибо, использование UISearchBar, вероятно, лучшая идея.

Daniel 15.06.2019 17:50

@Daniel, пока SwiftUI не станет лучше :)

Matteo Pacini 15.06.2019 18:05

Да, я уверен, что Apple многое запланировала со SwiftUI в будущем.

Daniel 15.06.2019 19:40

У меня это приводит к сбою компилятора (попробовал на игровой площадке в Мохаве, может быть, это как-то связано?).

cargath 20.06.2019 08:55

@cargath У меня сработало в Мохаве. Какую ошибку вы получили?

iComputerfreak 26.06.2019 21:17

Я получаю сообщение об ошибке в инициализации координатора «Невозможно назначить свойство:« $ text »неизменен». Я использую бета-версию 4.

Rob 19.07.2019 20:37

@Rob, то же самое здесь, в бета-версии 4, кто-нибудь знает, где я могу найти документацию о любых изменениях, внесенных в эти вещи?

gujci 24.07.2019 14:16

Сделать Координатором: var text: Binding<String>. Затем в init сделать self.text = text. Затем, чтобы изменить текст, сделайте text.value = searchText.

keji 28.07.2019 23:38

Можно получше посмотреть, если не выкладывать в шапку. Вместо этого поместите его в VStack с интервалом 0, содержащий панель поиска и список: VStack(alignment: .center, spacing: 0) { SearchBar(text: self.$searchQuery); List { .... Таким образом, вы используете всю ширину, а панель поиска всегда остается вверху.

kvaruni 13.08.2019 17:09

@kvaruni: Также производительность поиска намного лучше, если вы не помещаете панель поиска в заголовок. У меня есть tableView с> 1000 элементов, и решение раздела работает плохо.

Klaus 08.12.2019 21:12

Возможно, отправная точка здесь. Рассмотрите возможность использования 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)""#
    }
}

Screenshot

Эй, было бы интересно, как запускать действие (например, вызывать функцию) для каждого изменения search. Это возможно?

blackjacx 01.08.2019 19:39

@blackjacx Я ищу то же решение. Есть у тебя какой-то?

Evana 03.09.2019 07:32

@blackjacx Просто добавьте решение для этого.

Florent Morin 11.09.2019 11:22

@FlorentMorin Должно быть, я что-то упускаю, но я не вижу ZStack в вашем коде?

koen 17.10.2020 13:27

Я реализовал свой проект выбора страны 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

Другие вопросы по теме