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

SwiftUI视图位置动画异常:首次跳转与按钮延迟问题

Sheet弹出时背景视图动画异常修复

问题

  • 首次触发Sheet弹出时,背景视图直接跳转到目标位置,无过渡动画
  • Button视图相对Text视图存在明显的动画延迟

问题原因

  1. 首次无动画:初始高度值为0,Sheet弹出时通过异步方法更新高度,此时show的动画已经启动,高度变化滞后导致视图直接跳变
  2. 动画不同步:自定义的Geo类异步传递高度,加上外层动画作用范围的问题,导致子视图动画时序不一致

修复方案1(SwiftUI原生实现)

import SwiftUI

struct ContentView: View {
    @State private var show = false
    @State private var sheetHeight: CGFloat = 0
    
    var body: some View {
        VStack{
            VStack{
                Button("show"){
                    show.toggle()
                }
                .foregroundColor(.black)
                Text("Test")
            }
            .padding()
            .border(Color.black)
            .offset(y: show ? -sheetHeight * 0.5 : 0)
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity)
        .background(Color.green)
        .animation(.easeInOut, value: show)
        .sheet(isPresented: $show) {
            SheetContent(height: $sheetHeight)
                .presentationDetents([.fraction(0.4)])
                .presentationBackgroundInteraction(.enabled(upThrough: .fraction(0.4)))
        }
    }
}

struct SheetContent: View {
    @Binding var height: CGFloat
    
    var body: some View {
        Text("Showed")
            .padding()
            .background(
                GeometryReader { geo in
                    Color.clear
                        .onAppear {
                            // 同步获取Sheet高度,无需异步
                            height = geo.size.height
                        }
                        .onDisappear {
                            height = 0
                        }
                }
            )
    }
}

修复方案2(UIKit辅助实现)

如果原生方案有适配问题,也可以直接通过UIKit获取Sheet尺寸:

import SwiftUI

struct ContentView: View {
    @State private var show = false
    @State private var sheetHeight: CGFloat = 0
    
    var body: some View {
        VStack{
            VStack{
                Button("show"){
                    show.toggle()
                }
                .foregroundColor(.black)
                Text("Test")
            }
            .padding()
            .border(Color.black)
            .offset(y: show ? -sheetHeight * 0.5 : 0)
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity)
        .background(Color.green)
        .animation(.easeInOut, value: show)
        .sheet(isPresented: $show) {
            VStack{
                Text("Showed")
                    .padding()
            }
            .presentationDetents([.fraction(0.4)])
            .presentationBackgroundInteraction(.enabled(upThrough: .fraction(0.4)))
            .onAppear {
                // 通过UIKit获取Sheet视图高度
                guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
                      let sheetVC = windowScene.windows.first?.rootViewController?.presentedViewController as? UIHostingController<AnyView> else {
                    return
                }
                sheetHeight = sheetVC.view.frame.height
            }
            .onDisappear {
                sheetHeight = 0
            }
        }
    }
}

关键修复点

  • 移除异步更新高度的逻辑,确保Sheet弹出时高度值已就绪,让show状态驱动的动画能正常过渡
  • 用@Binding直接传递Sheet高度,替代自定义的ObservableObject,减少状态传递的延迟
  • 统一动画触发源为show,确保Button和Text的动画同步执行

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 22:42:35