SwiftUI自定义SearchBar

2个月前 161次点击 来自 iOS

原文链接:How To Create A Search Bar With SwiftUI

文章表述步骤非常清楚,每一步解决什么问题,思路清晰,值得推荐入门学习:

1.需求定义

myFruits列表内容添加一个搜索功能

struct ContentView: View {
     
    let myFruits = [
        "Apple 🍏", "Banana 🍌", "Blueberry 🫐", "Strawberry 🍓", "Avocado 🥑", "Cherries 🍒", "Mango 🥭", "Watermelon 🍉", "Grapes 🍇", "Lemon 🍋",
    ]
    var body: some View {
        NavigationView {
            List {
                ForEach(myFruits, id: \.self) { fruit in
                    Text(fruit)
                }
            }
            .listStyle(GroupedListStyle())
            .navigationTitle("MyFruits")
        }
    }
 }

2.添加自定义SearchBar

视图结果如下

首先在Assets自定义颜色LightGray

更改布局,添加搜索框:

struct ContentView: View {
    let myFruits = [
        "Apple 🍏", "Banana 🍌", "Blueberry 🫐", "Strawberry 🍓", "Avocado 🥑", "Cherries 🍒", "Mango 🥭", "Watermelon 🍉", "Grapes 🍇", "Lemon 🍋",
    ]
    @State var searchText = ""

    var body: some View {
        NavigationView {
            VStack(alignment: .leading) {
                ZStack {
                    HStack {
                        Image(systemName: "magnifyingglass")
                        TextField("Search ..", text: $searchText)
                    }.foregroundColor(.gray)
                        .padding(.leading, 13)

                    Rectangle()
                        .foregroundColor(Color("LightGray"))
                }
                .frame(height: 40)
                .cornerRadius(13)
                .padding()

                List {
                    ForEach(myFruits, id: \.self) { fruit in
                        Text(fruit)
                    }
                }
                .listStyle(GroupedListStyle())
                .navigationTitle("MyFruits")
            }
        }
    }
}

最后效果如下

3.提取SearchBar

稍微有点SwiftUI基础的同学都知道,SearchBar可以作为一个单独的子视图提取出来:

struct SearchBar: View {
     @Binding var searchText: String
		 
     var body: some View {
         ZStack {
             Rectangle()
                 .foregroundColor(Color("LightGray"))
             HStack {
                 Image(systemName: "magnifyingglass")
                 TextField("Search ..", text: $searchText)
             }
             .foregroundColor(.gray)
             .padding(.leading, 13)
         }
             .frame(height: 40)
             .cornerRadius(13)
             .padding()
     }
 }

此时父视图ContentView可直接引入SearchBar:

VStack(alignment: .leading) {
     SearchBar(searchText: $searchText)
     //...
 }

4.搜索状态

父视图ContentView与子视图SearchBar需要一个状态码来联动更新:

ContentView添加:

@State var searching = false

SearchBar(searchText: $searchText, searching: $searching)

SearchBar添加:

@Binding var searching: Bool

使用searching属性更新SearchBarTextField编辑状态:

 TextField("Search ..", text: $searchText) { startedEditing in
     if startedEditing {
         withAnimation {
             searching = true
         }
     }
 } onCommit: {
     withAnimation {
         searching = false
     }
 }

ContentView使用.navigationTitle.实时更新状态,同时使用.toolbar提供一个取消搜索按钮:

VStack(alignment: .leading) {
     //...
         .navigationTitle(searching ? "Searching" : "MyFruits")
         .toolbar {
             if searching {
                 Button("Cancel") {
                     searchText = ""
                     withAnimation {
                         searching = false
                     }
                 }
             }
         }
 }

效果如下:

稍微有点工程经验的同学都应该发现了键盘的隐藏出现了问题,点击取消按钮的同时键盘应该同时隐藏:
添加扩展:

extension UIApplication {
      func dismissKeyboard() {
          sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
      }
  }

响应事件:

Button("Cancel") {
     searchText = ""
     withAnimation {
         searching = false
         UIApplication.shared.dismissKeyboard()
     }
 }

同时,在我们滑动列表的时候(甚至是点击搜索框以外区域),也应该隐藏键盘:

List(myFruits, id: \.self) { fruit in
     //...
 }
     //...
	.gesture(DragGesture()
							 .onChanged({ _ in
									 UIApplication.shared.dismissKeyboard()
							 })
	 )

5.过滤结果

根据搜索框输入内容实时显示结果:

List {
     ForEach(myFruits.filter({ (fruit: String) -> Bool in
         return fruit.hasPrefix(searchText) || searchText == ""
     }), id: \.self) { fruit in
         Text(fruit)
     }
 }

Card image cap
开发者雷

尘世间一个小小的开发者,每天增加一些无聊的知识,就不会无聊了

要加油~~~

技术文档 >> 系列应用 >>
热推应用
Let'sLearnSwift
学习Swift的入门教程
PyPie
Python is as good as Pie
标签