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

如何在matchedGeometryEffect中实现旋转过渡效果?

问题描述

垂直或水平移动Rectangle时,matchedGeometryEffect可正常工作,但在垂直与水平位置模式间切换时,我需要添加旋转过渡效果却无法实现,目前使用的scale过渡无法解决该问题。如何在垂直与水平位置切换时实现旋转过渡效果?

效果演示

当前代码
struct ContentView: View {
    
    @StateObject var myClass: MyClass = MyClass.shared
    
    var body: some View {
        
        VStack {
            
            MyView()
                .frame(width: 300.0, height: 300.0)
                .background(Color.red).cornerRadius(20.0)
            
            HStack {
                
                Button("top") {
                    myClass.position = .top
                }
                
                Spacer()
                
                Button("bottom") {
                    myClass.position = .bottom
                }
                
                Spacer()
                
                Button("left") {
                    myClass.position = .left
                }
                
                Spacer()
                
                Button("right") {
                    myClass.position = .right
                }
                
            }
            .frame(width: 300.0)
            
        }
        .padding()
    }
}

enum Position: String {
    case top, bottom, left, right
}

class MyClass: ObservableObject {
    static let shared: MyClass = MyClass()
    @Published var position: Position = Position.bottom
}

struct MyView: View {
    
    @StateObject var myClass: MyClass = MyClass.shared
    @Namespace var matchedGeometryEffectOfRectangle
    
    var body: some View {
        VStack {
            
            switch myClass.position {
            case .top:
                
                VStack {
                    Color.blue.frame(height: 50.0).cornerRadius(10.0)
                        .matchedGeometryEffect(id: "Rectangle", in: matchedGeometryEffectOfRectangle)
                        .transition(.scale(scale: 1, anchor: .center))
                    Spacer()
                }
                
            case .bottom:
                
                VStack {
                    Spacer()
                    Color.blue.frame(height: 50.0).cornerRadius(10.0)
                        .matchedGeometryEffect(id: "Rectangle", in: matchedGeometryEffectOfRectangle)
                        .transition(.scale(scale: 1, anchor: .center))
                }
                
            case .left:
                
                HStack {
                    Color.blue.frame(width: 50.0).cornerRadius(10.0)
                        .matchedGeometryEffect(id: "Rectangle", in: matchedGeometryEffectOfRectangle)
                        .transition(.scale(scale: 1, anchor: .center))
                    Spacer()
                }
                
            case .right:
                
                HStack {
                    Spacer()
                    Color.blue.frame(width: 50.0).cornerRadius(10.0)
                        .matchedGeometryEffect(id: "Rectangle", in: matchedGeometryEffectOfRectangle)
                        .transition(.scale(scale: 1, anchor: .center))
                }
                
            }
            
        }
        .padding()
        .animation(.default, value: myClass.position)
    }
}
解决方案

要实现垂直/水平切换时的旋转过渡,需解决视图尺寸突变、旋转与位置动画协同的问题,具体实现如下:

修改核心逻辑

  1. 统一视图尺寸:给蓝色矩形设置固定50x50的宽高,避免垂直/水平模式切换时的尺寸突变。
  2. 动态旋转角度:根据当前位置给矩形添加对应旋转角度,水平位置时旋转90度,模拟横向显示效果。
  3. 简化布局结构:用ZStack替代多组VStack/HStack,通过偏移控制矩形位置,配合matchedGeometryEffect实现平滑过渡。
  4. 统一动画控制:移除局部transition,通过全局动画同步位置和旋转的过渡效果。

修改后的MyView代码

struct MyView: View {
    
    @StateObject var myClass: MyClass = MyClass.shared
    @Namespace var matchedGeometryEffectOfRectangle
    
    // 根据位置计算旋转角度
    private var rotationAngle: Angle {
        switch myClass.position {
        case .top, .bottom:
            return .degrees(0)
        case .left:
            return .degrees(-90)
        case .right:
            return .degrees(90)
        }
    }
    
    // 根据位置计算偏移,适配父容器位置
    private var offset: CGSize {
        switch myClass.position {
        case .top:
            return CGSize(width: 0, height: -100)
        case .bottom:
            return CGSize(width: 0, height: 100)
        case .left:
            return CGSize(width: -100, height: 0)
        case .right:
            return CGSize(width: 100, height: 0)
        }
    }
    
    var body: some View {
        ZStack {
            Color.blue.frame(width: 50, height: 50)
                .cornerRadius(10)
                .matchedGeometryEffect(id: "Rectangle", in: matchedGeometryEffectOfRectangle)
                .rotationEffect(rotationAngle)
                .offset(offset)
        }
        .padding()
        .animation(.easeInOut(duration: 0.5), value: myClass.position)
    }
}

效果说明

修改后,垂直/水平位置切换时,蓝色矩形会同时完成位置移动和旋转的平滑过渡,解决了原方案中无法实现旋转效果的问题。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 03:52:14