Zeichnen Sie eine gepunktete (nicht gestrichelte!) Linie mit IBDesignable im Jahr 2017


85

Mit UIKit ist es einfach, eine gestrichelte Linie zu zeichnen. So:

CGFloat dashes[] = {4, 2};
[path setLineDash:dashes count:2 phase:0];
[path stroke];

Geben Sie hier die Bildbeschreibung ein

Gibt es eine Möglichkeit, eine echte gepunktete Linie zu zeichnen?

Geben Sie hier die Bildbeschreibung ein

Irgendwelche Ideen?


Da diese Frage wirklich alt ist und niemand eine vollständige @IBDesignableLösung gefunden hat, ist sie hier ...

Hoffe, es erspart jemandem das Tippen.

@IBDesignable class DottedVertical: UIView {

    @IBInspectable var dotColor: UIColor = UIColor.etc
    @IBInspectable var lowerHalfOnly: Bool = false

    override func draw(_ rect: CGRect) {

        // say you want 8 dots, with perfect fenceposting:
        let totalCount = 8 + 8 - 1
        let fullHeight = bounds.size.height
        let width = bounds.size.width
        let itemLength = fullHeight / CGFloat(totalCount)

        let path = UIBezierPath()

        let beginFromTop = CGFloat(0.0)
        let top = CGPoint(x: width/2, y: beginFromTop)
        let bottom = CGPoint(x: width/2, y: fullHeight)

        path.move(to: top)
        path.addLine(to: bottom)

        path.lineWidth = width

        let dashes: [CGFloat] = [itemLength, itemLength]
        path.setLineDash(dashes, count: dashes.count, phase: 0)

        // for ROUNDED dots, simply change to....
        //let dashes: [CGFloat] = [0.0, itemLength * 2.0]
        //path.lineCapStyle = CGLineCap.round

        dotColor.setStroke()
        path.stroke()
    }
}

Ich habe es vertikal gemacht, du kannst es leicht ändern.

Geben Sie hier die Bildbeschreibung ein

Fügen Sie einfach eine UIView in die Szene ein. Machen Sie es wie Sie möchten, und das ist die Breite der gepunkteten Linie.

Ändern DottedVerticalSie einfach die Klasse in und Sie sind fertig. Es wird so richtig im Storyboard gerendert.

Geben Sie hier die Bildbeschreibung ein

Beachten Sie, dass der für die Höhe der Blöcke angegebene Beispielcode ("totalCount" usw.) dazu führt, dass die Blöcke perfekt zum Pixel passen und mit den Enden der UIView übereinstimmen, die die Linie erstellt.

Stellen Sie sicher, dass Sie RobMayoffs Antwort unten ankreuzen, die die beiden erforderlichen Codezeilen für Punkte-nicht-Blöcke enthält.


Hier ist eine fantastische Möglichkeit, diagonale Linien zu zeichnen! :) stackoverflow.com/a/45228178/294884
Fattie

Dank Fattie habe ich mit wenig Modifikation dafür gesorgt, dass Punkte nach Ansichtshöhe zählen. Lassen Sie totalDynamicDots = bounds.size.height / CGFloat (3); let itemLength = fullHeight / totalDynamicDots
Nitesh

Können wir eine horizontale Version davon haben? @ Fattie
Mohmmad S

Antworten:


94

Stellen Sie den Linienkappenstil auf rund und die Ein-Länge auf eine winzige Zahl ein.

Beispiel für einen schnellen Spielplatz:

import UIKit
import PlaygroundSupport

let path = UIBezierPath()
path.move(to: CGPoint(x:10,y:10))
path.addLine(to: CGPoint(x:290,y:10))
path.lineWidth = 8

let dashes: [CGFloat] = [0.001, path.lineWidth * 2]
path.setLineDash(dashes, count: dashes.count, phase: 0)
path.lineCapStyle = CGLineCap.round

UIGraphicsBeginImageContextWithOptions(CGSize(width:300, height:20), false, 2)

UIColor.white.setFill()
UIGraphicsGetCurrentContext()!.fill(.infinite)

UIColor.black.setStroke()
path.stroke()

let image = UIGraphicsGetImageFromCurrentImageContext()
let view = UIImageView(image: image)
PlaygroundPage.current.liveView = view

UIGraphicsEndImageContext()

Ergebnis:

Punkte


Fügen Sie für Ziel-C unter Verwendung derselben Beispielklasse wie in der Frage einfach hinzu

CGContextSetLineCap(cx, kCGLineCapRound);

vor dem Aufruf von CGContextStrokePathund ändern Sie die raArray-Werte so, dass sie meinem Swift-Code entsprechen.


9
Die wichtigsten Informationen befinden sich in der ersten Zeile (englischer Text) meiner Antwort. Der Rest ist Soße.
Rob Mayoff

5
Ich habe festgestellt, dass das Einstellen der Ein-Länge 0.01einen kreisförmigen Punkt ergibt, während sie bei Verwendung leicht verlängert sind 0.
James P

Ich habe dieses Bild durch einen Screenshot erstellt (systemweite Standardverknüpfung: ⌘⇧4). Es gab noch nie eine integrierte Erfassungsfunktion in Xcode, von der ich weiß.
Rob Mayoff

2
Der Rat von James P ist von unschätzbarem Wert. Dieser Fehler hat mir so viel Herzschmerz und Arbeit verursacht. Danke James. Ich werde eine Halbantwort erstellen, damit die Leute sie klarer sehen können.
Womble

1
Ich habe meine Antwort mit der Fehlerumgehung und der neuesten Swift-Syntax aktualisiert.
Rob Mayoff

13

Objective-C-Version des obigen Swift-Beispiels:

UIBezierPath * path = [[UIBezierPath alloc] init];
[path moveToPoint:CGPointMake(10.0, 10.0)];
[path addLineToPoint:CGPointMake(290.0, 10.0)];
[path setLineWidth:8.0];
CGFloat dashes[] = { path.lineWidth, path.lineWidth * 2 };
[path setLineDash:dashes count:2 phase:0];
[path setLineCapStyle:kCGLineCapRound];
UIGraphicsBeginImageContextWithOptions(CGSizeMake(300, 20), false, 2);
[path stroke];
UIImage * image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

11

Bei Verwendung einer UIView-Erweiterung, die mit Swift 3.0 kompatibel ist, sollte Folgendes funktionieren:

extension UIView {

    func addDashedBorder(strokeColor: UIColor, lineWidth: CGFloat) {
        self.layoutIfNeeded()
        let strokeColor = strokeColor.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 = strokeColor
        shapeLayer.lineWidth = lineWidth
        shapeLayer.lineJoin = kCALineJoinRound

        shapeLayer.lineDashPattern = [5,5] // adjust to your liking
        shapeLayer.path = UIBezierPath(roundedRect: CGRect(x: 0, y: 0, width: shapeRect.width, height: shapeRect.height), cornerRadius: self.layer.cornerRadius).cgPath

        self.layer.addSublayer(shapeLayer)
    }

}

Führen Sie dann in einer Funktion, die danach ausgeführt viewDidLoadwird viewDidLayoutSubviews, die addDashedBorderFunktion in der betreffenden Ansicht aus:

class ViewController: UIViewController {

    var someView: UIView!

    override func viewDidLoad() {
        super.viewDidLoad()

        someView = UIView()
        someView.layer.cornerRadius = 5.0

        view.addSubview(someView)

        someView.translatesAutoresizingMaskIntoConstraints = false
        someView.widthAnchor.constraint(equalToConstant: 200).isActive = true
        someView.heightAnchor.constraint(equalToConstant: 200).isActive = true
        someView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
        someView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
    }

    override func viewDidLayoutSubviews() {
        someView.addDashedBorder(strokeColor: UIColor.red, lineWidth: 1.0)
    }

}

1
Dadurch wird eine gestrichelte Linie (dh Rechtecke) erstellt. Wie wird jedoch eine gepunktete Linie (dh Kreise) erstellt?
Crashalot

3

Ich arbeite ein wenig an der von Rob Mayoff akzeptierten Lösung, um die gepunktete Linie einfach anzupassen:

  • Ändern Sie den Radius jedes Kreises.
  • Ändern Sie die Anzahl der Leerzeichen zwischen 2 Kreisen.
  • Ändern Sie die Anzahl der zu generierenden Muster.

Die Funktion gibt ein UIImage zurück:

extension UIImage {

    class func dottedLine(radius radius: CGFloat, space: CGFloat, numberOfPattern: CGFloat) -> UIImage {


        let path = UIBezierPath()
        path.moveToPoint(CGPointMake(radius/2, radius/2))
        path.addLineToPoint(CGPointMake((numberOfPattern)*(space+1)*radius, radius/2))
        path.lineWidth = radius

        let dashes: [CGFloat] = [path.lineWidth * 0, path.lineWidth * (space+1)]
        path.setLineDash(dashes, count: dashes.count, phase: 0)
        path.lineCapStyle = CGLineCap.Round


        UIGraphicsBeginImageContextWithOptions(CGSizeMake((numberOfPattern)*(space+1)*radius, radius), false, 1)
        UIColor.whiteColor().setStroke()
        path.stroke()
        let image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()

        return image

    }
}

Und so erhalten Sie das Bild:

UIImage.dottedLine(radius: 100, space: 2, numberOfPattern: 1)

3

Hallo Leute, diese Lösung hat bei mir gut funktioniert. Ich habe irgendwo gefunden und ein bisschen geändert, um Konsolenwarnungen zu verhindern.

extension UIImage {
    static func drawDottedImage(width: CGFloat, height: CGFloat, color: UIColor) -> UIImage {
        let path = UIBezierPath()
        path.move(to: CGPoint(x: 1.0, y: 1.0))
        path.addLine(to: CGPoint(x: width, y: 1))
        path.lineWidth = 1.5           
        let dashes: [CGFloat] = [path.lineWidth, path.lineWidth * 5]
        path.setLineDash(dashes, count: 2, phase: 0)
        path.lineCapStyle = .butt
        UIGraphicsBeginImageContextWithOptions(CGSize(width: width, height: height), false, 2)
        color.setStroke()
        path.stroke()

        let image: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
        UIGraphicsEndImageContext()

        return image
    }
}

Das ist das Ergebnis:

Ergebnis


2

Keine vollständige Antwort, nur ein sehr wichtiger Punkt , den James P in einem Kommentar zur Lieblingsantwort angesprochen hat:

Er schrieb:

Ich habe festgestellt, dass das Einstellen der Ein-Länge auf 0,01 einen kreisförmigen Punkt ergibt, während sie bei Verwendung von 0 leicht verlängert sind.

Beispielsweise,

   let dashes: [CGFloat] = [0.001, path.lineWidth * 2]

0

In Swift 3.1 können Sie den folgenden Code verwenden:

context.setLineCap(.round)

Haben Sie drei Stile:

 /* Line cap styles. */

public enum CGLineCap : Int32 {

    case butt

    case round

    case square
}

0

Funktioniert gut mit dem folgenden Code,

layer.path = linePath.cgPath
layer.lineWidth = 3
layer.lineDashPattern = [1,layer.lineWidth*2] as [NSNumber]
layer.lineCap = "round"

0

Hey, es ist vielleicht zu spät, um diese Frage zu beantworten. Aber wenn Sie zustimmen, möchte ich den Entwicklern, die möglicherweise in Zukunft mit diesem Problem konfrontiert werden, einen einfachen Weg zur Lösung dieses Problems vorstellen. Ich denke also, dass die einfachste Lösung mit @IBDesignable. Sie müssen nur diese Klasse erstellen

import UIKit

@IBDesignable class DottedVertical: UIView {

    @IBInspectable var dotColor: UIColor = UIColor.red
    @IBInspectable var lowerHalfOnly: Bool = false

    override func draw(_ rect: CGRect) {

        // say you want 8 dots, with perfect fenceposting:
        let totalCount = 8 + 8 - 1
        let fullHeight = bounds.size.height
        let width = bounds.size.width
        let itemLength = fullHeight / CGFloat(totalCount)

        let path = UIBezierPath()

        let beginFromTop = CGFloat(0.0)
        let top = CGPoint(x: width/2, y: beginFromTop)
        let bottom = CGPoint(x: width/2, y: fullHeight)

        path.move(to: top)
        path.addLine(to: bottom)

        path.lineWidth = width
        //DASHED SIMPLE LINE
        //let dashes: [CGFloat] = [itemLength, itemLength]
        //path.setLineDash(dashes, count: dashes.count, phase: 0)

        // for ROUNDED dots, simply change to....
        let dashes: [CGFloat] = [0.0, itemLength * 1.1]
        path.lineCapStyle = CGLineCap.round
        path.setLineDash(dashes, count: dashes.count, phase: 0)

        dotColor.setStroke()
        path.stroke()
    }
}

Und hängen Sie es dann so an Ihre Ansicht im Storyboard an Geben Sie hier die Bildbeschreibung ein

Sobald Sie fertig sind, let dashes: [CGFloat] = [0.0, itemLength * 1.1] passen Sie den Abstand zwischen den Ebenen dieser Zeile -> Zeile 39 in der DottedVertical-Klasse kalt an. Wenn Sie die Breite der Ebene anpassen möchten, müssen Sie nur die Breite der Linienansicht in Ihrem Storyboard bearbeiten


-1

Ich habe den folgenden Code implementiert, um einen Rand mit gepunktetem Stil am unteren Rand von titleLabel( UILabel) in hinzuzufügen viewDidAppear:

CAShapeLayer *shapelayer = [CAShapeLayer layer];
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(0.0, titileLabel.frame.size.height-2)];
[path addLineToPoint:CGPointMake(SCREEN_WIDTH, titileLabel.frame.size.height-2)];
UIColor *fill = [UIColor colorWithRed:0.80f green:0.80f blue:0.80f alpha:1.00f];
shapelayer.strokeStart = 0.0;
shapelayer.strokeColor = fill.CGColor;
shapelayer.lineWidth = 2.0;
shapelayer.lineJoin = kCALineJoinRound;
shapelayer.lineDashPattern = [NSArray arrayWithObjects:[NSNumber numberWithInt:2],[NSNumber numberWithInt:3 ], nil];
shapelayer.path = path.CGPath;

[titileLabel.layer addSublayer:shapelayer];

Aktualisierung: https://gist.github.com/kaiix/4070967


Dies erzeugt Quadrate, keine Kreise, wenn ich Ihren Code verwende.
Hallo B

Versuchen Sie, die Werte für moveToPoint, addLineToPoint, linewidth und lineDashPattern gemäß Ihren Anforderungen zu
ändern
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.