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

SwiftUI中如何实现UIKit同款日历月份切换滑动动画?

SwiftUI实现日历月份切换的左右滑动Push动画

SwiftUI的.transition()仅在视图被添加到或从视图层级中移除时生效。你当前的代码只是更新文本内容,视图本身并未被替换,所以.slide过渡不会触发。要复现UIKit中的push滑动效果,需要让SwiftUI感知到视图切换,同时匹配左右方向的动画逻辑。

方法一:视图切换+自定义方向过渡

利用ZStack承载月份视图,通过视图身份标记让SwiftUI识别为不同视图,结合方向对应的不对称过渡实现push效果:

// 定义切换方向枚举
enum SlideDirection {
    case left, right
}

struct CalendarMonthView: View {
    @State private var currentMonth = 10
    @State private var slideDirection: SlideDirection = .right
    
    var body: some View {
        VStack {
            HStack {
                Button("<") {
                    withAnimation(.easeInOut(duration: 0.3)) {
                        slideDirection = .left
                        currentMonth -= 1
                    }
                }
                Button(">") {
                    withAnimation(.easeInOut(duration: 0.3)) {
                        slideDirection = .right
                        currentMonth += 1
                    }
                }
            }
            
            ZStack {
                Text(String(currentMonth))
                    .padding()
                    .frame(maxWidth: .infinity, maxHeight: .infinity)
                    .background(Color.mint)
                    .transition(slideTransition(for: slideDirection))
            }
            
            Spacer()
        }
        .font(.title)
    }
    
    // 自定义对应方向的不对称push过渡
    private func slideTransition(for direction: SlideDirection) -> AnyTransition {
        let insertEdge: Edge = direction == .right ? .trailing : .leading
        let removeEdge: Edge = direction == .right ? .leading : .trailing
        
        return .asymmetric(
            insertion: .push(from: insertEdge),
            removal: .push(from: removeEdge)
        )
    }
}

方法二:复用UIKit的CATransition逻辑

如果想直接沿用你在UIKit中的动画配置,可以通过自定义ViewModifier包装CATransition:

enum SlideDirection {
    case left, right
}

struct PushTransitionModifier: ViewModifier {
    let direction: SlideDirection
    
    func body(content: Content) -> some View {
        content
            .overlay(UIViewRepresentable { view in
                let transition = CATransition()
                transition.type = .push
                transition.subtype = direction == .right ? .fromRight : .fromLeft
                transition.duration = 0.3
                transition.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
                view.layer.add(transition, forKey: "pushTransition")
            })
    }
}

extension View {
    func pushTransition(direction: SlideDirection) -> some View {
        modifier(PushTransitionModifier(direction: direction))
    }
}

// 使用示例
struct CalendarMonthView: View {
    @State private var currentMonth = 10
    @State private var slideDirection: SlideDirection = .right
    
    var body: some View {
        VStack {
            HStack {
                Button("<") {
                    slideDirection = .left
                    withAnimation {
                        currentMonth -= 1
                    }
                }
                Button(">") {
                    slideDirection = .right
                    withAnimation {
                        currentMonth += 1
                    }
                }
            }
            
            Text(String(currentMonth))
                .padding()
                .frame(maxWidth: .infinity, maxHeight: .infinity)
                .background(Color.mint)
                .pushTransition(direction: slideDirection)
            
            Spacer()
        }
        .font(.title)
    }
}

核心要点

  • 方法一中的不对称过渡分别定义了视图插入和移除时的动画方向,完全匹配UIKit的push滑动逻辑。
  • 方法二直接复用你原有的CATransition配置,保留了动画的细节参数。
  • 两种方案都需要记录切换方向,确保动画和操作的左右方向一致。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 07:27:04