圆圈和虚线UIView

7

我试图用虚线画一个圆形。我已经可以在矩形中画出线条,但不知道如何在圆形中画出虚线。这里是我找到的答案,但它是使用Objective-C编写的:UIView Draw Circle with Dotted Line Border

以下是我能够绘制由虚线组成的矩形的代码。

func addDashedBorder() {
    let color = UIColor.red.cgColor

    let shapeLayer:CAShapeLayer = CAShapeLayer()
    let frameSize = self.frame.size
    let shapeRect = CGRect(x: 0, y: 0, width: frameSize.width, height: frameSize.height)

    shapeLayer.bounds = shapeRect
    shapeLayer.position = CGPoint(x: frameSize.width/2, y: frameSize.height/2)
    shapeLayer.fillColor = UIColor.clear.cgColor
    shapeLayer.strokeColor = color
    shapeLayer.lineWidth = 2
    shapeLayer.lineJoin = CAShapeLayerLineJoin.round
    shapeLayer.lineDashPattern = [6,3]
    shapeLayer.path = UIBezierPath(roundedRect: shapeRect, cornerRadius: 5).cgPath

    self.layer.addSublayer(shapeLayer)
}

3
你尝试过使用完全相同的代码,但将UIBezierPath的调用更改为创建一个圆形而不是圆角矩形吗? - rmaddy
3个回答

12
当然,您可以使用所选的虚线模式渲染圆形 UIBezierPath
class DashedCircleView: UIView {
    private var shapeLayer: CAShapeLayer = {
        let shapeLayer = CAShapeLayer()
        shapeLayer.strokeColor = UIColor.red.cgColor
        shapeLayer.fillColor = UIColor.clear.cgColor
        shapeLayer.lineWidth = 10
        shapeLayer.lineCap = .round
        shapeLayer.lineDashPattern = [20, 60]
        return shapeLayer
    }()

    override init(frame: CGRect = .zero) {
        super.init(frame: frame)
        configure()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        configure()
    }

    override func layoutSubviews() {
        super.layoutSubviews()
        updatePath()
    }
}

private extension DashedCircleView {
    func configure() {
        layer.addSublayer(shapeLayer)
    }

    func updatePath() {
        let rect = bounds.insetBy(dx: shapeLayer.lineWidth / 2, dy: shapeLayer.lineWidth / 2)
        let radius = min(rect.width, rect.height) / 2
        let center = CGPoint(x: rect.midX, y: rect.midY)
        let path = UIBezierPath(arcCenter: center, radius: radius, startAngle: 0, endAngle: .pi * 2, clockwise: true)
        shapeLayer.path = path.cgPath
    }
}

这会产生:

enter image description here

这种方法的问题在于很难使虚线图案对齐(请注意“3点钟”位置的尴尬虚线)。您可以通过确保lineDashPattern的两个值加起来等于某个能够整除圆周长(例如2π×半径)的数字来修复此问题:

let circumference: CGFloat = 2 * .pi * radius
let count = 30
let relativeDashLength: CGFloat = 0.25
let dashLength = circumference / CGFloat(count)
shapeLayer.lineDashPattern = [dashLength * relativeDashLength, dashLength * (1 - relativeDashLength)] as [NSNumber]

相比于完全使用 lineDashPattern,您可以选择保持实心描边并将path自身作为一系列小弧线来制造期望的虚线效果。这样可以确保在从0到2π旋转时,它被均匀地分成count个小弧线:

class DashedCircleView: UIView {
    private var shapeLayer: CAShapeLayer = {
        let shapeLayer = CAShapeLayer()
        shapeLayer.strokeColor = UIColor.red.cgColor
        shapeLayer.fillColor = UIColor.clear.cgColor
        shapeLayer.lineWidth = 10
        shapeLayer.lineCap = .round
        return shapeLayer
    }()

    override init(frame: CGRect = .zero) {
        super.init(frame: frame)
        configure()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        configure()
    }

    override func layoutSubviews() {
        super.layoutSubviews()
        updatePath()
    }
}

private extension DashedCircleView {
    func configure() {
        layer.addSublayer(shapeLayer)
    }

    func updatePath() {
        let rect = bounds.insetBy(dx: shapeLayer.lineWidth / 2, dy: shapeLayer.lineWidth / 2)
        let radius = min(rect.width, rect.height) / 2
        let center = CGPoint(x: rect.midX, y: rect.midY)

        let path = UIBezierPath()
        let count = 30
        let relativeDashLength: CGFloat = 0.25 // a value between 0 and 1
        let increment: CGFloat = .pi * 2 / CGFloat(count)

        for i in 0 ..< count {
            let startAngle = increment * CGFloat(i)
            let endAngle = startAngle + relativeDashLength * increment
            path.move(to: CGPoint(x: center.x + radius * cos(startAngle), 
                                  y: center.y + radius * sin(startAngle)))
            path.addArc(withCenter: center, radius: radius, startAngle: startAngle, endAngle: endAngle, clockwise: true)
        } 
        shapeLayer.path = path.cgPath
    }
}

这将产生:

在此输入图片描述


当我将图像放入此圆形中时,约束并未设置为圆形。 - Shahzad Ali
我建议使用视图调试器并确认圆形视图和图像视图的框架,看看是否存在一个或多个框架错误(例如您没有正确设置约束)或者圆形视图的CAShapeLayer存在问题(例如您没有像上面那样在“layoutSubviews”或“viewDidLayoutSubviews”中更新形状层)。一旦确定了这两个问题中的哪一个适用于您,您就可以随时发布一个新的、单独的问题,并分享您的发现。 - Rob

3
您可以使用 UIBezierPath(ovalIn:) 在正方形视图中创建一个圆形路径。
extension UIView {
    func addDashedCircle() {
        let circleLayer = CAShapeLayer()
        circleLayer.path = UIBezierPath(ovalIn: bounds).cgPath
        circleLayer.lineWidth = 2.0
        circleLayer.strokeColor =  UIColor.red.cgColor//border of circle
        circleLayer.fillColor = UIColor.white.cgColor//inside the circle
        circleLayer.lineJoin = .round
        circleLayer.lineDashPattern = [6,3]
        layer.addSublayer(circleLayer)
    }
}

设置视图的背景颜色为.clear,图层的填充颜色为.white。
class View1: UIViewController {
    @IBOutlet weak var circleView: UIView!
    override func viewDidLoad() {
        super.viewDidLoad()
        circleView.backgroundColor = .clear//outside the circle
        circleView.addDashedCircle()
    }
}

或者使用UIBezierPath(arcCenter:radius:startAngle:endAngle:clockwise:)
circleLayer.path = UIBezierPath(arcCenter: CGPoint(x: frame.size.width/2, y: frame.size.height/2),
                            radius: min(frame.size.height,frame.size.width)/2,
                            startAngle: 0,
                            endAngle: .pi * 2,
                            clockwise: true).cgPath

enter image description here


是的,我已经做到了,现在我想在破折号之间增加更多的间隔。 - Shahzad Ali
好的,我通过 circleLayer.lineDashPattern = [6,6] 来实现了它,非常感谢您。 - Shahzad Ali
@ShahzadAli 使用 circleLayer.lineDashPattern = [10,15]。这里的10是红线的长度,15是红线之间的白色空间的长度。 - RajeshKumar R
兄弟,当我通过添加约束将图像或视图添加到这个圆形中时,它不会受到影响,约束没有设置到圆形上。 - Shahzad Ali
@ShahzadAli 您正在错误地添加约束。请发布另一个带有相关细节的问题。 - RajeshKumar R
显示剩余6条评论

0
使用 UIBezierPath 的圆形路径变体绘制路径
shapeLayer.path = UIBezierPath(arcCenter: CGPoint(x: frame.size.width * 0.5, y: frame.size.height * 0.5), radius: frame.size.width * 0.5, startAngle: 0, endAngle: .pi * 2, clockwise: true)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接