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

如何在开启Reduce Motion时禁用SwiftUI视图过渡动画

SwiftUI适配Reduce Motion模式的过渡动画问题

问题场景

我的App中有ContinueView视图,点击按钮会滑出视野。现在需要为开启Reduce Motion的用户提供支持:不使用滑动动画,直接显示/隐藏视图。已经在ContentView中声明了@Environment(\.accessibilityReduceMotion) var isReducedMotion环境变量,但尝试传nil给transition时报错:'nil' cannot be used in context expecting type 'AnyTransition'。

当前实现代码

ContentView 代码

struct ContentView: View {
    @Environment(\.accessibilityReduceMotion) var isReducedMotion
    @State private var hasRequestedNotifications    = UserDefaults.standard.bool(forKey: "hasRequestedNotifications")
    @State private var isShowingContinueView        = !UserDefaults.standard.bool(forKey: "isLoggedIn")
    
    var body: some View {
        // ... 其他视图代码
        
        if isShowingContinueView {
            ContinueView(isShowingContinueView: $isShowingContinueView)
                .transition(.asymmetric(
                    insertion: AnyTransition.move(edge: .trailing).combined(with: .opacity),
                    removal: AnyTransition.move(edge: .leading).combined(with: .opacity)
                ))
        } else if !hasRequestedNotifications {
            AllowNotificationsView(hasRequestedNotifications: $hasRequestedNotifications)
                .transition(.asymmetric(
                    insertion: AnyTransition.move(edge: .trailing).combined(with: .opacity),
                    removal: AnyTransition.move(edge: .leading).combined(with: .opacity)
                ))
        }
        
        // ... 其他视图代码
    }
}

ContinueView 按钮逻辑

Button("Continue") {
    withAnimation {
        self.isShowingContinueView.wrappedValue = false
    }
}

错误尝试方案

尝试通过isReducedMotion控制过渡动画,但因传入nil报错:

ContinueView(isShowingContinueView: $isShowingContinueView)
    .transition(isReducedMotion ? nil : .asymmetric(
        insertion: AnyTransition.move(edge: .trailing).combined(with: .opacity),
        removal: AnyTransition.move(edge: .leading).combined(with: .opacity)
    ))

解决方法

SwiftUI中transition不接受nil,可以用AnyTransition.identity替代——这是一个无任何动画效果的过渡,视图会直接显示/隐藏,符合Reduce Motion的需求。

修改后的代码

1. 更新ContentView中的过渡设置

if isShowingContinueView {
    ContinueView(isShowingContinueView: $isShowingContinueView)
        .transition(isReducedMotion ? .identity : .asymmetric(
            insertion: AnyTransition.move(edge: .trailing).combined(with: .opacity),
            removal: AnyTransition.move(edge: .leading).combined(with: .opacity)
        ))
} else if !hasRequestedNotifications {
    AllowNotificationsView(hasRequestedNotifications: $hasRequestedNotifications)
        .transition(isReducedMotion ? .identity : .asymmetric(
            insertion: AnyTransition.move(edge: .trailing).combined(with: .opacity),
            removal: AnyTransition.move(edge: .leading).combined(with: .opacity)
        ))
}

2. 优化ContinueView的按钮动画(可选)

为了彻底适配Reduce Motion,还可以在触发状态变化时,根据isReducedMotion决定是否启用动画:

// 需要先在ContinueView中注入isReducedMotion环境变量
struct ContinueView: View {
    @Environment(\.accessibilityReduceMotion) var isReducedMotion
    @Binding var isShowingContinueView: Bool
    
    var body: some View {
        // ... 视图内容
        Button("Continue") {
            withAnimation(isReducedMotion ? nil : .default) {
                isShowingContinueView = false
            }
        }
    }
}

这样,开启Reduce Motion的用户会看到视图直接显隐,关闭时则保留原有的滑动过渡动画。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 01:46:31