SwiftUI:具有内部笔触的楔形形状

时间:2019-07-19 06:24:59

标签: drawing border shapes swiftui

我正在尝试制作一张如图所示的图表。

enter image description here

下面的代码很好,但是我真的很希望每个楔形的边框都带有一个内部笔触(而不是居中):

import SwiftUI

struct WedgeShape: Shape {
    var startAngle: Angle
    var endAngle: Angle

    func path(in rect: CGRect) -> Path {
        var p = Path()

        p.addArc(
            center: CGPoint(x: rect.size.width/2, y: rect.size.width/2),
            radius: rect.size.width/2,
            startAngle: startAngle,
            endAngle: endAngle,
            clockwise: false
        )

        return p
    }
}

struct Wedge {
    var startAngle: Double
    var endAngle: Double
    var color: Color
}

struct ContentView: View {
    var wedges = [
        Wedge(startAngle: 0, endAngle: 90, color: Color.red),
        Wedge(startAngle: 90, endAngle: 180, color: Color.green),
        Wedge(startAngle: 180, endAngle: 360, color: Color.blue)
    ]

    var body: some View {
        ZStack {
            ForEach(0 ..< wedges.count) {
                WedgeShape(
                    startAngle: Angle(degrees: self.wedges[$0].startAngle),
                    endAngle: Angle(degrees: self.wedges[$0].endAngle)
                )
                .stroke(self.wedges[$0].color, lineWidth: 44)
            }
        }
    }
}

我知道我可以简单地调整视图的框架来说明笔划。但是,我想要类似Circle().strokeBorder(Color.green, lineWidth: 44)之类的笔画已被考虑的东西。 InsettableShape协议看起来可能对此有所帮助,但是我不确定如何使用inset(by:)方法。

5 个答案:

答案 0 :(得分:1)

这是我创建楔形形状的方式。我绘制了楔形的完整路径,然后可以设置foregroundColor。只需更改a0a1即可更改楔形的长度。

struct WedgeShape: Shape {
  func path(in rect: CGRect) -> Path {
    var p = Path()
    let a0 = Angle(degrees: 0.0)
    let a1 = Angle(degrees: 270.0)
    let cen =  CGPoint(x: rect.size.width/2, y: rect.size.width/2)
    let r0 = rect.size.width/3.5
    let r1 = rect.size.width/2
    p.addArc(center: cen, radius: r0, startAngle: a0, endAngle: a1, clockwise: false)
    p.addArc(center: cen, radius: r1, startAngle: a1, endAngle: a0, clockwise: true)
    p.closeSubpath()
    return p
  }
}

// Then in your `View`
var body: some View {
    WedgeShape()
      .frame(width: 20, height: 20, alignment: .center)
      .foregroundColor(.green)
}

答案 1 :(得分:1)

借助strokedPath(),足以像这样调整rect

struct Ring: Shape {

    var progress: Double = 0.0
    var thickness: Double = 10.0

    func path(in rect: CGRect) -> Path {

        let halfThickness = CGFloat(thickness / 2.0)
        let rect = rect.insetBy(dx: halfThickness, dy: halfThickness)

        return Path() {
            $0.addArc(center: CGPoint(x: rect.midX, y: rect.midY),
                      radius: min(rect.width, rect.height) / 2,
                      startAngle: Angle(degrees: -90),
                      endAngle: Angle(degrees: -90 + 360 * progress),
                      clockwise: false)
        }.strokedPath(StrokeStyle(lineWidth: CGFloat(thickness), lineCap: .round))
    }

    var animatableData: Double {
        get { progress }
        set { progress = min(1.0, max(0, newValue)) }
    }
}

然后就

Ring(progress: 0.5).foregroundColor(.red)

答案 2 :(得分:0)

基于Ricky Padilla的答案的更新解决方案,该解决方案重构了WedgeShape以在呼叫站点获取startAngleendAnglelineWidth

import SwiftUI

struct WedgeShape: Shape {
    let startAngle: Angle
    let endAngle: Angle
    let lineWidth: CGFloat

    func path(in rect: CGRect) -> Path {
        var p = Path()
        let center =  CGPoint(x: rect.size.width/2, y: rect.size.width/2)
        let r1 = rect.size.width/2
        p.addArc(center: center, radius: abs(lineWidth - r1), startAngle: startAngle, endAngle: endAngle, clockwise: false)
        p.addArc(center: center, radius: r1, startAngle: endAngle, endAngle: startAngle, clockwise: true)
        p.closeSubpath()
        return p
    }
}

struct Wedge {
    var startAngle: Double
    var endAngle: Double
    var color: Color
}

struct ContentView: View {
    let wedges = [
        Wedge(startAngle: -45, endAngle: 45, color: Color.blue),
        Wedge(startAngle: 45, endAngle: 180, color: Color.green),
        Wedge(startAngle: 180, endAngle: -45, color: Color.red)
    ]

    var body: some View {
        ZStack {
            ForEach(0 ..< wedges.count) {
                WedgeShape(
                    startAngle: Angle(degrees: self.wedges[$0].startAngle),
                    endAngle: Angle(degrees: self.wedges[$0].endAngle),
                    lineWidth: 44
                )
                .foregroundColor(self.wedges[$0].color)
            }
        }
    }
}

此外,还可以使用平均乔的答案:

import SwiftUI

struct WedgeShape: Shape {
    var progress: Double
    var lineWidth: CGFloat
    var lineCap: CGLineCap = .butt

    func path(in rect: CGRect) -> Path {
        let insetWidth = lineWidth/2
        let rect = rect.insetBy(dx: insetWidth, dy: insetWidth)

        return Path() {
            $0.addArc(
                center: CGPoint(x: rect.midX, y: rect.midY),
                radius: min(rect.width, rect.height)/2,
                startAngle: Angle(degrees: -90),
                endAngle: Angle(degrees: -90 + 360 * progress),
                clockwise: false
            )
        }
        .strokedPath(StrokeStyle(lineWidth: lineWidth, lineCap: lineCap))
    }
}

struct Wedge {
    var progress: Double
    var color: Color
}

struct ContentView: View {
    let wedges = [
        Wedge(progress: 1, color: Color.green),
        Wedge(progress: 0.625, color: Color.blue),
        Wedge(progress: 0.375, color: Color.red)
    ]

    var body: some View {
        ZStack {
            ForEach(0 ..< wedges.count) {
                WedgeShape(
                    progress: self.wedges[$0].progress,
                    lineWidth: 44
                )
                .foregroundColor(self.wedges[$0].color)
            }
        }
        .rotationEffect(Angle(degrees: -90))
    }
}

答案 3 :(得分:0)

我无法透露姓名的人要求我将这段代码留在这里。他注意到运行安全并且不会窃取您的加密货币。基于Ricky Padilla和JWK的示例。

struct WedgeShape: Shape {
    let startAngle: Angle
    let endAngle: Angle
    let outterScale: CGFloat
    let innerScale: CGFloat

    func path(in rect: CGRect) -> Path {
        var p = Path()
        let center =  CGPoint(x: rect.size.width/2, y: rect.size.width/2)
        //let r1 = rect.size.width/2
        p.addArc(center: center,
                 radius: outterScale*rect.size.width/2,
                 //radius: abs(lineWidth - r1),
                 startAngle: startAngle,
                 endAngle: endAngle,
                 clockwise: false)
        p.addArc(center: center,
                 //radius: abs(lineWidth - r1),
                 radius: innerScale*rect.size.width/6,
                 startAngle: endAngle,
                 endAngle: startAngle,
                 clockwise: true)
        p.closeSubpath()
        return p
    }
}

struct Wedge {
    var startAngle: Double
    var endAngle: Double
    var color: Color
}

struct ContentViewWedge: View {
    let pokemonWedges = [
        //Wedge(startAngle: -45, endAngle: 45, color: Color.blue),
        Wedge(startAngle: 90, endAngle: 270, color: Color.white),
        Wedge(startAngle: -90, endAngle: 90, color: Color.red)
    ]

    let backgroundWedge = Wedge(startAngle: 0, endAngle: 360, color: Color.black)

    var body: some View {
        ZStack(alignment: .center) {
            WedgeShape(startAngle: Angle(degrees: self.backgroundWedge.startAngle-90),
                       endAngle: Angle(degrees: self.backgroundWedge.endAngle-90),
                       outterScale: 1.07,
                       innerScale: 1)
                .foregroundColor(self.backgroundWedge.color)
            WedgeShape(
                startAngle: Angle(degrees: self.pokemonWedges[1].startAngle-90),
                endAngle: Angle(degrees: self.pokemonWedges[1].endAngle-90),
                outterScale: 1,
                innerScale: 1.3
            ).foregroundColor(self.pokemonWedges[1].color).offset(y: -3)
            WedgeShape(
             startAngle: Angle(degrees: self.pokemonWedges[0].startAngle-90),
             endAngle: Angle(degrees: self.pokemonWedges[0].endAngle-90),
             outterScale: 1,
             innerScale: 1.3
             ).foregroundColor(self.pokemonWedges[0].color).offset(y: +3)
        }
        .aspectRatio(1, contentMode: .fit)
        .padding(20)
    }
}

struct WedgeShapeView_Previews: PreviewProvider {
    static var previews: some View {
        ContentViewWedge()
    }
}

答案 4 :(得分:0)

我更喜欢使用带有修整效果的圆形

struct WedgeView: View {
    let lineWidth: CGFloat = 60

    var body: some View {
        ZStack {
            Circle()
                .trim(from: 0, to: 0.4)
                .stroke(Color.red, style: StrokeStyle(lineWidth: lineWidth, lineCap: .butt)
            ).rotationEffect(.degrees(-180))
            Circle()
                .trim(from: 0, to: 0.2)
                .stroke(Color.blue, style: StrokeStyle(lineWidth: lineWidth, lineCap: .butt)
            ).rotationEffect(.degrees(-180 + 360.0 * 0.4))
            Circle()
                .trim(from: 0, to: 0.4)
                .stroke(Color.green, style: StrokeStyle(lineWidth: lineWidth, lineCap: .butt)
            ).rotationEffect(.degrees(-180.0 + 360.0 * 0.6)) // 0.6 = 0.4 + 0.2

        }
             // Stroke draws over the edge, so we add some padding to keep things within the frame
            .padding(lineWidth/2)
    }
}

所有内容都在此处进行了硬编码,但是您可以理解要点。