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

SwiftUI:导航标题与搜索栏间距过大问题排查

解决SwiftUI模态视图导航标题与搜索栏间距过大问题

问题概述

开发复杂应用时制作了示例App,遇到以下布局异常:

  • ContentView按钮打开带导航标题、搜索栏的Main View(模态视图),该视图布局正常;
  • 点击Main View按钮进入Sub View后,导航标题与搜索栏之间出现过大间距,怀疑是双层NavigationStack导致。

异常截图:
Sub View导航标题与搜索栏间距过大

问题分析

核心问题确实是双层NavigationStack嵌套,再加上几个额外的布局设置,共同导致了间距异常:

  1. 根视图SampleAppApp中的NavigationStack完全多余——模态视图MainView自身已经包含独立的导航栈,双层嵌套会让SwiftUI的导航栏布局计算逻辑混乱;
  2. MainView在sheet中被添加了.padding(.top, 15.0),人为给模态视图顶部加了额外空间;
  3. 导航栏ToolbarItem里的.padding(.bottom, 15.0),让标题下方多出不必要的间距;
  4. SubView的searchable修饰符嵌套在子视图里,和导航栏的布局层级冲突。

修复方案

按以下修改即可解决问题:

  • 删掉根视图SampleAppApp中的NavigationStack;
  • 移除MainView在sheet里的.padding(.top, 15.0);
  • 去掉两个ToolbarItem中的.padding(.bottom, 15.0);
  • 将SubView的searchable修饰符移到视图根层级,确保和导航栏正确关联;
  • (可选)如果需要隐藏默认返回按钮,手动添加自定义返回按钮,避免布局偏移。

修改后完整代码

import SwiftUI
import Foundation
import Combine

@main
struct SampleAppApp: App {
    var body: some Scene {
        WindowGroup {
            // 移除多余的根NavigationStack
            ContentView()
        }
    }
}

struct ContentView: View {
    @State private var showModal: Bool = false
    var body: some View {
        VStack {
            Button("Click ME", action: { self.showModal.toggle() })
        }
        .sheet(isPresented: $showModal) {
            ZStack {
                Color(red: 0.95, green: 0.95, blue: 0.97)
                // 移除.top padding
                MainView(isPresented: $showModal)
            }
        }
    }
}

// MARK: Main View
struct MainView: View {
    @State private var searchText = ""
    let searchTextPublisher = PassthroughSubject<String, Never>()

    var isPresented: Binding<Bool>
    @State private var showSubView: Bool = false

    var body: some View {
        NavigationStack {
            ZStack {
                Color(red: 0.95, green: 0.95, blue: 0.97).edgesIgnoringSafeArea(.all)
                VStack {
                    VStack {
                        Button("Tap to open sub-view", action: {
                            self.showSubView.toggle()
                        }).padding(.top, 20.0)
                    }
                    .navigationDestination(isPresented: $showSubView) {
                        SubView()
                    }
                    
                    MainViewSearchResultListAndNavTitle()
                    .searchable(
                        text: $searchText,
                        placement: .navigationBarDrawer(displayMode: .always),
                        prompt: "search"
                    )
                    .onChange(of: searchText) { newText in
                        searchTextPublisher.send(newText)
                    }
                    Spacer()
                }
                .frame(maxWidth: .infinity, maxHeight: .infinity)
                .background(Color(red: 0.95, green: 0.95, blue: 0.97))
            }
        }
        .presentationDetents([.large])
        .interactiveDismissDisabled(true)
        .presentationBackgroundInteraction(.enabled)
    }
}

struct MainViewSearchResultListAndNavTitle: View {
    var body: some View {
        VStack {
            // 搜索结果列表
        }
        .toolbar(content: {
            ToolbarItem(placement: .navigationBarLeading) {
                Text("Main View")
                    .bold()
                    .font(.largeTitle)
                    // 移除.bottom padding
            }
        })
        .toolbarBackground(Color(red: 0.95, green: 0.95, blue: 0.97), for: .navigationBar)
        .toolbarBackground(.visible, for: .navigationBar)
    }
}

// MARK: Sub View
struct SubView: View {
    var body: some View {
        NavigationStack {
            ZStack {
                Color(red: 0.95, green: 0.95, blue: 0.97).edgesIgnoringSafeArea(.all)
                VStack {
                    SubViewSearchResultsListAndNavTitle()
                    Spacer()
                }
                .frame(maxWidth: .infinity, maxHeight: .infinity)
                .background(Color(red: 0.95, green: 0.95, blue: 0.97))
            }
            // 将searchable提升到根层级
            .searchable(
                text: .constant(""),
                placement: .navigationBarDrawer(displayMode: .always),
                prompt: "search"
            )
        }
    }
}

private struct SubViewSearchResultsListAndNavTitle: View {
    var body: some View {
        VStack {
            // 子视图搜索结果列表
        }
        .toolbar(content: {
            ToolbarItem(placement: .principal) {
                Text("Sub view").bold()
                    .font(.title)
                    // 移除.bottom padding
            }
            // 手动添加自定义返回按钮
            ToolbarItem(placement: .navigationBarLeading) {
                Button(action: {
                    if let navController = UIApplication.shared.windows.first?.rootViewController as? UINavigationController {
                        navController.popViewController(animated: true)
                    }
                }) {
                    Image(systemName: "chevron.left")
                        .foregroundColor(.black)
                }
            }
        })
        .toolbarBackground(Color(red: 0.95, green: 0.95, blue: 0.97), for: .navigationBar)
        .toolbarBackground(.visible, for: .navigationBar)
        .navigationBarBackButtonHidden(true)
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 19:45:06