Кольцевая диаграмма с округлением

Я хочу создать кольцевую диаграмму с перекрытием одной стороны с закругленным углом. нравиться

Требование: начальный конец должен быть ниже предыдущей строки, а конечный конец должен быть выше следующей строки. Я устал это делать, но последний слой линии показывает оба сверху. Я пробовал с кодом ниже

private func drawCircle(){
        var startAngle: CGFloat = .pi / -2
        var endAngle: CGFloat = 0.0
        
        drawableItems.forEach { (item) in
            print(item.title)
            print(startAngle)
            let center = calculateCenter()
            endAngle = startAngle + calculateAngles(item: item)
            if endAngle > 2 * CGFloat.pi {
                endAngle -= 2 * .pi
            }
            
            // Radius of the view
            let radius: CGFloat = calculateRadius()
            let arcWidth: CGFloat = self.arcWidth
            let circlePath = UIBezierPath(arcCenter: center,
                                          radius: radius/2 - arcWidth/2,
                                          startAngle: startAngle,
                                          endAngle: endAngle,
                                          clockwise: true)
            
            circlePath.lineCapStyle = .round
            
            let shapeLayer: CAShapeLayer = CAShapeLayer()
            shapeLayer.path = circlePath.cgPath
            shapeLayer.strokeColor = item.color.cgColor
            shapeLayer.lineWidth = arcWidth
            shapeLayer.fillColor = UIColor.clear.cgColor
            shapeLayer.lineCap = .round
            layer.addSublayer(shapeLayer)
            startAngle = endAngle
        }
    }
    

вывод дает, как показано ниже. в котором последний слой с зеленым цветом показывает оба конца сверху вместо одного ниже предыдущего и другого поверх следующего

пожалуйста, помогите мне сделать это.

вам, возможно, придется построить каждую фигуру самостоятельно из составляющих ее дуг. Вам нужно будет начертить каждую фигуру на миллиметровой бумаге, определить центр и радиус каждой дуги и построить заполненный путь для каждой секции.

Duncan C 20.11.2022 14:51
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
1
52
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Проблема в том, что вы рисуете простую дугу с толстой линией и закругленными концами. Два закругленных конца последней дуги всегда будут закрывать предыдущую и первую дуги.

Решение состоит в том, чтобы нарисовать дуги с вогнутым концом, не закрывающим выпуклый конец предыдущей дуги.

Следующее расширение UIBezierPath нарисует такую ​​дугу:

extension UIBezierPath {
    convenience init(
        chartArcCenter center: CGPoint,
        radius: CGFloat,
        lineWidth: CGFloat,
        startAngle: CGFloat,
        endAngle: CGFloat,
        clockwise: Bool
    ) {
        self.init()
        // Draw inner radius arc
        addArc(withCenter: center, radius: radius - lineWidth/2, startAngle: startAngle, endAngle: endAngle, clockwise: clockwise)
        // Draw convex endcap
        var x = cos(endAngle) * radius + center.x
        var y = sin(endAngle) * radius + center.y
        addArc(withCenter: CGPoint(x: x, y: y), radius: lineWidth / 2, startAngle: endAngle - .pi, endAngle: endAngle, clockwise: !clockwise)
        // Draw outer radius arc
        addArc(withCenter: center, radius: radius + lineWidth/2, startAngle: endAngle, endAngle: startAngle, clockwise: !clockwise)
        // Draw concave startcap
        x = cos(startAngle) * radius + center.x
        y = sin(startAngle) * radius + center.y
        addArc(withCenter: CGPoint(x: x, y: y), radius: lineWidth / 2, startAngle: startAngle, endAngle: startAngle + .pi, clockwise: clockwise)
        close()
    }
}

Используйте это в своем коде вместо текущего вызова UIBezierPath. Вы также можете внести изменения в настройки рисования слоя формы.

Со всеми изменениями ваш метод становится:

private func drawCircle(){
    var startAngle: CGFloat = .pi / -2
    var endAngle: CGFloat = 0.0
        
    // Radius of the view
    let radius: CGFloat = calculateRadius()
    let arcWidth: CGFloat = self.arcWidth
    let center = calculateCenter()

    drawableItems.forEach { (item) in
        endAngle = startAngle + calculateAngles(item: item)
            
        let circlePath = UIBezierPath(chartArcCenter: center,
                                      radius: radius/2 - arcWidth/2,
                                      lineWidth: arcWidth,
                                      startAngle: startAngle,
                                      endAngle: endAngle,
                                      clockwise: true)
           
        let shapeLayer: CAShapeLayer = CAShapeLayer()
        shapeLayer.path = circlePath.cgPath
        shapeLayer.fillColor = item.color.cgColor
        layer.addSublayer(shapeLayer)
        startAngle = endAngle
    }
}

Также обратите внимание, что для блока if endAngle > 2 * CGFloat.pi { нет причин. Ничего страшного, если угол «заворачивается». А вычисления радиуса, ширины и центра нужно выполнить только один раз, поэтому нет необходимости в тех, кто находится внутри цикла.

Другие вопросы по теме