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

如何在SwiftUI中实现旋转式matchedGeometryEffect?

实现旋转式MatchedGeometryEffect布局切换

需求说明

  • 切换HStack/VStack布局时,实现沿最短路径的旋转过渡
  • 过渡要求:
    • 文本始终保持直立,仅沿弧线移动
    • 灰色背景以红色圆形为中心旋转,而非视图自身中心
    • 背景色在过渡全程保持一致,无淡入淡出

原始代码

struct MyView: View {
    
    @Namespace private var animationNamespace
    @State private var isVertical: Bool = false
    
    var body: some View {
        VStack {
            
            Spacer()
            
            ZStack {
                
                if isVertical {
                    VStack {
                        ForEach(0...10, id: \.self) { index in
                            Rectangle()
                                .frame(width: 20.0, height: 20.0)
                                .foregroundColor((index == 0) ? Color.red : Color.black)
                                .overlay(Text(String(describing: index)).foregroundColor(.white))
                                .matchedGeometryEffect(id: "\(index)", in: animationNamespace)
                                .transition(.scale(scale: 1))
                                
                        }
                    }
                    
                } else {
                    HStack {
                        ForEach(0...10, id: \.self) { index in
                            Rectangle()
                                .frame(width: 20.0, height: 20.0)
                                .foregroundColor((index == 0) ? Color.red : Color.black)
                                .overlay(Text(String(describing: index)).foregroundColor(.white))
                                .matchedGeometryEffect(id: "\(index)", in: animationNamespace)
                                .transition(.scale(scale: 1))

                        }
                    }
                }
            }
            .bold()
            .padding()
            .background(Color.gray)
            .cornerRadius(10)
            .animation(.easeInOut(duration: 2.0), value: isVertical)
            
            Spacer()
            
            Button("Toggle Layout") {
                isVertical.toggle()
            }
        }
        .padding()
    }
}

解决方案

核心修改思路

  1. 分离背景层与内容层,避免背景随布局切换产生淡入淡出
  2. 以红色圆形为锚点设置容器旋转,实现中心旋转效果
  3. 给文本添加反向旋转,抵消容器旋转,保证文本始终直立
  4. 保留matchedGeometryEffect确保视图位置过渡平滑

修改后代码

struct MyView: View {
    @Namespace private var animationNamespace
    @State private var isVertical: Bool = false
    
    // 自动沿最短路径计算旋转角度(90°/-270°)
    private var rotationAngle: Angle {
        isVertical ? .degrees(90) : .degrees(0)
    }
    
    var body: some View {
        VStack {
            Spacer()
            
            ZStack {
                // 固定灰色背景,不随布局切换重建
                Color.gray
                    .cornerRadius(10)
                    .padding()
                
                // 内容容器:应用旋转动画,锚点指向红色圆形中心
                ZStack {
                    if isVertical {
                        VStack(spacing: 0) {
                            contentViews
                        }
                    } else {
                        HStack(spacing: 0) {
                            contentViews
                        }
                    }
                }
                // 锚点对应HStack中红色圆形的中心位置(总宽度220,红色圆心x坐标10,相对比例1/22)
                .rotationEffect(rotationAngle, anchor: .init(x: 1/22, y: 0.5))
                .animation(.easeInOut(duration: 2.0), value: isVertical)
            }
            .bold()
            
            Spacer()
            
            Button("Toggle Layout") {
                isVertical.toggle()
            }
        }
        .padding()
    }
    
    // 提取重复视图,简化代码
    private var contentViews: some View {
        ForEach(0...10, id: \.self) { index in
            Rectangle()
                .frame(width: 20.0, height: 20.0)
                .foregroundColor(index == 0 ? .red : .black)
                .overlay(
                    Text("\(index)")
                        .foregroundColor(.white)
                        // 反向旋转抵消容器旋转,保持文本直立
                        .rotationEffect(-rotationAngle)
                )
                .matchedGeometryEffect(id: "\(index)", in: animationNamespace)
        }
    }
}

关键细节说明

  • 锚点精准定位:anchor: .init(x: 1/22, y: 0.5)是根据HStack总宽度(20*11)和红色圆形中心坐标计算出的相对比例,确保旋转中心完全对齐红色圆形
  • 文本直立逻辑:文本层添加与容器方向相反的旋转角度,彻底抵消容器旋转带来的文本倾斜
  • 背景固化处理:将灰色背景从动态切换的ZStack中移出,作为独立固定层,彻底解决背景淡入淡出问题

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 02:05:20