如何在iOS Swift中画一个圆?


77
let block = UIView(frame: CGRectMake(cellWidth-25, cellHeight/2-8, 16, 16))
block.backgroundColor = UIColor(netHex: 0xff3b30)
block.layer.cornerRadius = 9
block.clipsToBounds = true

这就是我现在所拥有的,但是显然这不是正确的方法。

最简单的方法是什么?


您可以通过多种方式来执行此操作,例如SpriteKit,Core Graphics等。您可能应该为我们提供更多帮助。
Clay Bridges'Aug

5
完全看不出任何原因,有人会投票结束这个问题吗?
Fattie

@Joe我很确定您可以以管理员身份获得某些活动的积分,包括结束问题。这可能可以解释为什么当(对于其中的四个)问题已经关闭时,有五个人被关闭问题。
文斯·奥沙利文

1
嗨,文斯-对,我不明白。这个问题被关闭是完全奇怪的。也标记为“不清楚”。只是再清晰不过了,它是非常清晰的。怪异的!
Fattie

有关改进工作代码的问题最好在代码审查中提出
昆汀

Answers:


208

警告!这是不正确的解决方案。在drawRect方法中无限添加图层(每次绘制视图时)。您永远不要在drawRect方法中添加图层。使用layoutSubview代替。

您可以以此绘制一个圆(Swift 3.0+):

let circlePath = UIBezierPath(arcCenter: CGPoint(x: 100, y: 100), radius: CGFloat(20), startAngle: CGFloat(0), endAngle: CGFloat(Double.pi * 2), clockwise: true)
    
let shapeLayer = CAShapeLayer()
shapeLayer.path = circlePath.cgPath
    
// Change the fill color
shapeLayer.fillColor = UIColor.clear.cgColor
// You can change the stroke color
shapeLayer.strokeColor = UIColor.red.cgColor
// You can change the line width
shapeLayer.lineWidth = 3.0
    
view.layer.addSublayer(shapeLayer)

使用您发布的代码,您可以裁剪的角落,而UIView不是在视图中添加圆圈。


这是使用该方法的完整示例:

/// A special UIView displayed as a ring of color
class Ring: UIView {
    override func drawRect(rect: CGRect) {
        drawRingFittingInsideView()
    }
    
    internal func drawRingFittingInsideView() -> () {
        let halfSize:CGFloat = min( bounds.size.width/2, bounds.size.height/2)
        let desiredLineWidth:CGFloat = 1 // your desired value
            
        let circlePath = UIBezierPath(
                arcCenter: CGPoint(x:halfSize,y:halfSize),
                radius: CGFloat( halfSize - (desiredLineWidth/2) ),
                startAngle: CGFloat(0),
                endAngle:CGFloat(M_PI * 2),
                clockwise: true)
    
         let shapeLayer = CAShapeLayer()
         shapeLayer.path = circlePath.CGPath
            
         shapeLayer.fillColor = UIColor.clearColor().CGColor
         shapeLayer.strokeColor = UIColor.redColor().CGColor
         shapeLayer.lineWidth = desiredLineWidth
    
         layer.addSublayer(shapeLayer)
     }
}

在红色概述的圈子并且充满在黄色背景的黄色。


请注意,但是有一个非常方便的电话:

let circlePath = UIBezierPath(ovalInRect: rect)

这会完成所有的工作。(不要忘了用线条粗细来插入它,使用CGRectInset。也非常容易。)

internal func drawRingFittingInsideView(rect: CGRect) {
    let desiredLineWidth:CGFloat = 4    // Your desired value
    let hw:CGFloat = desiredLineWidth/2
    
    let circlePath = UIBezierPath(ovalInRect: CGRectInset(rect,hw,hw))
    let shapeLayer = CAShapeLayer()
    shapeLayer.path = circlePath.CGPath
    shapeLayer.fillColor = UIColor.clearColor().CGColor
    shapeLayer.strokeColor = UIColor.redColor().CGColor
    shapeLayer.lineWidth = desiredLineWidth
    layer.addSublayer(shapeLayer)
}

椭圆形(椭圆形)以红色概述,并在黄色背景上充满黄色。


在Swift的实践中,您肯定会使用@IBDesignable@IBInspectable。使用这些,您实际上可以在情节提要中查看和更改渲染!

如您所见,它实际上在情节提要上向检查器添加了新功能,您可以在情节提要上对其进行更改:

具有自定义字段的Xcode故事板属性检查器。

/// A dot with a border, which you can control completely in Storyboard
@IBDesignable class Dot: UIView {

    @IBInspectable var mainColor: UIColor = UIColor.blueColor() {
        didSet { 
             print("mainColor was set here")
        }
    }

    @IBInspectable var ringColor: UIColor = UIColor.orangeColor() {
         didSet {
             print("bColor was set here") 
        }
    }

    @IBInspectable var ringThickness: CGFloat = 4 {
        didSet { 
            print("ringThickness was set here")
        }
    }
    
    @IBInspectable var isSelected: Bool = true
    
    override func drawRect(rect: CGRect) {
        let dotPath = UIBezierPath(ovalInRect:rect)
        let shapeLayer = CAShapeLayer()
        shapeLayer.path = dotPath.CGPath
        shapeLayer.fillColor = mainColor.CGColor
        layer.addSublayer(shapeLayer)
        
        if (isSelected) { 
            drawRingFittingInsideView(rect)
        }
    }
    
    internal func drawRingFittingInsideView(rect: CGRect) {
        let hw:CGFloat = ringThickness/2
        let circlePath = UIBezierPath(ovalInRect: CGRectInset(rect,hw,hw) )
        
        let shapeLayer = CAShapeLayer()
        shapeLayer.path = circlePath.CGPath
        shapeLayer.fillColor = UIColor.clearColor().CGColor
        shapeLayer.strokeColor = ringColor.CGColor
        shapeLayer.lineWidth = ringThickness
        layer.addSublayer(shapeLayer)
    }
}

最后,请注意,如果您有一个UIView(是正方形,并且在情节提要中设置为红色),并且只想将其变成红色圆圈,则可以执行以下操作:

// Makes a UIView into a circular dot of color
class Dot: UIView {
    override func layoutSubviews() {
        layer.cornerRadius = bounds.size.width/2
    }
}

谢谢你的回答!您知道使用cornerRadius和BezierPath之间的性能差异吗?只是好奇:)
hyouuu

3
M_PI不推荐使用。现在Double.pi(在Swift 3中)
KVISH

4
func drawRect(rect: CGRect)方法中添加新的CALayer可能不是一个好主意,因为该方法应该在自己的层中进行自定义绘制,因此该方法也称为多次。
Alex Bin Zhao

3
请不要在drawRect方法中添加子层!首先,在视图生命周期中,实际上是在每个布局过程中,都会多次调用drawRect。这意味着,您最终将在层次结构中拥有数十个相等(或可能没有)的子层,从而消耗内存并降低应用程序运行速度。其次,drawRect不适用于修改视图层次结构。如果您确实需要绘制一些特殊的东西-您可以将其与CoreGraphics上下文函数结合使用。更好的方法-只是在init方法中添加形状层。
vahotm '19

8

如果要使用UIView进行绘制,则需要将半径/设置为高度或宽度。

所以只需更改:

block.layer.cornerRadius = 9

至:

block.layer.cornerRadius = block.frame.width / 2

但是,您需要使高度和宽度相同。如果您想使用核心图形,则需要执行以下操作:

CGContextRef ctx= UIGraphicsGetCurrentContext();
CGRect bounds = [self bounds];

CGPoint center;
center.x = bounds.origin.x + bounds.size.width / 2.0;
center.y = bounds.origin.y + bounds.size.height / 2.0;
CGContextSaveGState(ctx);

CGContextSetLineWidth(ctx,5);
CGContextSetRGBStrokeColor(ctx,0.8,0.8,0.8,1.0);
CGContextAddArc(ctx,locationOfTouch.x,locationOfTouch.y,30,0.0,M_PI*2,YES);
CGContextStrokePath(ctx);

1
非常聪明。用一个正方形画一个圆。对我来说真的很容易。
亚历山大·朗格

8

制作一个类UIView并将此代码分配给它一个简单的圆圈

import UIKit
@IBDesignable
class DRAW: UIView {

    override func draw(_ rect: CGRect) {

        var path = UIBezierPath()
        path = UIBezierPath(ovalIn: CGRect(x: 50, y: 50, width: 100, height: 100))
        UIColor.yellow.setStroke()
        UIColor.red.setFill()
        path.lineWidth = 5
        path.stroke()
        path.fill()


    }


}

这是迄今为止唯一正确的答案,其他人则有重大问题。
亚历山大·沃尔科夫

3

Swift 4版本接受的答案:

@IBDesignable
class CircledDotView: UIView {

    @IBInspectable var mainColor: UIColor = .white {
        didSet { print("mainColor was set here") }
    }
    @IBInspectable var ringColor: UIColor = .black {
        didSet { print("bColor was set here") }
    }
    @IBInspectable var ringThickness: CGFloat = 4 {
        didSet { print("ringThickness was set here") }
    }

    @IBInspectable var isSelected: Bool = true

    override func draw(_ rect: CGRect) {
        let dotPath = UIBezierPath(ovalIn: rect)
        let shapeLayer = CAShapeLayer()
        shapeLayer.path = dotPath.cgPath
        shapeLayer.fillColor = mainColor.cgColor
        layer.addSublayer(shapeLayer)

        if (isSelected) {
            drawRingFittingInsideView(rect: rect)
        }
    }

    internal func drawRingFittingInsideView(rect: CGRect) {
        let hw: CGFloat = ringThickness / 2
        let circlePath = UIBezierPath(ovalIn: rect.insetBy(dx: hw, dy: hw))

        let shapeLayer = CAShapeLayer()
        shapeLayer.path = circlePath.cgPath
        shapeLayer.fillColor = UIColor.clear.cgColor
        shapeLayer.strokeColor = ringColor.cgColor
        shapeLayer.lineWidth = ringThickness
        layer.addSublayer(shapeLayer)
    }
}

您在每个绘图调用上都添加了CALayer,这并不好,因为每次rect无效时都会调用绘图。您需要一些检查,以检查是否已经添加了图层。
马丁·伯杰

2

为Xcode 8.2.2,Swift 3.x更新@Dario的代码方法。注意在情节提要中,将“背景”颜色设置为“清除”,以避免在方形UIView中出现黑色背景:

import UIKit
@IBDesignable
class Dot:UIView
{
    @IBInspectable var mainColor: UIColor = UIColor.clear
        {
        didSet { print("mainColor was set here") }
    }
    @IBInspectable var ringColor: UIColor = UIColor.clear
        {
        didSet { print("bColor was set here") }
    }
    @IBInspectable var ringThickness: CGFloat = 4
        {
        didSet { print("ringThickness was set here") }
    }


    @IBInspectable var isSelected: Bool = true

    override func draw(_ rect: CGRect)
    {

        let dotPath = UIBezierPath(ovalIn: rect)
        let shapeLayer = CAShapeLayer()
        shapeLayer.path = dotPath.cgPath
        shapeLayer.fillColor = mainColor.cgColor
        layer.addSublayer(shapeLayer)

        if (isSelected) { drawRingFittingInsideView(rect: rect) }
    }

    internal func drawRingFittingInsideView(rect: CGRect)->()
    {
        let hw:CGFloat = ringThickness/2
        let circlePath = UIBezierPath(ovalIn: rect.insetBy(dx: hw,dy: hw) )

        let shapeLayer = CAShapeLayer()
        shapeLayer.path = circlePath.cgPath
        shapeLayer.fillColor = UIColor.clear.cgColor
        shapeLayer.strokeColor = ringColor.cgColor
        shapeLayer.lineWidth = ringThickness
        layer.addSublayer(shapeLayer)
    }
}

如果要控制起点和终点角度:

import UIKit
@IBDesignable
class Dot:UIView
{
    @IBInspectable var mainColor: UIColor = UIColor.clear
        {
        didSet { print("mainColor was set here") }
    }
    @IBInspectable var ringColor: UIColor = UIColor.clear
        {
        didSet { print("bColor was set here") }
    }
    @IBInspectable var ringThickness: CGFloat = 4
        {
        didSet { print("ringThickness was set here") }
    }


    @IBInspectable var isSelected: Bool = true

    override func draw(_ rect: CGRect)
    {

        let dotPath = UIBezierPath(ovalIn: rect)
        let shapeLayer = CAShapeLayer()
        shapeLayer.path = dotPath.cgPath
        shapeLayer.fillColor = mainColor.cgColor
        layer.addSublayer(shapeLayer)

        if (isSelected) { drawRingFittingInsideView(rect: rect) }
    }

    internal func drawRingFittingInsideView(rect: CGRect)->()
    {
        let halfSize:CGFloat = min( bounds.size.width/2, bounds.size.height/2)
        let desiredLineWidth:CGFloat = ringThickness   // your desired value

        let circlePath = UIBezierPath(
            arcCenter: CGPoint(x: halfSize, y: halfSize),
            radius: CGFloat( halfSize - (desiredLineWidth/2) ),
            startAngle: CGFloat(0),
            endAngle:CGFloat(Double.pi),
            clockwise: true)

        let shapeLayer = CAShapeLayer()
        shapeLayer.path = circlePath.cgPath
        shapeLayer.fillColor = UIColor.clear.cgColor
        shapeLayer.strokeColor = ringColor.cgColor
        shapeLayer.lineWidth = ringThickness
        layer.addSublayer(shapeLayer)
    }
}

感谢Swift 3的翻译,为我节省了很多时间和测试!
denisq91

警告!这是不正确的解决方案-图层在draw方法中无限添加(每次绘制视图时)。
亚历山大·沃尔科夫

@AlexanderVolkov好抓。我已经离开iOS开发站了几个星期,您是否有修复或改进?
Jacob F. Davis C-CISO

1

我发现Core Graphics对于Swift 3非常简单:

if let cgcontext = UIGraphicsGetCurrentContext() {
    cgcontext.strokeEllipse(in: CGRect(x: center.x-diameter/2, y: center.y-diameter/2, width: diameter, height: diameter))
}

1

一个简单的函数,使用乘数百分比在窗口框架的中间绘制一个圆

/// CGFloat is a multiplicator from self.view.frame.width
func drawCircle(withMultiplicator coefficient: CGFloat) {

    let radius = self.view.frame.width / 2 * coefficient

    let circlePath = UIBezierPath(arcCenter: self.view.center, radius: radius, startAngle: CGFloat(0), endAngle:CGFloat(Double.pi * 2), clockwise: true)
    let shapeLayer = CAShapeLayer()
    shapeLayer.path = circlePath.cgPath

    //change the fill color
    shapeLayer.fillColor = UIColor.clear.cgColor
    shapeLayer.strokeColor = UIColor.darkGray.cgColor
    shapeLayer.lineWidth = 2.0

    view.layer.addSublayer(shapeLayer)
}

0

在视图中添加确实加载了

    //Circle Points

     var CircleLayer   = CAShapeLayer() 

    let center = CGPoint (x: myCircleView.frame.size.width / 2, y: myCircleView.frame.size.height / 2)
    let circleRadius = myCircleView.frame.size.width / 2
    let circlePath = UIBezierPath(arcCenter: center, radius: circleRadius, startAngle: CGFloat(M_PI), endAngle: CGFloat(M_PI * 4), clockwise: true)
    CircleLayer.path = circlePath.cgPath
   CircleLayer.strokeColor = UIColor.red.cgColor
    CircleLayer.fillColor = UIColor.blue.cgColor
    CircleLayer.lineWidth = 8
    CircleLayer.strokeStart = 0
    CircleLayer.strokeEnd  = 1
    Self.View.layer.addSublayer(CircleLayer)

0

一种更容易且资源友好的方法。

import UIKit

@IBDesignable
class CircleDrawView: UIView {

    @IBInspectable var borderColor: UIColor = UIColor.red;

    @IBInspectable var borderSize: CGFloat = 4

    override func draw(_ rect: CGRect)
    {
        layer.borderColor = borderColor.cgColor
        layer.borderWidth = borderSize
        layer.cornerRadius = self.frame.height/2
    }

}

使用Border ColorBorder Size和默认Background属性,可以定义圆的外观。

在此处输入图片说明

请注意,要绘制一个圆,视图的高度和宽度必须相等。

该代码适用于Swift >= 4Xcode >= 9


0

这是我使用Swift 5和Core Graphics的版本。

我创建了一个班画两个圆。第一个圆是使用创建的addEllipse()。它将椭圆变成正方形,从而创建一个圆。没有功能我感到惊讶addCircle()。第二个圆是使用addArc()2pi弧度创建的

import UIKit

@IBDesignable
class DrawCircles: UIView {

    override init(frame: CGRect) {
        super.init(frame: frame)
    }

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

    override func draw(_ rect: CGRect) {

        guard let context = UIGraphicsGetCurrentContext() else {
            print("could not get graphics context")
            return
        }

        context.setLineWidth(2)

        context.setStrokeColor(UIColor.blue.cgColor)

        context.addEllipse(in: CGRect(x: 30, y: 30, width: 50.0, height: 50.0))

        context.strokePath()

        context.setStrokeColor(UIColor.red.cgColor)

        context.beginPath() // this prevents a straight line being drawn from the current point to the arc

        context.addArc(center: CGPoint(x:100, y: 100), radius: 20, startAngle: 0, endAngle: 2.0*CGFloat.pi, clockwise: false)

        context.strokePath()
    }
}

在您的ViewController中didViewLoad()添加以下内容:

let myView = DrawCircles(frame: CGRect(x: 50, y: 50, width: 300, height: 300))

self.view.addSubview(myView)

它运行时应如下所示。希望您喜欢我的解决方案!

在此处输入图片说明

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.