SwiftUI在使用.toolbarRole(.editor) 且禁用Liquid Glass时,返回按钮的左箭头在带有弹跳动画时出现故障

移动开发 2026-07-08

我有一个全新的SwiftUI应用,为了避免Liquid Glass的重新设计,我在Info.plist中添加 UIDesignRequiresCompatibility = YES

在一个推送到 NavigationStack 的详情屏幕上,我应用 .toolbarRole(.editor),以使返回按钮只显示箭头(而不在旁边显示上一屏的标题文本)。使用该修饰符时,返回按钮箭头在推送过渡期间会出现glitchy动画:到达详情屏幕时,箭头向下移动,再往上回弹,最终稳定在左上角的正确位置。

这里 有一个简短视频,展示了这个glitchy的返回按钮动画。

我观察到 / 尝试的做法

  • 这个glitch仅在禁用Liquid Glass的情况下才会发生。如果把 UIDesignRequiresCompatibility = NO(让应用使用新的Liquid Glass设计),返回按钮的过渡是完全平滑的。
  • 移除 .toolbarRole(.editor) 会让glitch消失,但这样返回按钮就会显示上一屏的标题文本,这正是我想要避免的。
  • 如果父视图的标题是inline而不是large时,glitch也不会发生:在 ParentView 设置 .navigationBarTitleDisplayMode(.inline)(而不是 .large)会得到平滑的返回按钮过渡。
  • 它可在 Xcode PreviewSimulator 以及实体设备上重现。
struct ParentView: View {
    @State private var path = NavigationPath()

    var body: some View {
        NavigationStack(path: $path) {
            Text("HI")
                .navigationTitle("My Items")
                .navigationDestination(for: String.self) { route in
                    switch route {
                    case "View2":
                        DetailView()
                    default:
                        EmptyView()
                    }
                }
                .navigationBarTitleDisplayMode(.large) 
                .toolbar {
                    ToolbarItem(placement: .topBarTrailing) {
                        Button {
                            path.append("View2")
                        } label: {
                            Image(systemName: "plus")
                        }
                    }
                }
        }
    }
}

struct DetailView: View {
    var body: some View {
        Text("Detail View")
            .navigationTitle("Detail Screen")
            .toolbarRole(.editor)
    }
}

环境

  • Xcode 26.5
  • 最低部署目标:iOS 26
  • UIDesignRequiresCompatibility = YES(Liquid Glass禁用)。将其设为 NO 会使问题消失。
  • 可在 Xcode PreviewSimulator 以及真实设备上重现。

问题

为何只有在禁用 Liquid GlassUIDesignRequiresCompatibility = YES)且父标题为 .large 的情况下,.toolbarRole(.editor) 才会在推送过渡期间让返回按钮的箭头发生动画/抖动?以及在不触发此动画glitch的前提下,如何保留仅箭头的返回按钮?

解决方案

正如评论中所提及,苹果在WWDC26期间确认Xcode 27将不再支持opt-out(选择退出)。该说法可在 Platforms State of the Union 的0:41:03看到。这意味着,UIDesignRequiresCompatibility 将无法在Xcode 27上工作。

基于App Store提交的当前要求,我预期苹果会要求从2027年 4月起提交的应用必须使用Xcode 27构建。

因此,如果你在短时间内仍无法准备好采用liquid glass,应该考虑寻找其他方式来保留旧的外观。


一般而言,工具栏按钮的外观可以通过应用自定义的 ButtonStyle 来进行自定义。这种方法可以用于任何类型的自定义样式。以下演示了如何使用这种技术来对按钮进行样式化,使其在所有iOS版本上看起来一致,模拟旧的外观:

  • 创建一个 ButtonStyle,它模仿旧的样式,并用于对作为工具栏项添加的按钮进行样式化。类似如下:
struct NavButtonStyle: ButtonStyle {
    let alignment: Alignment

    private var sidePadding: CGFloat {
        if #available(iOS 26.0, *) {
            -4
        } else {
            0
        }
    }

    func makeBody(configuration: Configuration) -> some View {
        configuration.label
            .frame(minWidth: 44, minHeight: 44, alignment: alignment)
            .padding(alignment == .leading ? .leading : .trailing, sidePadding)
            .fontWeight(.semibold)
            .foregroundStyle(.tint)
    }
}
  • 为了方便,你可能想定义一个相应的对 ButtonStyle 的扩展:
extension ButtonStyle where Self == NavButtonStyle {
    static func navButton(alignment: Alignment) -> Self {
        Self(alignment: alignment)
    }
}

对于返回按钮的情况,还需要以下附加步骤:

  • 与其应用 .toolbarRole(.editor),不如通过应用 .navigationBarBackButtonHidden() 来隐藏默认的返回按钮。
  • 在详情视图中,添加环境值 dismiss
  • 将你自定义的返回按钮作为工具栏项添加,放置在 .topBarLeading。点击时应调用 dismiss()

下面是将你的示例更新为使用上述按钮样式和扩展的方法:

struct ParentView: View {
    @State private var path = NavigationPath()

    var body: some View {
        NavigationStack(path: $path) {
            Text("HI")
                .navigationTitle("My Items")
                .navigationDestination(for: String.self) { route in
                    switch route {
                    case "View2":
                        DetailView()
                    default:
                        EmptyView()
                    }
                }
                .navigationBarTitleDisplayMode(.large)
                .toolbar {
                    ToolbarItem(placement: .topBarTrailing) {
                        Button {
                            path.append("View2")
                        } label: {
                            Image(systemName: "plus")
                        }
                        .buttonStyle(.navButton(alignment: .trailing))
                    }
                    // See https://stackoverflow.com/a/79791386/20386264
                    .hideSharedBackgroundIfAvailable()
                }
        }
    }
}

struct DetailView: View {
    @Environment(\.dismiss) private var dismiss

    var body: some View {
        Text("Detail View")
            .navigationTitle("Detail Screen")
//            .toolbarRole(.editor)
            .navigationBarBackButtonHidden()
            .toolbar {
                ToolbarItem(placement: .topBarLeading) {
                    Button("Back", systemImage: "chevron.backward") {
                        dismiss()
                    }
                    .buttonStyle(.navButton(alignment: .leading))
                }
                // See https://stackoverflow.com/a/79791386/20386264
                .hideSharedBackgroundIfAvailable()
            }
    }
}

在iPhone 17模拟器上运行、iOS 26.5时的效果如下:

Animation

在较旧的iOS版本(iOS 26之前)上运行时看起来也非常相似。你也会发现动画没有glitch,因此帖子原先的问题也因此成为副作用得到解决。

不幸的是,看起来在隐藏原生返回按钮时,滑动返回手势似乎不可用,因此这是一个折衷。帖子 Hide navigation bar without losing swipe back gesture in SwiftUI 介绍了一些可能的解决方法。

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章