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

SwiftUI中如何让Shape的CGAffineTransform参数自动更新并保持路径可见

问题:如何让CGAffineTransform参数自动适配视图尺寸,保持路径在帧内?

我有一个基于Shape的矩形视图,添加自定义路径时需要用到CGAffineTransform。其中参数a和tx需要在Shape内部计算更新,才能保证最终完整路径可见。目前我只能通过手动滑块调整参数(作为调试工具),不清楚视图尺寸变化时怎么让a和tx自动更新。从演示效果来看,修改a和tx的值可以让路径保持在帧内,但我需要这个过程自动完成。请问如何实现a和tx的自动更新,让整个路径始终在帧内?

Part 1:
调整前路径超出紫色容器的效果

Part 2:
调整参数后路径适配紫色容器的效果

原始代码

import SwiftUI

struct ContentView: View {

    @State private var a: Double = 0.8
    @State private var b: Double = 0
    @State private var c: Double = -0.17
    @State private var d: Double = 1
    @State private var tx: Double = 100
    @State private var ty: Double = 0

    var body: some View {
        
        FinalShape(a: a, b: b, c: c, d: d, tx: tx, ty: ty)
            .strokeBorder(style: StrokeStyle(lineWidth: 5.0, lineCap: .round, lineJoin: .round))
            .background(Color.purple)
            .cornerRadius(5.0)
            .padding()

        VStack {

            HStack {
                Text("a:")
                Slider(value: $a, in: -1...1)
            }
            
            HStack {
                Text("b:")
                Slider(value: $b, in: -1...1)
            }
            
            HStack {
                Text("c:")
                Slider(value: $c, in: -1...1)
            }
            
            HStack {
                Text("d:")
                Slider(value: $d, in: -1...1)
            }
            
            HStack {
                Text("tx:")
                Slider(value: $tx, in: -200...200)
            }
            
            HStack {
                Text("ty:")
                Slider(value: $ty, in: -200...200)
            }
 
        }
        .padding()

        HStack {
            VStack(alignment: .leading) {
                Text("a:" + String(describing: a))
                Text("b:" + String(describing: b))
                Text("c:" + String(describing: c))
                Text("d:" + String(describing: d))
                Text("tx:" + String(describing: tx))
                Text("ty:" + String(describing: ty))
            }
            
            Spacer()
        }
        .padding()

    }
}


struct FinalShape: InsettableShape {

    var a: Double
    var b: Double
    var c: Double
    var d: Double
    var tx: Double
    var ty: Double
    
    var insetAmount: CGFloat = CGFloat.zero

    func path(in rect: CGRect) -> Path {
        return Path { path in

            let transform = CGAffineTransform(a: a, b: b, c: c, d: d, tx: tx, ty: ty)

            path.addPath(RecShape(insetAmount: insetAmount).path(in: CGRect(origin: CGPoint(x: rect.minX, y: rect.minY), size: rect.size)), transform: transform)

        }
    }
    
    func inset(by amount: CGFloat) -> some InsettableShape {
        var myShape: Self = self
        myShape.insetAmount += amount
        return myShape
    }
}


struct RecShape: InsettableShape {
    var insetAmount: CGFloat = CGFloat.zero
    func path(in rect: CGRect) -> Path {
        return Path { path in
            path.addRect(CGRect(origin: CGPoint(x: rect.minX + insetAmount, y: rect.minY + insetAmount), size: CGSize(width: rect.width - 2*insetAmount, height: rect.height - 2*insetAmount)))
        }
    }
    func inset(by amount: CGFloat) -> some InsettableShape {
        var myShape: Self = self
        myShape.insetAmount += amount
        return myShape
    }
}
解决方案

核心思路是:把a和tx的计算逻辑移到FinalShape的path(in:)方法里,每次视图尺寸变化时,自动根据当前容器尺寸计算出适配的参数值,不再依赖外部手动调整。

1. 修改FinalShape结构

移除a和tx的外部传入参数,在内部动态计算:

struct FinalShape: InsettableShape {
    // 保留其他需要固定或手动调整的变换参数
    var b: Double = 0
    var c: Double = -0.17
    var d: Double = 1
    var ty: Double = 0
    
    var insetAmount: CGFloat = .zero

    func path(in rect: CGRect) -> Path {
        // 先获取原始矩形的路径和边界
        let originalPath = RecShape(insetAmount: insetAmount).path(in: rect)
        let originalBounds = originalPath.boundingRect
        
        // 先应用除a和tx外的变换,得到临时边界
        let tempTransform = CGAffineTransform(b: b, c: c, d: d, ty: ty)
        let tempBounds = originalPath.applying(tempTransform).boundingRect
        
        // 计算缩放因子a:让变换后的宽度刚好适配容器宽度
        let scaleFactor = rect.width / tempBounds.width
        // 计算平移tx:让变换后的路径水平居中对齐容器
        let translateX = rect.midX - (tempBounds.midX * scaleFactor) - tempBounds.minX * (scaleFactor - 1)
        
        // 组合最终的变换矩阵
        let finalTransform = CGAffineTransform(a: scaleFactor, b: b, c: c, d: d, tx: translateX, ty: ty)
        
        return originalPath.applying(finalTransform)
    }
    
    func inset(by amount: CGFloat) -> some InsettableShape {
        var shape = self
        shape.insetAmount += amount
        return shape
    }
}

2. 简化ContentView

去掉不需要的a和tx相关状态、滑块和文本显示:

struct ContentView: View {
    @State private var b: Double = 0
    @State private var c: Double = -0.17
    @State private var d: Double = 1
    @State private var ty: Double = 0

    var body: some View {
        FinalShape(b: b, c: c, d: d, ty: ty)
            .strokeBorder(style: StrokeStyle(lineWidth: 5.0, lineCap: .round, lineJoin: .round))
            .background(Color.purple)
            .cornerRadius(5.0)
            .padding()
        
        // 保留其他参数的调试滑块(如果需要)
        VStack {
            HStack {
                Text("b:")
                Slider(value: $b, in: -1...1)
            }
            
            HStack {
                Text("c:")
                Slider(value: $c, in: -1...1)
            }
            
            HStack {
                Text("d:")
                Slider(value: $d, in: -1...1)
            }
            
            HStack {
                Text("ty:")
                Slider(value: $ty, in: -200...200)
            }
        }
        .padding()
    }
}

关键逻辑说明

  • 自动缩放(参数a):通过对比临时变换后的路径宽度和容器宽度,计算出刚好能让路径适配容器的缩放比例。
  • 自动平移(参数tx):计算出平移距离,让变换后的路径在容器内水平居中,避免超出边界。
  • 动态适配:每次视图尺寸变化时,path(in:)方法都会被系统调用,自动重新计算参数,保证路径始终适配当前容器。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 06:25:19