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

iOS 15 SwiftUI中isSearching导航时自动取消的问题求助

问题

在iOS 15的SwiftUI视图中实现了搜索栏逻辑:激活搜索时显示搜索列表,未激活时显示常规内容视图。但现在遇到问题:从搜索列表触发导航链接跳转后,isSearching会自动变为false,界面切回常规内容视图。期望跳转后保持搜索状态,返回时仍能看到搜索结果,想知道如何控制isSearching的变化逻辑。

演示效果:
搜索跳转后状态丢失演示

复现代码:

import SwiftUI

struct ContentView: View {
    @ObservedObject var viewModel: ContentView.ViewModel
    
    var body: some View {
        NavigationView {
            VStack {
                ContentViewWrapper(viewModel: viewModel)
            }
            .navigationTitle("Searchable")
            .navigationBarTitleDisplayMode(.inline)
            .searchable(text: $viewModel.searchString, placement: .navigationBarDrawer(displayMode: .always), prompt: "Search")
            .navigationViewStyle(.stack)
            .edgesIgnoringSafeArea(.top)
        }
    }
}

// MARK: View model for the content view

extension ContentView {
    class ViewModel: ObservableObject {
        @Published var isShowingDestinationScreen = false
        @Published var isSearching = false
        @Published var searchString = ""
        
        func buttonTapped() {
            if !isShowingDestinationScreen {
                isShowingDestinationScreen = true
            }
        }
        
        func isSearchingHasChanged(newValue: Bool) {
            if isSearching != newValue {
                isSearching = newValue
            }
        }
    }
}

// MARK: Wrapper for the content view so it can be used with the searchable API

struct ContentViewWrapper: View {
    @ObservedObject var viewModel: ContentView.ViewModel
    @Environment(\.isSearching) var isSearching
    
    var body: some View {
        VStack {
            if viewModel.isSearching {
                NavigationLink(
                    isActive: $viewModel.isShowingDestinationScreen,
                    destination: {
                        DestinationView()
                            .navigationTitle("Destination")
                    }, label: {
                        EmptyView()
                    }
                )
                
                SearchList() {
                    viewModel.buttonTapped()
                }
            } else {
                ContentViewMenu()
            }
        }
        .onChange(of: isSearching) { newValue in
            viewModel.isSearchingHasChanged(newValue: newValue)
        }
    }
}

// MARK: Just three simple screens below

struct ContentViewMenu: View {
    var body: some View {
        Text("Content View Menu")
    }
}

struct SearchList: View {
    var destinationButtonTapped: () -> Void
    
    var body: some View {
        VStack {
            Text("Search list")
            Button("Go to destination") {
                destinationButtonTapped()
            }
        }
    }
}

struct DestinationView: View {
    var body: some View {
        Text("Destination")
    }
}

解决方案

系统默认的\.isSearching环境变量在导航跳转时会自动置为false,这是SwiftUI的内置行为。要保持搜索状态,需要自定义维护搜索激活状态,不直接依赖系统的isSearching,而是结合搜索栏的编辑回调和搜索文本状态来控制:

关键修改步骤

  • 在ViewModel中添加自定义的搜索激活状态变量,替代原有的系统依赖逻辑
  • 通过searchable的onEditingChanged和onSubmit回调,手动控制自定义状态的切换
  • 仅当用户主动清空搜索文本并退出搜索时,才关闭自定义搜索状态

修改后的完整代码

import SwiftUI

struct ContentView: View {
    @ObservedObject var viewModel: ViewModel
    
    var body: some View {
        NavigationView {
            VStack {
                ContentViewWrapper(viewModel: viewModel)
            }
            .navigationTitle("Searchable")
            .navigationBarTitleDisplayMode(.inline)
            .searchable(
                text: $viewModel.searchString,
                placement: .navigationBarDrawer(displayMode: .always),
                prompt: "Search"
            )
            // 监听搜索栏编辑状态变化
            .onEditingChanged { isEditing in
                if isEditing {
                    viewModel.isSearchActive = true
                } else {
                    // 仅当搜索文本为空时,才关闭搜索状态
                    if viewModel.searchString.isEmpty {
                        viewModel.isSearchActive = false
                    }
                }
            }
            // 处理搜索提交事件
            .onSubmit(of: .search) {
                viewModel.isSearchActive = true
            }
            .navigationViewStyle(.stack)
            .edgesIgnoringSafeArea(.top)
        }
    }
}

extension ContentView {
    class ViewModel: ObservableObject {
        @Published var isShowingDestinationScreen = false
        // 自定义搜索激活状态,替代原isSearching
        @Published var isSearchActive = false
        @Published var searchString = ""
        
        func buttonTapped() {
            isShowingDestinationScreen = true
        }
    }
}

struct ContentViewWrapper: View {
    @ObservedObject var viewModel: ContentView.ViewModel
    
    var body: some View {
        VStack {
            // 结合自定义状态和搜索文本判断,确保状态不丢失
            if viewModel.isSearchActive || !viewModel.searchString.isEmpty {
                NavigationLink(
                    isActive: $viewModel.isShowingDestinationScreen,
                    destination: {
                        DestinationView()
                            .navigationTitle("Destination")
                    }, label: {
                        EmptyView()
                    }
                )
                
                SearchList() {
                    viewModel.buttonTapped()
                }
            } else {
                ContentViewMenu()
            }
        }
    }
}

struct ContentViewMenu: View {
    var body: some View {
        Text("Content View Menu")
    }
}

struct SearchList: View {
    var destinationButtonTapped: () -> Void
    
    var body: some View {
        VStack {
            Text("Search list")
            Button("Go to destination") {
                destinationButtonTapped()
            }
        }
    }
}

struct DestinationView: View {
    var body: some View {
        Text("Destination")
    }
}

效果说明

  • 用isSearchActive自定义变量控制视图切换,不再受系统\.isSearching的自动重置影响
  • 在onEditingChanged中,仅当用户退出搜索且搜索文本为空时,才关闭自定义搜索状态
  • 结合searchString是否为空的判断,确保即使搜索栏失去焦点但有文本时,仍保持搜索列表显示
  • 导航跳转后,自定义状态和搜索文本会被保留,返回时直接回到搜索列表界面

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 00:09:24