【问题标题】:How to create custom slider by using SwiftUI?如何使用 SwiftUI 创建自定义滑块?
【发布时间】:2020-02-05 17:46:33
【问题描述】:

我可以使用 SwiftUI 创建一个滑块,但我无法更改滑块的样式,如下图所示(下图)。

问题:我无法在 SwiftUI 中找到任何选项来更改滑块样式。

注意:我只想使用 SwiftUI 创建它。我已经使用“https://github.com/daprice/iOS-Tactile-Slider”在 Swift 中创建了这个滑块

我尝试了以下方法,但这不是解决方案:

1. Slider(value: .constant(0.3)).accentColor(Color.white)

2. Slider(value: $age, in: 18...20, step: 1, minimumValueLabel: Text("18"), maximumValueLabel: Text("20")) { Text("") }

3. Slider(value: $age, in: 18...20, step: 1, minimumValueLabel: Image(systemName: "18.circle"), maximumValueLabel: Image(systemName: "20.circle")) { Text("") }

如何仅使用 SwiftUI 创建具有如图所示样式的滑块?

【问题讨论】:

  • 对不起,首先,我没有正确阅读。我开始研究“如何将参数传递给UIViewRepresentable”。但后来意识到,你想要完整的 swiftUI 东西。所以我想问题是如何从原始视图中提取修饰符信息。
  • 是的。有两件事。首先是使用 SwiftUI 从头开始​​创建 Slider,其次是通过从 UIKit 库中提取某些内容来创建 Slider。两者都是不错的选择,因为我的重点是在 SwiftUI 中设计这个滑块。
  • 第二件事,如果我理解正确的话,包装它是解决方案。这个 sn-p 可能是初学者的东西。 gist.github.com/Gujci/7a7c37ce6a4bc29c498ca3c593bf2b69
  • 另外,accentColor 取决于上下文,因此,如果您创建一个新的 swiftUI 视图并使用 .foregroundColor(.accentColor),它将是您在外层设置的那个。
  • 我也不认为 SwiftUI 颜色可以通过公共 API 转换回 UIKit。

标签: ios swift user-interface swiftui ios13


【解决方案1】:

对我来说,强调颜色取决于上下文以及框架,所以我们不需要处理。

就控制而言,我做了一个非常简单的例子。请不要将此视为解决方案,而应视为入门。

struct CustomView: View {

    @Binding var percentage: Float // or some value binded

    var body: some View {
        GeometryReader { geometry in
            // TODO: - there might be a need for horizontal and vertical alignments
            ZStack(alignment: .leading) {
                Rectangle()
                    .foregroundColor(.gray)
                Rectangle()
                    .foregroundColor(.accentColor)
                    .frame(width: geometry.size.width * CGFloat(self.percentage / 100))
            }
            .cornerRadius(12)
            .gesture(DragGesture(minimumDistance: 0)
                .onChanged({ value in
                    // TODO: - maybe use other logic here
                    self.percentage = min(max(0, Float(value.location.x / geometry.size.width * 100)), 100)
                }))
        }
    }
}

你可以像这样使用它

    @State var percentage: Float = 50

    ...
    var body: some View {
    ...
            CustomView(percentage: $percentage)
                .accentColor(.red)
                .frame(width: 200, height: 44)
    ...

【讨论】:

  • 感谢您的解决方案。我明白了。
  • 我发现了一个问题。如果我想通过点击滑块上的任意位置来更改滑块的值,但该值没有改变。我需要拖动它,然后唯一的值正在改变。如何通过点击滑块上的任意位置来更改值?
  • LOL,更简单,使用DragGesture(minimumDistance: 0)Found here
  • 这让我走上了正确的道路。将它与持有音频播放器的 @EnvironmentObject 结合起来,您就开始了一个简单、动态的 SwiftUI 音频播放器跟踪器
【解决方案2】:

就我而言,我必须自定义拇指。 (例如屏幕储物柜)

我留下一个类似问题的答案。




LockerSlider.swift

import SwiftUI

struct LockerSlider<V>: View where V : BinaryFloatingPoint, V.Stride : BinaryFloatingPoint {

    // MARK: - Value
    // MARK: Private
    @Binding private var value: V
    private let bounds: ClosedRange<V>
    private let step: V.Stride

    private let length: CGFloat    = 50
    private let lineWidth: CGFloat = 2

    @State private var ratio: CGFloat   = 0
    @State private var startX: CGFloat? = nil


    // MARK: - Initializer
    init(value: Binding<V>, in bounds: ClosedRange<V>, step: V.Stride = 1) {
        _value  = value
    
        self.bounds = bounds
        self.step   = step
    }


    // MARK: - View
    // MARK: Public
    var body: some View {
        GeometryReader { proxy in
            ZStack(alignment: .leading) {
                // Track
                RoundedRectangle(cornerRadius: length / 2)
                    .foregroundColor(Color(#colorLiteral(red: 0.8039215803, green: 0.8039215803, blue: 0.8039215803, alpha: 1)))
            
                // Thumb
                Circle()
                    .foregroundColor(.white)
                    .frame(width: length, height: length)
                    .offset(x: (proxy.size.width - length) * ratio)
                    .gesture(DragGesture(minimumDistance: 0)
                                .onChanged({ updateStatus(value: $0, proxy: proxy) })
                                .onEnded { _ in startX = nil })
            }
            .frame(height: length)
            .overlay(overlay)
            .simultaneousGesture(DragGesture(minimumDistance: 0)
                                    .onChanged({ update(value: $0, proxy: proxy) }))
            .onAppear {
                ratio = min(1, max(0,CGFloat(value / bounds.upperBound)))
            }
        }
    }

    // MARK: Private
    private var overlay: some View {
        RoundedRectangle(cornerRadius: (length + lineWidth) / 2)
            .stroke(Color.gray, lineWidth: lineWidth)
            .frame(height: length + lineWidth)
    }


    // MARK: - Function
    // MARK: Private
    private func updateStatus(value: DragGesture.Value, proxy: GeometryProxy) {
        guard startX == nil else { return }
    
        let delta = value.startLocation.x - (proxy.size.width - length) * ratio
        startX = (length < value.startLocation.x && 0 < delta) ? delta : value.startLocation.x
    }

    private func update(value: DragGesture.Value, proxy: GeometryProxy) {
        guard let x = startX else { return }
        startX = min(length, max(0, x))
    
        var point = value.location.x - x
        let delta = proxy.size.width - length
    
        // Check the boundary
        if point < 0 {
            startX = value.location.x
            point = 0
        
        } else if delta < point {
            startX = value.location.x - delta
            point = delta
        }
    
        // Ratio
        var ratio = point / delta
    
    
        // Step
        if step != 1 {
            let unit = CGFloat(step) / CGFloat(bounds.upperBound)
        
            let remainder = ratio.remainder(dividingBy: unit)
            if remainder != 0 {
                ratio = ratio - CGFloat(remainder)
            }
        }
    
        self.ratio = ratio
        self.value = V(bounds.upperBound) * V(ratio)
    }
}

Demo.swift

import SwiftUI

struct Demo: View {

    // MARK: - Value
    // MARK: Private
    @State private var number = 150000.0


    // MARK - View
    // MARK: Public
    var body: some View {
        VStack(alignment: .leading, spacing: 10) {
            Text("Number\n\(number)")
                .bold()
                .padding(.bottom, 20)
        
            Text("OS Slider")
            Slider(value: $number, in: 0...1050000, step: 0.02)
                .padding(.bottom, 20)
        
            Text("Custom Slider")
            LockerSlider(value: $number, in: 0...1050000, step: 0.02)
                .padding(.bottom, 20)
        }
        .padding(20)
    }
}

【讨论】:

  • 嘿,登!这段代码太棒了。我想知道你是否有时间我可以付钱让你快速辅导我。我试图了解它是如何工作的,并且对数字是如何更新的感到困惑。谢谢!
  • @JohnKubik 我认为 WWDC 是最好的导师。 Swift 和 SwiftUI 仍然是婴儿,因此您可以通过 WWDC 快速轻松地学习并跟上最新的技术。我还推荐斯坦福大学的免费 iOS 课程。您可以了解 SwiftUI。 youtube.com/watch?v=bqu6BquVi2M
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-11-21
  • 2014-04-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多