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

SwiftUI自定义ActionSheet子视图无法随父视图动画问题

解决SwiftUI自定义ActionSheet显示动画失效问题

问题核心

自定义ActionSheet通过底部滑入转场显示时,若选项列表被动态包裹进ScrollView,会直接跳转到最终位置,无滑入动画;但关闭动画正常。移除ScrollView后动画恢复,却无法满足小设备上选项列表的滚动需求。

原因分析

SwiftUI的动画依赖稳定的视图结构。当通过wrappedInScrollView(when:)动态切换「普通视图」和ScrollView容器时,显示阶段的视图结构发生突变,SwiftUI无法识别这是同一个逻辑视图的状态变化,导致转场动画中断。而关闭时是同一结构的视图消失,因此动画不受影响。

解决方案

方案1:固定视图结构,控制滚动状态(推荐)

不要动态添加/移除ScrollView,始终保留ScrollView容器,通过条件控制是否允许滚动。这样视图结构稳定,动画能正常执行:

修改wrappedInScrollView(when:)实现:

fileprivate extension View {
    func wrappedInScrollView(when condition: Bool) -> some View {
        ScrollView {
            self
        }
        .scrollDisabled(!condition) // 仅当需要时允许滚动
        .clipped() // 防止内容超出容器范围
    }
}

无论optionsDontFit是否为true,视图始终是ScrollView结构,既保证转场动画正常,又满足小设备的滚动需求。

方案2:给动态视图添加固定标识

通过id(_:)给选项列表设置固定标识,让SwiftUI识别动态切换的视图是同一个逻辑实体:

var sheet: some View {
    VStack {
        HStack { /* some stuff here */ }
        
        list(of: options)
            .readSize($optionsSize)
            .wrappedInScrollView(when: optionsDontFit)
            .id("action-sheet-options") // 固定唯一标识
            .transition(.opacity)
    }
    .readSize($sheetSize)
}

方案3:明确触发显示动画

在设置isDismissed = false时,用withAnimation包裹,确保动画被明确触发:

.onAppear {
    withAnimation {
        isDismissed = false
    }
}

验证说明

方案1从根源上避免了视图结构突变,是最稳定的解决方案。若方案2或3单独使用无效,可结合方案1一起尝试。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 04:07:51