You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

iOS 16下如何让.searchable修饰符的scope bar始终显示?

在iOS 16中让SwiftUI的Search Scope Bar始终显示的方案

问题背景

在SwiftUI列表中使用.searchable修饰符时,需要实现scope bar始终显示的效果(即使用户未触发搜索操作)。iOS 16.4新增的带activation参数的.searchScopes修饰符仅支持在搜索触发时显示scope bar,且不兼容iOS 16早期版本;而UIKit的UISearchBar提供了手动控制选项可实现该需求,但需要在SwiftUI中适配。同时希望保留搜索栏点击时的原生动画效果,因此排除直接在VStack中放置分段选择器的方案。

原始代码示例

enum SearchScope: String, CaseIterable {
    case inbox, message
}

struct ContentView: View {
    @State private var searchText = ""
    @State private var searchScope: SearchScope = .inbox

    var body: some View {
        NavigationStack {
            List {
                ForEach(1..<30) { num in
                    Text(String(num))
                }
            }
            .searchable(text: $searchText)
            .searchScopes($searchScope, activation: .onSearchPresentation) {
                ForEach(SearchScope.allCases, id: \.self) { scope in
                    Text(scope.rawValue.capitalized)
                }
            }
            .navigationTitle("Search")
        }
    }
}

解决方案:使用SwiftUI-Introspect库

由于原生SwiftUI在iOS 16上无直接API支持始终显示scope bar,可通过SwiftUI-Introspect库获取底层UIKit组件,手动控制UISearchBar的显示状态,同时保留原生动画效果。

步骤1:添加依赖

通过Swift Package Manager引入SwiftUI-Introspect库,确保版本支持iOS 16。

步骤2:修改视图代码

通过Introspect获取UISearchBar实例,设置showsScopeBar为true,并同步SwiftUI与UIKit的scope选中状态:

import SwiftUI
import Introspect

enum SearchScope: String, CaseIterable {
    case inbox, message
}

struct ContentView: View {
    @State private var searchText = ""
    @State private var searchScope: SearchScope = .inbox
    private var scopeTitles: [String] { SearchScope.allCases.map { $0.rawValue.capitalized } }

    var body: some View {
        NavigationStack {
            List {
                ForEach(1..<30) { num in
                    Text(String(num))
                }
            }
            .searchable(text: $searchText)
            .searchScopes($searchScope, activation: .onSearchPresentation) {
                ForEach(SearchScope.allCases, id: \.self) { scope in
                    Text(scope.rawValue.capitalized)
                }
            }
            .navigationTitle("Search")
            // 直接获取底层UISearchBar实例
            .introspectSearchBar { searchBar in
                // 强制始终显示scope bar
                searchBar.showsScopeBar = true
                // 同步scope标题与SwiftUI定义一致
                searchBar.scopeButtonTitles = scopeTitles
                // 初始选中状态同步
                searchBar.selectedScopeButtonIndex = SearchScope.allCases.firstIndex(of: searchScope) ?? 0
                // 监听scope切换,同步到SwiftUI状态
                searchBar.addTarget(self, action: #selector(scopeSelectionChanged), for: .valueChanged)
            }
        }
    }

    @objc private func scopeSelectionChanged(_ searchBar: UISearchBar) {
        guard let selectedIndex = searchBar.selectedScopeButtonIndex,
              selectedIndex < SearchScope.allCases.count else { return }
        searchScope = SearchScope.allCases[selectedIndex]
    }
}

关键说明

  • 动画保留:该方案直接控制原生UISearchBar,因此完全保留搜索栏展开/收起的动画效果。
  • 状态同步:通过监听UISearchBar的valueChanged事件,将UIKit的选中状态同步到SwiftUI的@State变量中,确保双向一致性。
  • 兼容性:适配iOS 16全版本,无需依赖iOS 16.4的新API。

内容的提问来源于stack exchange,提问作者code24

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 14:27:12