SwiftUI:MagnificationGesture以CGPoint为中心放大

uz75evzq  于 2023-11-16  发布在  Swift
关注(0)|答案(1)|浏览(160)

我有一段代码,可以让你使用放大手势放大和缩小一个带有渐变的圆圈。如果我把手指放在屏幕中间并放大,这很好,但是如果我把手指放在屏幕边缘并做放大手势,我希望它放大我手指之间的点。现在,它仍然以屏幕的中心作为放大的中心进行放大。
我如何修改我的代码以允许用户在手指放置之间的CGPoint上居中?

struct ContentView: View {
    @GestureState var magnificationState = MagnificationState.inactive
    @State var viewMagnificationState = CGFloat(1.0)
    
    var magnificationScale: CGFloat {
        return viewMagnificationState * magnificationState.scale
    }
    
    var body: some View {
        let gradient = Gradient(colors: [.red, .yellow, .green, .blue, .purple, .red, .yellow, .green, .blue, .purple, .red, .yellow, .green, .blue, .purple])
        
        let magnificationGesture = MagnificationGesture()
            .updating($magnificationState) { value, state, transaction in
                state = .zooming(scale: value)
            }.onEnded { value in
                self.viewMagnificationState *= value
            }
        
        
        Circle()
            .fill(
                RadialGradient(gradient: gradient, center: .center, startRadius: 50, endRadius: 2000)
            )
            .frame(width: 2000, height: 2000)
            .scaleEffect(magnificationScale)
            .gesture(magnificationGesture)
    }
}

enum MagnificationState {
    case inactive
    case zooming(scale: CGFloat)
    
    var scale: CGFloat {
        switch self {
        case .zooming(let scale):
            return scale
        default:
            return CGFloat(1.0)
        }
    }
}

字符串

xzv2uavs

xzv2uavs1#

SwiftUI仍然不支持以锚点为中心进行缩放。作为解决方案,我们可以在透明的UIView上使用UIPinchGestureRecognizerUIViewRepresentable。使用锚点进行缩放本质上是缩放和平移。我们可以将其应用于具有transformEffect视图修饰符的视图。此视图修饰符将CGAffineTransform应用于视图。
以下扩展简化了围绕锚点的缩放:

extension CGAffineTransform {
    func scaled(by scale: CGFloat, with anchor: CGPoint) -> CGAffineTransform {
        self
            .translatedBy(x: anchor.x, y: anchor.y)
            .scaledBy(x: scale, y: scale)
            .translatedBy(x: -anchor.x, y: -anchor.y)
    }
}

字符串
GestureTransformView是一个绑定到transform的UIViewRepresentable。我们将在UIPinchGestureRecognizer的委托中更新transform。

struct GestureTransformView: UIViewRepresentable {
    @Binding var transform: CGAffineTransform

    func makeUIView(context: Context) -> UIView {
        let view = UIView()
        
        let zoomRecognizer = UIPinchGestureRecognizer(
            target: context.coordinator,
            action: #selector(Coordinator.zoom(_:)))
        
        zoomRecognizer.delegate = context.coordinator
        view.addGestureRecognizer(zoomRecognizer)
        context.coordinator.zoomRecognizer = zoomRecognizer
        
        return view
    }
    
    func updateUIView(_ uiView: UIView, context: Context) {
    }
    
    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }
}

extension GestureTransformView {
    class Coordinator: NSObject, UIGestureRecognizerDelegate {
        var parent: GestureTransformView
        var zoomRecognizer: UIPinchGestureRecognizer?

        var startTransform: CGAffineTransform = .identity
        var pivot: CGPoint = .zero
        
        init(_ parent: GestureTransformView){
            self.parent = parent
        }
        
        func setGestureStart(_ gesture: UIGestureRecognizer) {
            startTransform = parent.transform
            pivot = gesture.location(in: gesture.view)
        }
        
        @objc func zoom(_ gesture: UIPinchGestureRecognizer) {
            switch gesture.state {
            case .began:
                setGestureStart(gesture)
                break
            case .changed:
                applyZoom()
                break
            case .cancelled:
                fallthrough
            case .ended:
                applyZoom()
                startTransform = parent.transform
                zoomRecognizer?.scale = 1
            default:
                break
            }
        }
        
        func applyZoom() {
            let gestureScale = zoomRecognizer?.scale ?? 1
            parent.transform = startTransform
                .scaled(by: gestureScale, with: pivot)
        }
    }
}


这就是如何使用GestureTransformView。请注意transformEffect应用于Stack,而不是Circle。这可以确保(以前的)转换也正确应用于叠加。

struct ContentView: View {
    @State var transform: CGAffineTransform = .identity
    
    var body: some View {
        let gradient = Gradient(colors: [.red, .yellow, .green, .blue, .purple,
                                         .red, .yellow, .green, .blue, .purple,
                                         .red, .yellow, .green, .blue, .purple])
        ZStack {
            Circle()
                .fill(
                    RadialGradient(gradient   : gradient,
                                   center     : .center,
                                   startRadius: 50,
                                   endRadius  : 2000)
                )
                .frame(width: 2000, height: 2000)
                .overlay {
                    GestureTransformView(transform: $transform)
                }
        }   .transformEffect(transform)
    }
}

相关问题