【问题标题】:Half circular progress bar with gradient swift带渐变的半圆形进度条 swift
【发布时间】:2019-12-07 09:32:11
【问题描述】:

我想使用 swift 在 iOS 中创建一个带有渐变色的 half circular progress bar。进度条最小值为 300,进度条最大值为 900。进度条的当前值是动态的。我附上屏幕截图和 css 以供颜色参考。有人可以给我一个小的工作演示吗?

下面是 CSS -

/* Layout Properties */
top: 103px;
left: 105px;
width: 165px;
height: 108px;

/* UI Properties */

 background: transparent linear-gradient
(269deg, #32E1A0 0%, #EEED56 23%, #EFBF39 50%, #E59148 75%, #ED4D4D 100%)
 0% 0% no-repeat padding-box;
 opacity: 1;

【问题讨论】:

标签: ios swift progress-bar gradient swift4.2


【解决方案1】:

这可以解决您在 swiftui 中的问题

import SwiftUI

struct ContentView: View {
    @State var progressValue: Float = 0.3
    let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
    @State private var degress: Double = -110
    
    var body: some View {
        
        VStack {
            ZStack{
                
                ProgressBar(progress: self.$progressValue)
                    .frame(width: 250.0, height: 250.0)
                    .padding(40.0).onReceive(timer) { _ in
                        withAnimation {
                            if progressValue < 0.8999996 {
                                progressValue += 0.0275
                            }
                        }
                    }
                
                ProgressBarTriangle(progress: self.$progressValue).frame(width: 280.0, height: 290.0).rotationEffect(.degrees(degress), anchor: .bottom)
                    .offset(x: 0, y: -150).onReceive(timer) { input in
                        withAnimation(.linear(duration: 0.01).speed(200)) {
                            if degress < 110.0 {
                                degress += 10
                            }
                            print(degress)
                        }
                    }
            }
            Spacer()
        }
    }
    
    struct ProgressBar: View {
        @Binding var progress: Float
        
        var body: some View {
            ZStack {
                Circle()
                    .trim(from: 0.3, to: 0.9)
                    .stroke(style: StrokeStyle(lineWidth: 12.0, lineCap: .round, lineJoin: .round))
                    .opacity(0.3)
                    .foregroundColor(Color.gray)
                    .rotationEffect(.degrees(54.5))
                
                Circle()
                    .trim(from: 0.3, to: CGFloat(self.progress))
                    .stroke(style: StrokeStyle(lineWidth: 12.0, lineCap: .round, lineJoin: .round))
                    .fill(AngularGradient(gradient: Gradient(stops: [
                        .init(color: Color.init(hex: "ED4D4D"), location: 0.39000002),
                        .init(color: Color.init(hex: "E59148"), location: 0.48000002),
                        .init(color: Color.init(hex: "EFBF39"), location: 0.5999999),
                        .init(color: Color.init(hex: "EEED56"), location: 0.7199998),
                        .init(color: Color.init(hex: "32E1A0"), location: 0.8099997)]), center: .center))
                    .rotationEffect(.degrees(54.5))
                
                VStack{
                    Text("824").font(Font.system(size: 44)).bold().foregroundColor(Color.init(hex: "314058"))
                    Text("Great Score!").bold().foregroundColor(Color.init(hex: "32E1A0"))
                }
            }
        }
    }
    
    struct ProgressBarTriangle: View {
        @Binding var progress: Float
        
        
        var body: some View {
            ZStack {
                Image("triangle").resizable().frame(width: 10, height: 10, alignment: .center)
            }
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

extension Color {
    init(hex: String) {
        let hex = hex.trimmingCharacters(in: CharacterSet.alphanumerics.inverted)
        var int: UInt64 = 0
        Scanner(string: hex).scanHexInt64(&int)
        let a, r, g, b: UInt64
        switch hex.count {
        case 3: // RGB (12-bit)
            (a, r, g, b) = (255, (int >> 8) * 17, (int >> 4 & 0xF) * 17, (int & 0xF) * 17)
        case 6: // RGB (24-bit)
            (a, r, g, b) = (255, int >> 16, int >> 8 & 0xFF, int & 0xFF)
        case 8: // ARGB (32-bit)
            (a, r, g, b) = (int >> 24, int >> 16 & 0xFF, int >> 8 & 0xFF, int & 0xFF)
        default:
            (a, r, g, b) = (1, 1, 1, 0)
        }
        
        self.init(
            .sRGB,
            red: Double(r) / 255,
            green: Double(g) / 255,
            blue:  Double(b) / 255,
            opacity: Double(a) / 255
        )
    }
}

【讨论】:

  • 这个动画只是为了好玩。您可以初始化progressValue,一切顺利。
  • 我还使用静态值来演示您可以在自己的代码中使它们动态化的解决方案。
  • 如何用三角形表示值?我需要三角形,因为我的仪表不是“进度条”。
  • 我已经添加了三角形,但@John 请在您想使用它时使值动态化。
  • @John 检查这个 repo。我这样做是为了让任何人都可以使用进度条和角度渐变。希望对你有帮助github.com/al100/SwiftPartialCircularProgressBar
【解决方案2】:

您可以使用名为的框架创建不同但相似的进度条 MKMagneticProgress

示例代码:-

import MKMagneticProgress

@IBOutlet weak var magProgress:MKMagneticProgress!

override func viewDidLoad() {
    magProgress.setProgress(progress: 0.5, animated: true)
    magProgress.progressShapeColor = UIColor.blue
    magProgress.backgroundShapeColor = UIColor.yellow
    magProgress.titleColor = UIColor.red
    magProgress.percentColor = UIColor.black

    magProgress.lineWidth = 10
    magProgress.orientation = .top
    magProgress.lineCap = .round

    magProgress.title = "Title"
    magProgress.percentLabelFormat = "%.2f%%"

}

我希望它会工作...... :)

【讨论】:

  • 是否有 SwiftUI 解决方案(不使用 UIViewRepresentable 包装器?)
【解决方案3】:

我实现了类似你的进度条的东西, 用它来升级 - 获得经验。进步。,

如果从 300 增加到 1000,则总条数为 1500 progressFrom: 300/1500, progressTo: 1000/1500 ..

func progressAnimation(duration: TimeInterval, progressFrom: CGFloat, progressTo: CGFloat) {
    if progressFrom == 0 {
        baseProgressLayer.removeFromSuperlayer()
    }
    
    let circlePath = UIBezierPath(ovalIn: CGRect(center: CGPoint(x: 93.0, y: 93.0), size: CGSize(width: 178, height: 178)))
    
    progressLayer.path = circlePath.cgPath
    progressLayer.fillColor = UIColor.clear.cgColor
    progressLayer.strokeColor = getColor(progress: progressTo).cgColor
    progressLayer.lineWidth = 8.0
    layer.addSublayer(progressLayer)
    layer.transform = CATransform3DMakeRotation(CGFloat(90 * Double.pi / 180), 0, 0, -1)
    
    let end = CABasicAnimation(keyPath: "strokeEnd")
    end.fromValue = progressFrom
    end.toValue = progressTo
    end.duration = duration * 0.85
    end.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.linear)
    end.fillMode = CAMediaTimingFillMode.forwards
    end.isRemovedOnCompletion = false
    
    let color = CABasicAnimation(keyPath: "strokeColor")
    color.fromValue = getColor(progress: progressFrom).cgColor
    color.toValue = getColor(progress: progressTo).cgColor
    color.duration = duration * 0.85
    color.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.linear)
    color.fillMode = CAMediaTimingFillMode.forwards
    color.isRemovedOnCompletion = false
    
    let group = CAAnimationGroup()
    group.animations = [end, color]
    group.duration = duration * 0.85
    progressLayer.strokeStart = progressFrom
    progressLayer.strokeEnd = progressTo
    progressLayer.add(group, forKey: "move")

    if progressFrom != 0 {
        let color2 = CABasicAnimation(keyPath: "strokeColor")
        color2.fromValue = getColor(progress: progressFrom).cgColor
        color2.toValue = getColor(progress: progressTo).cgColor
        color2.duration = duration * 0.85
        color2.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.linear)
        color2.fillMode = CAMediaTimingFillMode.forwards
        color2.isRemovedOnCompletion = false
        
       baseProgressLayer.add(color2, forKey: "baseColor")
    }
    
}

有一个为进度获取颜色的扩展

扩展 CircularProgressBar {

private func getColor (progress: CGFloat) -> UIColor{
    if progress >= 0 && progress <= 0.5{
        return UIColor(red: CGFloat(255 / 255), green: CGFloat((0+((108-0)*progress*2)) / 255), blue: 0, alpha: 1.0)
    }
    else if progress > 0.5 && progress <= 1.0 {
        return UIColor(red: CGFloat((255-((255-126)*(progress-0.5)*2)) / 255), green: CGFloat((108+((211-108)*(progress-0.5)*2)) / 255), blue: CGFloat((0+((33-0)*(progress-0.5)*2)) / 255), alpha: 1.0)
    }
    else {
        return UIColor.green
    }
}

}

【讨论】: