2017年にIBDesignableを使用して、点線(破線ではありません!)を描画します


86

UIKitで破線を描くのは簡単です。そう:

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

ここに画像の説明を入力してください

本物の点線を描く方法はありますか?

ここに画像の説明を入力してください

何か案は?


この質問は本当に古く、誰も完全な@IBDesignable解決策を提示していないので、ここにあります...

それが誰かのタイピングを節約することを願っています。

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

縦にしたので、簡単に交換できます。

ここに画像の説明を入力してください

UIViewをシーンに配置するだけです。好きな幅にすると、点線の幅になります。

クラスをに変更するだけでDottedVertical完了です。ストーリーボードではそのように適切にレンダリングされます。

ここに画像の説明を入力してください

ブロックの高さ(「totalCount」など)に指定されたサンプルコードは、行を作成しているUIViewの端と一致する、ピクセルに対して完全にブロックを生成することに注意してください。

以下のRobMayoffの回答にチェックマークを付けてください。これは、ブロックではなくドットに必要な2行のコードを示しています。


これが対角線を描く素晴らしい方法です!:) stackoverflow.com/a/45228178/294884
Fattie 2018

Fattieに感謝します。ビューの高さごとにドットをカウントするように少し変更を加えて使用しました。totalDynamicDots= bounds.size.height / CGFloat(3); itemLength = fullHeight / totalDynamicDots
Nitesh

これの水平バージョンを使用できますか?@Fattie
MohmmadS19年

回答:


97

ラインキャップスタイルを丸く設定し、「オン」の長さを小さな数に設定します。

スウィフトプレイグラウンドの例:

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

結果:

ドット


Objective-Cの場合、質問と同じサンプルクラスを使用して、単に追加します

CGContextSetLineCap(cx, kCGLineCapRound);

を呼び出す前に、Swiftコードに一致CGContextStrokePathするようにra配列値を変更します。


9
重要な情報は私の答えの最初の(英語のテキスト)行にあります。残りは肉汁です。
rob mayoff 2014年

5
onの長さを設定する0.01と、円形のドットが得られますが、を使用するとわずかに長くなり0ます。
James P

スクリーンショットを撮ってその画像を作成しました(デフォルトのシステム全体のショートカット:⌘⇧4)。私が知っているXcodeに組み込まれたキャプチャ機能はありませんでした。
rob mayoff 2016年

2
ジェームズPのアドバイスは非常に貴重です。このバグは私に多くの心の痛みと仕事を引き起こしました。ジェームスありがとう。セミアンサーを作成して、人々がよりはっきりと見えるようにします。
ウォンブルズ2018

1
バグの回避策と最新のSwift構文で回答を更新しました。
rob mayoff 2018

13

上記のSwiftの例のObjective-Cバージョン:

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

Swift 3.0と互換性のあるUIView拡張機能を使用すると、以下が機能するはずです。

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

}

そして、関数内で実行した後ことviewDidLoad、のようなviewDidLayoutSubviews、実行addDashedBorder問題のビューの機能を:

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
これにより、破線(つまり、長方形)が作成されますが、点線(つまり、円)をどのように作成しますか?
Crashalot 2016

4

こんにちはみんなこのソリューションは私のためにうまくいきました。私はどこかを見つけて、コンソールの警告を防ぐために少し変更しました。

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

結果は次のとおりです。

結果


3

点線を簡単にカスタマイズするために、robmayoffが受け入れたソリューションに少し取り組んでいます。

  • 各円の半径を変更します。
  • 2つの円の間のスペースの数を変更します。
  • 生成するパターンの数を変更します。

この関数はUIImageを返します。

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

    }
}

そして、これが画像を取得する方法です:

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

2

完全な答えではなく、JamesPがお気に入りの答えに対するコメントで提起した非常に重要な落とし穴です。

彼が書きました:

オンの長さを0.01に設定すると円形のドットが得られますが、0を使用するとわずかに長くなります。

例えば、

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

0

swift 3.1では、以下のコードを使用できます。

context.setLineCap(.round)

3つのスタイルがあります:

 /* Line cap styles. */

public enum CGLineCap : Int32 {

    case butt

    case round

    case square
}

0

以下のコードで正常に動作し、

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

0

ねえ、その質問に答えるには遅すぎるかもしれません。しかし、あなたが同意するなら、私はそれを解決する簡単な方法を、将来その問題に直面するかもしれない開発者に共有したいと思います。だから私は@IBDesignableを使用する最も簡単な解決策だと思います。そのクラスを作成するだけです

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

そして、そのようにストーリーボードのビューに追加します ここに画像の説明を入力してください

完了したら、この行からレイヤー間のスペースをコールドカスタマイズしますlet dashes: [CGFloat] = [0.0, itemLength * 1.1] -> DottedVerticalクラスの39行目。または、レイヤーの幅をカスタマイズする場合は、ストーリーボードからラインビューの幅を編集するだけです。


-1

次のコードを実装して、titleLabelUILabel)の下部にドットスタイルの境界線を追加しました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];

参照:https://gist.github.com/kaiix/4070967


私があなたのコードを使用するとき、これは円ではなく正方形を生成しています。
helloB 2016年

要件に応じて、moveToPoint、addLineToPoint、linewidth、lineDashPatternの値を変更してみてください
iAkshay 2016年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.