Wie zeichne ich in iOS Swift einen Kreis?


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

Das ist es, was ich gerade habe, aber es ist offensichtlich nicht der richtige Weg, es zu tun.

Was ist der einfachste Weg, dies zu tun?


Sie haben eine Reihe von Möglichkeiten, wie Sie dies tun können, SpriteKit, Core Graphics usw. Sie sollten es wahrscheinlich ein bisschen mehr für uns festnageln.
Clay Bridges

5
Kannst du nicht den geringsten Grund sehen, warum jemand dafür stimmen würde, diese Frage zu schließen?
Fattie

@ Joe Ich bin mir ziemlich sicher, dass Sie als Administrator Punkte für bestimmte Aktivitäten verdienen können, einschließlich Abschlussfragen. Das erklärt wahrscheinlich, warum fünf Personen als Abschluss der Frage angeführt werden, als sie (für vier von ihnen) bereits geschlossen war.
Vince O'Sullivan

1
Hallo Vince - richtig, ich verstehe es nicht. Es ist total bizarr, dass diese Frage geschlossen wurde. Auch ist es als "unklar" markiert. Es könnte einfach nicht klarer sein, es ist kristallklar . Bizarr!
Fattie

Fragen über die Verbesserung der Arbeits Code besser auf gefragte Code - Review .
Quentin

Antworten:


208

WARNUNG! Dies ist eine falsche Lösung. Ebenen werden in der drawRectMethode unendlich hinzugefügt (jedes Mal, wenn die Ansicht gezeichnet wird). Sie sollten der drawRectMethode NIEMALS Ebenen hinzufügen . Verwenden Sie layoutSubviewstattdessen.

Sie können damit einen Kreis zeichnen ( 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)

Mit dem Code, den Sie gepostet haben, beschneiden Sie die Ecken von UIViewund fügen der Ansicht keinen Kreis hinzu.


Hier ist ein vollständiges Beispiel für die Verwendung dieser Methode:

/// 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)
     }
}

Ein Kreis, rot umrandet und mit Gelb auf gelbem Grund gefüllt.


Beachten Sie jedoch, dass es einen unglaublich praktischen Anruf gibt:

let circlePath = UIBezierPath(ovalInRect: rect)

das macht die ganze Arbeit, den Weg zu machen. (Vergessen Sie nicht, es für die Linienstärke einzufügen, was auch mit unglaublich einfach ist 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)
}

Ellipsen (ovalartig), rot umrandet und mit Gelb auf gelbem Grund gefüllt.


In der Praxis dieser Tage in Swift würden Sie sicherlich @IBDesignableund verwenden @IBInspectable. Mit diesen können Sie tatsächlich das Rendering im Storyboard sehen und ändern!

Wie Sie sehen können, werden dem Inspektor im Storyboard neue Funktionen hinzugefügt, die Sie im Storyboard ändern können:

Xcode Storyboard Attributes Inspector mit benutzerdefinierten Feldern.

/// 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)
    }
}

Beachten Sie schließlich, dass Sie, wenn Sie ein UIView(das quadratisch ist und das Sie im Storyboard auf rot setzen) haben und es einfach in einen roten Kreis verwandeln möchten, einfach Folgendes tun können:

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

Danke für die Antwort! Kennen Sie den perfekten Unterschied zwischen CornerRadius und BezierPath? Nur neugierig :)
Hyouuu

3
M_PIist veraltet. Es ist jetzt Double.pi(in Swift 3)
KVISH

4
Es ist wahrscheinlich keine gute Idee, eine neue CALayer- func drawRect(rect: CGRect)Methode in die Methode aufzunehmen, da diese Methode benutzerdefiniertes Zeichnen in einer eigenen Ebene ausführen soll. Diese Methode wird auch als mehrfach bezeichnet.
Alex Bin Zhao

3
Bitte fügen Sie keine Unterschichten in der drawRect-Methode hinzu! Erstens wird drawRect während des Ansichtslebenszyklus mehrmals aufgerufen, buchstäblich bei jedem Layoutprozess. Das bedeutet, dass Sie am Ende Dutzende gleicher (oder wahrscheinlich nicht) Unterschichten in Ihrer Hierarchie haben, Speicherplatz verbrauchen und Ihre App verlangsamen. Zweitens ist drawRect nicht zum Ändern der Ansichtshierarchie vorgesehen. Wenn Sie wirklich etwas Besonderes zeichnen müssen, verwenden Sie es in Verbindung mit CoreGraphics-Kontextfunktionen. Besser hier - fügt nur eine Formebene in der Init-Methode hinzu.
Vahotm

8

Wenn Sie ein UIView zum Zeichnen verwenden möchten, müssen Sie den Radius / der Höhe oder Breite festlegen.

Also einfach ändern:

block.layer.cornerRadius = 9

zu:

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

Sie müssen jedoch Höhe und Breite gleich einstellen. Wenn Sie Koregraphik verwenden möchten, sollten Sie Folgendes tun:

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
Sehr schlau. Erstellen eines Kreises mithilfe eines Quadrats. Hat es mir wirklich leicht gemacht.
Alexander Langer

8

Erstellen Sie eine Klasse UIView und weisen Sie ihr diesen Code für einen einfachen Kreis zu

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()


    }


}

Dies ist die einzig richtige Antwort auf das Datum, andere haben große Probleme.
Alexander Volkov

3

Swift 4- Version der akzeptierten Antwort:

@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)
    }
}

2

Aktualisierung des Code-Ansatzes von @ Dario für Xcode 8.2.2, Swift 3.x. Stellen Sie im Storyboard fest, dass die Hintergrundfarbe auf "klar" gesetzt ist, um einen schwarzen Hintergrund in der quadratischen UIView zu vermeiden:

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)
    }
}

Und wenn Sie den Start- und Endwinkel steuern möchten:

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)
    }
}

Vielen Dank für die Swift 3-Übersetzungen, die mir viel Zeit und Tests erspart haben!
Denisq91

WARNUNG! Dies ist eine falsche Lösung - Ebenen werden beim Zeichnen unendlich hinzugefügt (jedes Mal, wenn die Ansicht gezeichnet wird).
Alexander Volkov

@ AlexanderVolkov guter Fang. Ich bin für ein paar Wochen von meiner iOS-Entwicklungsstation entfernt. Haben Sie eine Lösung oder Verbesserung?
Jacob F. Davis C-CISO

1

Ich finde Core Graphics für Swift 3 ziemlich einfach :

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

1

Eine einfache Funktion, die einen Kreis in der Mitte Ihres Fensterrahmens mit einem Multiplikatorprozentsatz zeichnet

/// 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

In Ansicht hinzufügen wurde geladen

    //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

Ein viel einfacherer und ressourcenschonender Ansatz wäre.

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
    }

}

Mit Border Colorund Border Sizeund der Standardeigenschaft Backgroundkönnen Sie das Erscheinungsbild des Kreises definieren.

Geben Sie hier die Bildbeschreibung ein

Bitte beachten Sie, dass zum Zeichnen eines Kreises die Höhe und Breite der Ansicht gleich groß sein müssen.

Der Code funktioniert für Swift >= 4und Xcode >= 9.


0

Hier ist meine Version mit Swift 5 und Core Graphics.

Ich habe eine Klasse erstellt, um zwei Kreise zu zeichnen. Der erste Kreis wird mit erstellt addEllipse(). Es setzt die Ellipse in ein Quadrat und erzeugt so einen Kreis. Ich finde es überraschend, dass es keine Funktion gibt addCircle(). Der zweite Kreis wird mit addArc()2pi Bogenmaß erstellt

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()
    }
}

Fügen Sie in Ihrem ViewController didViewLoad()Folgendes hinzu:

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

self.view.addSubview(myView)

Wenn es läuft, sollte es so aussehen. Ich hoffe dir gefällt meine Lösung!

Geben Sie hier die Bildbeschreibung ein

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.