DEV Community

GoyesDev
GoyesDev

Posted on

[SUI] Barra de búsqueda

Un NavigationStack puede incluir una barra de búsqueda, para lo cual se debe aplicar el siguiente modificador:

struct Person: Identifiable {
  let id = UUID()
  let name: String
}
Enter fullscreen mode Exit fullscreen mode
private let people: [Person] = [
  .init(name: "David Goyes"),
  .init(name: "Midoriya Izuku"),
  .init(name: "Tanjiro Kamado"),
  .init(name: "David Beckham"),
]
Enter fullscreen mode Exit fullscreen mode
struct ContentView: View {

  @State private var searchText: String = ""

  private var filteredPeople: [Person] {
    if searchText.isEmpty {
      people
    } else {
      people.filter { $0.name.localizedStandardContains(searchText) }
    }
  }

  var body: some View {
    NavigationStack {
      List(filteredPeople) { person in
        Text(person.name)
      }
      .navigationTitle("Personas")
      .searchable(text: $searchText, prompt: "¿A quién busca?")
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

En iOS 26, POR DEFECTO la barra de búsqueda queda en la parte inferior de la pantalla porque tiene SearchFieldPlacement en automatic, que en iOS, MacOS y iPadOS sería lo mismo que toolbar. Cuando la barra se vuelve el "first responder", queda encima del teclado cuando este se presenta.

Notar que para que funcionase el ejemplo anterior, filteredPeople se hizo una variable computada y no almacenada. De lo contrario, habría sido necesario usar una aproximación basada en onChange(of:initial:_:), teniendo en cuenta que se debe marcar initial=true para que se cargue la información en filteredPeople en el arranque.

struct ContentView: View {

  @State private var searchText: String = ""

  @State private var filteredPeople: [Person] = []

  var body: some View {
    NavigationStack {
      List(filteredPeople) { person in
        Text(person.name)
      }
      .navigationTitle("Personas")
      .searchable(text: $searchText, prompt: "¿A quién busca?")
      .onChange(of: searchText, initial: true) {
        filterPeople()
      }
    }
  }

  private func filterPeople() {
    filteredPeople = if searchText.isEmpty {
      people
    } else {
      people.filter { $0.name.localizedStandardContains(searchText) }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Visibilidad de la barra de búsqueda

Cuando la barra de búsqueda usa la ubicación por defecto (i.e. automatic) o toolbar, siempre será visible. Si hacemos que aparezca en la barra de navegación con navigationBarDrawer, entonces la barra de búsqueda desaparecerá cuando desplacemos la lista hacia arriba, y volverá a aparecer cuando la desplacemos hacia abajo.

Para que la barra de búsqueda siempre sea visible en la barra de navegación, se puede usar navigationBarDrawer(displayMode:), pasando always como argumento.

struct ContentView: View {

  @State private var searchText: String = ""

  private var filteredPeople: [Person] {
    if searchText.isEmpty {
      people
    } else {
      people.filter { $0.name.localizedStandardContains(searchText) }
    }
  }

  var body: some View {
    NavigationStack {
      List(filteredPeople) { person in
        Text(person.name)
      }
      .navigationTitle("Personas")
      .searchable(text: $searchText, placement: .navigationBarDrawer(displayMode: .always), prompt: "¿A quién busca?")
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Activando la barra de búsqueda de forma programática

Se puede fijar/quitar el foco sobre una barra de búsqueda de forma programática a través del modificador searchFocused(_:) usando un Binding de tipo Bool con el "property-wrapper" FocusState.

En el siguiente ejemplo, el botón "Buscar" del toolbar, pone isFocused en true, lo que enfoca la barra de búsqueda y muestra el teclado.

struct ContentView: View {

  @State private var searchText: String = ""
  // ⚠️ Notar el uso de @FocusState
  @FocusState private var isFocused: Bool

  private var filteredPeople: [Person] {
    if searchText.isEmpty {
      people
    } else {
      people.filter { $0.name.localizedStandardContains(searchText) }
    }
  }

  var body: some View {
    NavigationStack {
      List(filteredPeople) { person in
        Text(person.name)
      }
      .navigationTitle("Personas")
      .toolbar(content: {
        ToolbarItem(placement: .topBarTrailing) {
          Button("Buscar", systemImage: "magnifyingglass") {
            // ⚠️ Aquí se cambia isFocused para centrar el foco
            // en la barra de búsqueda
            isFocused = true
          }
        }
      })
      .searchable(text: $searchText, placement: .navigationBarDrawer(displayMode: .automatic), prompt: "¿A quién busca?")
      // ⚠️ por medio de searchFocused se puede cambiar el foco
      .searchFocused($isFocused)
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)