SwiftUI macOS开发如何实现ToolbarItem绝对居中保留前后置项
macOS 三段式固定居中工具栏实现方案
SwiftUI 原生.toolbar的默认布局逻辑不会给.principal位置分配全局居中的布局优先级,才会出现后置项紧贴居中元素、居中元素随前置内容偏移的问题,可通过以下两种方案实现需求:
方案1:原生Toolbar调整(保留系统特性,推荐)
不需要完全废弃原生Toolbar,只要调整布局规则和placement使用方式即可,适配macOS 12+,保留系统工具栏的所有原生交互(自定义排序、全屏自适应、touch bar联动等)。
- 布局规则:
- 前置区域元素统一使用
.navigationplacement,不要混放其他位置 - 居中区域使用
.principalplacement,给容器设置最大宽度撑满可用空间,同时设置最高布局优先级,避免被前后元素挤压偏移 - 后置区域元素统一使用
.primaryAction/.confirmationActionplacement,不要使用.automaticplacement
- 前置区域元素统一使用
- 实现代码:
.toolbar { // 前置(leading)区域 ToolbarItem(placement: .navigation) { HStack(spacing: 12) { // 放所有前置按钮、控件 Button {} label: { Image(systemName: "sidebar.left") } Button {} label: { Image(systemName: "arrow.backward") } Button {} label: { Image(systemName: "arrow.forward") } } } // 居中(centered)区域 ToolbarItem(placement: .principal) { HStack { Spacer() // 放所有居中控件 Button {} label: { Image(systemName: "star.fill") .scaleEffect(1.5) } Spacer() } .frame(maxWidth: .infinity) .layoutPriority(.greatestFiniteMagnitude) } // 后置(trailing)区域 ToolbarItem(placement: .primaryAction) { HStack(spacing: 12) { // 放所有后置按钮、控件 Button {} label: { Image(systemName: "square.and.arrow.up") } Button {} label: { Image(systemName: "star") } } } }
方案2:自定义标题栏工具栏(布局100%可控)
如果需要兼容更低系统版本,或者对居中精度要求极高(完全不受前后元素动态变化影响),可以放弃系统原生Toolbar,自定义标题栏视图实现布局。
- 实现逻辑:
- 隐藏系统原生标题栏/工具栏
- 顶部自定义工具栏高度和系统工具栏一致,使用系统栏材质背景
- 前后置区域分别左右对齐,居中元素用overlay锚定在工具栏绝对中心位置,完全不受前后内容宽度影响
- 实现代码:
// 窗口修饰符:隐藏原生标题栏 .windowStyle(.hiddenTitleBar) // 页面根视图 var body: some View { VStack(spacing: 0) { // 自定义工具栏 HStack(spacing: 0) { // 前置区域 HStack(spacing: 12) { Button {} label: { Image(systemName: "sidebar.left") } Button {} label: { Image(systemName: "arrow.backward") } Button {} label: { Image(systemName: "arrow.forward") } } .padding(.horizontal, 8) Spacer() // 后置区域 HStack(spacing: 12) { Button {} label: { Image(systemName: "square.and.arrow.up") } Button {} label: { Image(systemName: "star") } } .padding(.horizontal, 8) } .frame(height: 52) // 匹配系统工具栏默认高度 .background(.bar) // 使用系统半透明栏材质 .overlay(alignment: .center) { // 居中元素直接锚定在父视图中心,完全不受前后元素影响 Button {} label: { Image(systemName: "star.fill") .scaleEffect(1.5) } } // 页面正文内容 YourContentPage() } .ignoresSafeArea(.container, edges: .top) // 让工具栏贴合窗口顶部 }
- 注意点:该方案需要自行实现窗口拖拽、全屏工具栏自动隐藏等系统特性,如果需要支持系统级工具栏自定义排序能力不建议使用。
内容的提问来源于stack exchange,提问作者Yiming Designer
相关产品推荐
相关产品推荐

