简体   繁体   中英

How to add shadow to SwiftUI arc?

I have the following arc:

struct Arc : Shape
{
    @Binding var endAngle: Double
    var center: CGPoint
    var radius: CGFloat
    func path(in rect: CGRect) -> Path
    {
        var path = Path()

        path.addArc(center: center, radius: radius, startAngle: .degrees(270), endAngle: .degrees(endAngle), clockwise: false)

        return path.strokedPath(.init(lineWidth: 50, lineCap: .round))
    }
}

How can I add shadow, similar to the activity arcs on Apple Watch, such that at full circle the endAngle is still discernible?

EDIT: There's the additional issue at 360+ degrees (so full circle), that both arc ends get combined and are shown as a radial line (I see this because I've a applied an AngularGradient ). Of course Arc won't do advanced things like continuing above the startAngle position, like the Apple Watch arcs do. But this was what I was looking for. Does anyone know how to do that in SwiftUI?

在此处输入图像描述 what about this? ok...bit "tricky"

import SwiftUI

    struct Arc : Shape
    {
    @Binding var startAngle: Double
    @Binding var endAngle: Double

    var center: CGPoint
    var radius: CGFloat
    var color: Color

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

        let cgPath = CGMutablePath()
        cgPath.addArc(center: CGPoint(x: rect.midX, y: rect.midY), radius: radius, startAngle: CGFloat(startAngle), endAngle: CGFloat(endAngle), clockwise: true)

      //  path.addArc(center: center, radius: radius, startAngle: .degrees(270), endAngle: .degrees(endAngle), clockwise: false)

        path = Path(cgPath)

        return path.strokedPath(.init(lineWidth: 50, lineCap: .round))
    }
}

struct ContentView: View {
    var body: some View {
        ZStack() {
       //     Arc(endAngle: .constant(269), center: CGPoint(x: 200, y: 200), radius: 150, color: .red).foregroundColor(.red).opacity(0.2)
            Arc(startAngle: .constant(80 + 271), endAngle: .constant(80 + 271 + 340), center: CGPoint(x: 205, y: 205), radius: 150, color: .red).foregroundColor(.red)//.shadow(color: .black, radius: 5, x: -30, y: -170)
            Arc(startAngle: .constant(90), endAngle: .constant(80 + 270), center: CGPoint(x: 200, y: 200), radius: 150, color: .red).foregroundColor(.red).shadow(color: .black, radius: 5, x: -114, y: -230)
        }.background(Color.black)
    }
}

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

do you mean something like this?

struct Arc : Shape
{
    @Binding var endAngle: Double

    var center: CGPoint
    var radius: CGFloat
    var color: Color

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

        path.addArc(center: center, radius: radius, startAngle: .degrees(270), endAngle: .degrees(endAngle), clockwise: false)

        return path.strokedPath(.init(lineWidth: 50, lineCap: .round))
    }
}

struct ContentView: View {
    var body: some View {
        ZStack() {
            Arc(endAngle: .constant(269), center: CGPoint(x: 200, y: 200), radius: 150, color: .red).foregroundColor(.red).opacity(0.2)
            Arc(endAngle: .constant(90), center: CGPoint(x: 200, y: 200), radius: 150, color: .red).foregroundColor(.red)
        }
    }
}

Hi you can fix and handle all of these features by writing an extension in your project file

    import UIKit
extension UIView {
func roundCorners(corners: UIRectCorner, radius: CGFloat) {
    let path = UIBezierPath(roundedRect: self.bounds, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius))
    let mask = CAShapeLayer()
    mask.path = path.cgPath
    self.layer.mask = mask
}

// MARK: Shadow extension UIView {

func dropShadow(scale: Bool = true) {
    layer.masksToBounds = false
    layer.shadowColor = UIColor.blue.cgColor
    layer.shadowOpacity = 1.0
    layer.shadowOffset = CGSize(width: -1.5, height: 3)
    layer.shadowRadius = 3

    layer.shadowPath = UIBezierPath(rect: bounds).cgPath
    layer.shouldRasterize = true
    layer.rasterizationScale = scale ? UIScreen.main.scale : 1
}

func dropShadow(color: UIColor, opacity: Float = 0.5, offSet: CGSize, radius: CGFloat = 1, scale: Bool = true) {
    layer.masksToBounds = false
    layer.shadowColor = color.cgColor
    layer.shadowOpacity = opacity
    layer.shadowOffset = offSet
    layer.shadowRadius = radius

    layer.shadowPath = UIBezierPath(rect: self.bounds).cgPath
    layer.shouldRasterize = true
    layer.rasterizationScale = scale ? UIScreen.main.scale : 1
}

func setAnchorPoint(_ point: CGPoint) {
    var newPoint = CGPoint(x: bounds.size.width * point.x, y: bounds.size.height * point.y)
    var oldPoint = CGPoint(x: bounds.size.width * layer.anchorPoint.x, y: bounds.size.height * layer.anchorPoint.y)

    newPoint = newPoint.applying(transform)
    oldPoint = oldPoint.applying(transform)

    var position = layer.position

    position.x -= oldPoint.x
    position.x += newPoint.x

    position.y -= oldPoint.y
    position.y += newPoint.y

    layer.position = position
    layer.anchorPoint = point
}

class func fromNib<T: UIView>() -> T {
    guard let view = Bundle.main.loadNibNamed(String(describing: T.self), owner: nil, options: nil)?.first as? T else { fatalError() }
    return view
}

now in storyboard you can handle shadows or you don't want to, and it's too much, you can use the only part you need.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM