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

如何用SwiftUI复刻macOS Feedback.app的工具栏UI?

复刻macOS Feedback/Mail风格的SwiftUI工具栏问题与解决方案

需求说明

  • 内容栏(Content)和详情栏(Detail)拥有各自独立的工具栏
  • 面板内容滚动至工具栏后方时,工具栏呈现模糊背景效果
  • 仅当内容滚动至工具栏后方时,工具栏才显示模糊背景及底部分隔线
  • 内容栏和详情栏的工具栏样式不得延伸至侧边栏区域

当前实现问题

已通过NavigationSplitView实现独立工具栏,但以下需求未解决:

  • 无法实现内容滚动时工具栏背景的动态切换(模糊/透明)
  • 使用.toolbarBackground(...)虽能添加模糊效果,但会导致工具栏背景永久显示,且样式延伸至侧边栏

相关代码

原ContentView

struct ContentView: View {
    var body: some View {
        NavigationSplitView(
            sidebar: { self.toolbarView(name: "Sidebar", showDivider: false) },
            content: { self.toolbarView(name: "Content") },
            detail: { self.toolbarView(name: "Detail", showDivider: false) }
        )
    }
    
    func toolbarView(name: String, showDivider: Bool = true) -> some View {
        VStack(spacing: 0.0) {
            if showDivider {
                Divider()
            }
            List { Text(name) }
                .toolbar {
                    ToolbarItemGroup {
                        VStack(alignment: .leading) {
                            Text(name).font(.headline)
                            Text("Subtitle").font(.subheadline).foregroundStyle(.secondary)
                        }
                        Spacer()
                        Text(name)
                    }
                }
        }
    }
}

原App入口

@main
struct NavigationSplitViewTestApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
        .windowToolbarStyle(.unified)
        .windowStyle(.hiddenTitleBar)
        .windowResizability(.contentMinSize)
    }
}

原生控件实现方案

通过调整NavigationSplitView的配置及工具栏背景设置,可完全依赖系统原生行为实现需求:

修改后的ContentView

struct ContentView: View {
    var body: some View {
        NavigationSplitView(
            sidebar: {
                // 侧边栏移除工具栏,避免样式延伸
                List {
                    Text("Sidebar Item")
                }
            },
            content: { toolbarView(name: "Content") },
            detail: { toolbarView(name: "Detail") }
        )
    }
    
    func toolbarView(name: String) -> some View {
        List {
            // 添加足够多内容以便测试滚动效果
            ForEach(1...50, id: \.self) { index in
                Text("\(name) Item \(index)")
            }
        }
        .toolbar {
            ToolbarItemGroup {
                VStack(alignment: .leading) {
                    Text(name).font(.headline)
                    Text("Subtitle").font(.subheadline).foregroundStyle(.secondary)
                }
                Spacer()
                Text(name)
            }
        }
        // 关键设置:让系统自动根据滚动状态切换工具栏背景
        .toolbarBackground(.automatic, for: .windowToolbar)
    }
}

修改说明

  1. 移除侧边栏工具栏:直接给侧边栏设置独立的List,不添加工具栏,确保工具栏样式不会覆盖到侧边栏区域。
  2. 启用动态工具栏背景:通过.toolbarBackground(.automatic, for: .windowToolbar)让系统原生处理背景逻辑:
    • 当列表内容在顶部时,工具栏透明,内容直接显示在后方
    • 当内容滚动至工具栏下方时,自动显示模糊背景及底部分隔线
  3. 添加可滚动内容:生成多条列表项,方便测试滚动时的背景切换效果

窗口配置保持

原App入口的窗口设置无需修改,.unified工具栏样式配合.hiddenTitleBar可完美匹配Feedback/Mail的外观风格。

效果验证

  • 内容栏和详情栏各自拥有独立工具栏,互不干扰
  • 滚动列表时,工具栏背景会动态切换:顶部透明、滚动后显示模糊效果及分隔线
  • 侧边栏无工具栏,样式完全独立

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 14:35:03