ios UIBezierPath位于1下方的图表

yvgpqqbh  于 2022-11-26  发布在  iOS
关注(0)|答案(2)|浏览(229)

我试图使饼图如下,这是工作正常,但我有问题的结束路径(这是橙子的下图)。

我想做的是使橙子形状的结束到下面的绿色之一,这样我就可以实现如下。

有什么建议可以做到这一点?
代码可以在下面的链接找到。
https://drive.google.com/file/d/1ST0zNooLgRaI8s2pDK3NMjBQYjBSRoXB/view?usp=sharing
下面是我所拥有的。

func drawBeizer(start_angle : CGFloat, end_angle : CGFloat, final_color : UIColor) {
    let path1 : UIBezierPath = UIBezierPath()
    
    path1.addArc(withCenter: CGPoint(x: self.frame.size.width/2, y: self.frame.size.height/2), radius: ((self.frame.size.width-main_view_width)/2), startAngle: start_angle, endAngle: end_angle, clockwise: true)
    path1.lineWidth = main_view_width
    path1.lineCapStyle = .round
    final_color.setStroke()
    path1.stroke()
    
}

这个函数传递路径的起始Angular 和结束Angular 及颜色。

y4ekin9u

y4ekin9u1#

此解决方案适用于使用PathSwiftUI

struct DonutElement: Shape{
    
    var width: CGFloat = 50
    var startAngle: Angle
    var endAngle: Angle
    
    func path(in rect: CGRect) -> Path {
        // define commonly used vars
        // the extend of the shape
        let outerRadius = min(rect.width, rect.height) / 2
        // the middle of the shape
        let midRadius = outerRadius - width / 2
        // the inner radius
        let innerRadius = outerRadius - width
        // centerpoint used to move coordinate system in to center
        let center = CGPoint(x: rect.width / 2, y: rect.height / 2)
        
        return Path{ path in
            // calculate the startpoint from the startAngle. startAngle is allready normalized
            let startPoint = startAngle.polarCoordinates(center: center, radius: outerRadius)

            // move the path without drawing to the starting position
            path.move(to: startPoint)
            
            // add the starting concav arc
            // the center point for this arc are the polar coordinates relative to midradius of the donut
            path.addArc(center: startAngle.polarCoordinates(center: center, radius: midRadius), radius: width / 2, startAngle: startAngle.normalizing(), endAngle: (.init(degrees: 180) - startAngle), clockwise: true)
            
            // add the arc that presents the inner donut line
            // center point is the center of the drawing rect with normalized angles
            path.addArc(center: center, radius: innerRadius, startAngle: startAngle.normalizing(), endAngle: endAngle.normalizing(), clockwise: true)
            
            // add the convex arc at the end of the donut element
            // switch clockwise to false and swap end and start angle
            // replace startAngle with endAngle
            path.addArc(center: endAngle.polarCoordinates(center: center, radius: midRadius), radius: width / 2, startAngle: (.init(degrees: 180) - endAngle), endAngle:  endAngle.normalizing(), clockwise: false)
            
            // add the outer stroke to close the shape
            path.addArc(center: center, radius: outerRadius, startAngle: endAngle.normalizing(), endAngle: startAngle.normalizing(), clockwise: false)
            
            // just in case
            path.closeSubpath()
        }
    }
}

extension Shape {
    func fillWithStroke<Fill: ShapeStyle, Stroke: ShapeStyle>(_ fillStyle: Fill, strokeBorder strokeStyle: Stroke, lineWidth: Double = 1) -> some View {
        self
            .stroke(strokeStyle, lineWidth: lineWidth)
            .background(self.fill(fillStyle))
    }
}

struct AngleContainer: Identifiable, Hashable{
    let id = UUID()
    var startAngle: Angle
    var endAngle: Angle
    var color: Color
}


struct Donut: View{
    // each element ends here and starts at the previous or 0. Values in percent
    var elementStops: [(CGFloat , Color)] = [(0.4 , .blue), (45 , .red), (55 , .green), (78 , .gray), (100 , .white)]
    var width: CGFloat = 50.0
    private var angles: [AngleContainer] {
        var angles = [AngleContainer]()
        
        for (index, stop) in elementStops.enumerated(){
            if index == 0{
                let startAngle = Angle(degrees: 0)
                let endAngle = Angle(degrees: stop.0/100 * 360)
                
                angles.append(AngleContainer(startAngle: startAngle,endAngle: endAngle,color: stop.1))
            } else{
                let startAngle = Angle(degrees: elementStops[index - 1].0 / 100 * 360)
                let endAngle = Angle(degrees: stop.0/100 * 360)
                
                angles.append(AngleContainer(startAngle: startAngle,endAngle: endAngle,color: stop.1))
            }
        }
        
        return angles
    }
    var body: some View{
        ZStack{
            ForEach(angles){ angleContainer in
                DonutElement(width: width, startAngle: angleContainer.startAngle, endAngle: angleContainer.endAngle)
                    .fillWithStroke(angleContainer.color, strokeBorder: .black.opacity(0.2))
            }
        }
        .background(.white)
    }
}

// used for transfering coordinate system
extension CGPoint{
    mutating func transfered(to center: CGPoint){
        x = center.x + x
        y = center.y - y
    }
    
    func transfering(to center: CGPoint) -> CGPoint{
        .init(x:  center.x + x, y: center.y - y)
    }

}

我正在规范化Angular 的行为更像在数学与90度在顶部,而不是在底部。

// convenience so angle feels more natural
extension Angle{
    func normalizing() -> Angle {
        Angle(degrees: 0) - self
    }
    
    func polarCoordinates(center: CGPoint, radius: CGFloat) -> CGPoint{
        CGPoint(x: cos(self.radians) * radius, y: sin(self.radians) * radius )
            .transfering(to: center)
    }
}

结果:

bqf10yzr

bqf10yzr2#

我找到了一个方法来做到这一点。下面是完整的代码。
我正在使用结束Angular 添加其他addArc。

视图控制器.swift

import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var myView: MyView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        
        myView.setupData(inner_color: [.green, .purple, .blue, .orange], inner_view_width: self.view.frame.width/5.0, inner_angle: [0.0, 90.0, 180.0, 270.0])
        
        myView.backgroundColor = UIColor.white

    }
    
}

我的视图.swift

import UIKit

class MyView: UIView {
    
    var main_radius : CGFloat = 0.0
    
    var main_color : [UIColor] = [UIColor.black, UIColor.green]
    
    var main_view_width : CGFloat = 1.0
    
    var main_angle : [CGFloat] = [-CGFloat.pi, 0.0]
    var actual_angle : [CGFloat] = []
    
    func setupData(inner_color : [UIColor], inner_view_width : CGFloat, inner_angle : [CGFloat]) {
        main_color = inner_color
        
        main_view_width = inner_view_width
        
        main_angle = inner_angle
        
        actual_angle = inner_angle
        
        var temp_main_angle : [CGFloat] = []
        for i in 0..<main_angle.count {
            temp_main_angle.append(main_angle[i]*Double.pi/180)
        }
        main_angle = temp_main_angle
        
        draw(CGRect(x: 0, y: 0, width: self.frame.size.width, height: self.frame.size.width))
    }
    
    override func draw(_ rect: CGRect) {
        
        if (main_color.count >= 2) {
            
            var inner_position : Int = 0
            
            // first draw with sharp corner
            for i in 0..<main_color.count {
                
                if (i == (main_color.count-1)) {
                    inner_position = 0
                } else {
                    inner_position = i+1
                }
                
                drawBeizer(start_angle: main_angle[i], end_angle: main_angle[inner_position], final_color: main_color[i])
            }
            
            
            // append with ending icons for circle
            for i in 0..<main_color.count {
                
                if (i == (main_color.count-1)) {
                    inner_position = 0
                } else {
                    inner_position = i+1
                }
                
                drawBeizer(start_angle: main_angle[inner_position], end_angle: main_angle[inner_position]+(1*Double.pi/180), final_color: main_color[i], withCircle: true) // make here final_color as .black to see what I am doing with this loop
            }
        }
    }
    
    func drawBeizer(start_angle : CGFloat, end_angle : CGFloat, final_color : UIColor, withCircle: Bool = false) {
        let path1 : UIBezierPath = UIBezierPath()
        
        path1.addArc(withCenter: CGPoint(x: self.frame.size.width/2, y: self.frame.size.height/2), radius: ((self.frame.size.width-main_view_width)/2), startAngle: start_angle, endAngle: end_angle, clockwise: true)
        path1.lineWidth = main_view_width
        if (withCircle) {
            path1.lineCapStyle = .round
        }
        final_color.setStroke()
        path1.stroke()
        
    }
}

完整代码here
第一次

相关问题