【问题标题】:Rotary Knob with SwiftUI带有 SwiftUI 的旋钮
【发布时间】:2021-06-12 09:06:16
【问题描述】:

所以我尝试使用旋钮复制正常的 SwiftUI 滑块功能。我已将 UI 编码并运行,当前连接到标准 SwiftUI 滑块以旋转它。

现在我需要添加其余的滑块功能(即$value、range、stride)和触摸功能(即上下左右拖动时旋钮旋转)。老实说,我迷失了做到这一点的最佳方式。非常感谢任何帮助。

下面是主文件,该项目可以在这里找到Github Slider Project

//
//  FKKnob.swift
//
//  Created by Brent Brinkley on 3/7/21.
//

import SwiftUI

struct FKKnob: View {
    // Set the color for outer ring and inner dash
    let color: Color
    
    // Minimum appearance value:
    let circleMin: CGFloat = 0.0
    
    // Maximum appearance value
    let circleMax: CGFloat = 0.9
    
    // Because our circle is missing a chunk of degrees we have to account
    // for this adjustment
    let circOffsetAmnt: CGFloat = 1 / 0.09
    
    // Offset needed to align knob properly
    let knobOffset: Angle = .degrees(110)
    
    // calculate the our circle's mid point
    var cirMidPoint: CGFloat {
        0.4 * circOffsetAmnt
    }
    
    // User modfiable control value
    @State var value: CGFloat = 0.0
    
    var body: some View {
        
        VStack {
            ZStack {
                
                // MARK: - Knob with dashline
                
                Knob(color: color)
                    .rotationEffect(
                        // Currently controlled by slider
                        .degrees(max(0, Double(360 * value )))
                    )
                    .gesture(DragGesture(minimumDistance: 0)
                                .onChanged({ value  in
                                    
                                    // Need help here setting amount based on x and y touch drag
                                    
                                }))
                
                // MARK: - Greyed Out Ring
                
                Circle()
                    .trim(from: circleMin, to: circleMax)
                    .stroke(Color.gray ,style: StrokeStyle(lineWidth: 6, lineCap: .round, dash: [0.5,8], dashPhase: 20))
                    .frame(width: 100, height: 100)
                
                // MARK: - Colored ring inidicating change
                
                Circle()
                    .trim(from: circleMin, to: value)
                    .stroke(color ,style: StrokeStyle(lineWidth: 6, lineCap: .round, dash: [0.5,8], dashPhase: 20))
                    .frame(width: 100, height: 100)
                
            }
            .rotationEffect(knobOffset)
            
            Text("\(value * circOffsetAmnt, specifier: "%.0f")")
            
            Slider(value: $value, in: circleMin...circleMax)
                .frame(width: 300)
                .accentColor(.orange)
        }
    }
}

struct DashedCircle_Previews: PreviewProvider {
    static var previews: some View {
        FKKnob(color: Color.orange)
    }
}

【问题讨论】:

    标签: ios swift user-interface swiftui slider


    【解决方案1】:

    这是我在一个项目中使用的版本。

    当拖动开始时,它会设置一个初始值(存储在startDragValue 中)。这是因为您总是希望值的修改基于您开始时的旋钮值。

    然后,我决定只更改基于 y 轴的值。理由是:可以根据从 x1, y1 到 x2, y2 的绝对距离进行更改,但是在尝试获取负值时会遇到问题。例如,右上象限会整体增加可能是有道理的——但左上象限会导致正变化(因为 y 轴变化)还是负变化(因为 x轴)?

    如果你决定走 x,y 变化的路线,这个方法仍然会让你做好准备。

    struct ContentView: View {
        @State var value : Double = 0.0
        @State private var startDragValue : Double = -1.0
        var body: some View {
            Text("Knob \(value)")
                .gesture(DragGesture(minimumDistance: 0)
                .onEnded({ _ in
                    startDragValue = -1.0
                })
                .onChanged { dragValue in
                    let diff =  dragValue.startLocation.y - dragValue.location.y
                    if startDragValue == -1 {
                        startDragValue = value
                    }
                    let newValue = startDragValue + Double(diff)
                    value = newValue < 0 ? 0 : newValue > 100 ? 100 : newValue
                })
        }
    }
    

    我的滑块基于控件上下 100pt 的值,但您显然也可以根据自己的喜好更改这些值。

    就范围而言,我建议始终让旋钮从 0.0 变为 1.0,然后再对值进行插值。

    【讨论】:

    • 感谢您,这对推动这一进程绝对有帮助。我现在正在玩它。
    • 你的问题@jnpdx。我已经完成了这项工作,但是当您再次点击旋钮时,它会自动重置为其初始起始位置。我们怎样才能避免这种情况?
    • @BrentBrinkley 我刚刚复制并粘贴了我编写的代码,这对我来说并没有发生。您确定是我的代码在执行此操作,而不是您所做的修改或您放入的情况的函数吗?
    • 发现问题@jnpdx 这是我所做的编辑。
    【解决方案2】:

    我发现 SwiftUI RotationGesture() 可以完美地解决这个问题,因为它允许直观地旋转旋钮,而没有上面答案中列出的缺点。

    唯一独特的缺点是它可能用户已经习惯了单指旋钮操作(尤其是在 iPhone 上),如果您的设备很小,单指拖动手势可能是一种选择应用中的旋钮很难用两根手指按下。

    话虽如此,您始终可以附加多个手势并同时获得。无论如何,感谢@jnpdx 提供了一些关于从您使用 startRotation 中断的位置开始的未来手势的信息,不用多说:

    import SwiftUI
    
    
    struct KnobView: View {
        @State private var rotation: Double = 0.0
        @State private var startRotation: Double = 0.0
        
        var body: some View {
            Knob()
                .rotationEffect(.degrees(rotation))
                .gesture(
                    RotationGesture()
                        .onChanged({ angle in
                            if startRotation == 0 {
                                startRotation = rotation
                            }
                            rotation = startRotation + angle.degrees
                        })
                        .onEnded({ _ in
                            startRotation = 0
                        })
                )
        }
    }
    
    // convience Knob to checkout this Stack Overflow answer
    // that can be later replaced with your own
    struct Knob: View {
        var body: some View {
            RoundedRectangle(cornerRadius: 12)
                .frame(width: 200, height: 200)
        }
    }
    
    struct KnobView_Previews: PreviewProvider {
        static var previews: some View {
            KnobView()
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2020-11-11
      • 1970-01-01
      • 2012-09-04
      • 2020-07-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多