SwiftUI:在最小值和最大值之间更改值的放大手势

SwiftUI: magnification gesture that changes value between min and max

我想用滑块做同样的事情:

@State itemSize: CGFloat = 55.60

....

Text("ItemSize: \(itemSize)")
Slider(value: $itemSize, in: 45...120)

但有放大手势

我为此创建了修改器:

import SwiftUI

@available(OSX 11.0, *)
public extension View {
    func magnificationZoomer(value: Binding<CGFloat>,min: CGFloat, max: CGFloat) -> some View
    {
         self.modifier( MagnificationZoomerMod(minZoom: min, maxZoom: max, zoom: value) )
    }
}

@available(OSX 11.0, *)
public struct MagnificationZoomerMod: ViewModifier {
    let minZoom: CGFloat
    let maxZoom: CGFloat
    @Binding var zoom: CGFloat
    
    public func body (content: Content) -> some View
    {
        content
            .gesture(
                MagnificationGesture()
                    .onChanged() { val in
                        let magnification = (val - 1) * 2.2
                        
                        print("magnification = \(magnification)")
                        
                        zoom = max(min(zoom + magnification, maxZoom), minZoom)
                    }
            )
    }
}

使用示例:

@State itemSize: CGFloat = 55.60

var body: some View {
    HStack {
       VStack {
           Text("ItemSize: \(itemSize)")
           Slider(value: $itemSize, in: 45...120)
       }
    }
    .frame(width: 500, height: 500)
    .magnificationZoomer(value: $itemSize, min: 45, max: 120)
}

但是我在使用这段代码时遇到了一些问题:

  1. 它以一种奇怪的方式滑动值 -- 有时以正确的速度,它不会改变它
  2. 使用一段时间后它完全停止工作

我做错了什么?

它就像魔术一样工作,您可以通过 Slider 或您的手指在 MagnificationGesture 上更改圆的 size/scale , 共同努力, 共同沉沦




import SwiftUI

struct ContentView: View {
    
    let minZoom: CGFloat = 0.5
    let maxZoom: CGFloat = 1.5
    
    @State private var scale: CGFloat = 1.0
    @State private var lastScale: CGFloat = 1.0
    @State private var magnitudeIsActive: Bool = Bool()
    
    var body: some View {
        
        ZStack {
            
            Circle()
                .fill(Color.red)
                .scaleEffect(scale)
                .gesture(magnificationGesture.simultaneously(with: dragGesture)) // <<: Here: adding unneeded dragGesture! on macOS! no need on iOS!
            
            VStack {
                
                Spacer()
                
                Text("scale: " + scale.rounded)
                
                HStack {
                    
                    Button("min") { scale = minZoom; lastScale = scale }
                    
                    Slider(value: Binding.init(get: { () -> CGFloat in return scale },
                                               set: { (newValue) in if !magnitudeIsActive { scale = newValue; lastScale = newValue } }), in: minZoom...maxZoom)

                    Button("max") { scale = maxZoom; lastScale = scale }
                }
                
            }
            
        }
        .padding()
        .compositingGroup()
        .shadow(radius: 10.0)
        .animation(Animation.easeInOut(duration: 0.2), value: scale)

        
    }

    var magnificationGesture: some Gesture {
        
        MagnificationGesture(minimumScaleDelta: 0.0)
            .onChanged { value in
                
                if !magnitudeIsActive { magnitudeIsActive = true }
                
                let magnification = (lastScale + value.magnitude - 1.0)
                
                if (magnification >= minZoom && magnification <= maxZoom) {
                    
                    scale = magnification
                    
                }
                else if (magnification < minZoom) {
                    
                    scale = minZoom
                }
                else if (magnification > maxZoom) {
                    
                    scale = maxZoom
                }
                
            }
            .onEnded { value in
                
                let magnification = (lastScale + value.magnitude - 1.0)
                
                if (magnification >= minZoom && magnification <= maxZoom) {
                    
                    lastScale = magnification
                    
                }
                else if (magnification < minZoom) {
                    
                    lastScale = minZoom
                }
                else if (magnification > maxZoom) {
                    
                    lastScale = maxZoom
                }
                
                scale = lastScale
                magnitudeIsActive = false
                
            }
        
    }
    
    
    var dragGesture: some Gesture { DragGesture(minimumDistance: 0.0) } // <<: Here: this Extra un-needed gesture would keep magnificationGesture alive! And Stop it to get killed in macOS! We do not need this line of code in iOS!
    
}

extension CGFloat { var rounded: String { get { return String(Double(self*100.0).rounded()/100.0) } } }