首页 > 解决方案 > 带有 SwiftUI 的旋钮

问题描述

所以我试图用旋钮复制普通的 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)
    }
}

标签: iosswiftuser-interfaceswiftuislider

解决方案


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

当拖动开始时,它会设置一个初始值(存储在 中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,然后再对值进行插值。


推荐阅读