Gambar garis putus-putus (tidak putus-putus!), Dengan IBDesignable pada tahun 2017

86

Sangat mudah untuk menggambar garis putus - putus dengan UIKit. Begitu:

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

masukkan deskripsi gambar di sini

Apakah ada cara untuk menggambar garis putus-putus yang asli?

masukkan deskripsi gambar di sini

Ada ide?


Karena pertanyaan ini benar-benar tua dan tidak ada yang dimasukkan secara lengkap @IBDesignable solusi , ini dia ...

Semoga ini menghemat waktu mengetik seseorang.

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

Saya membuatnya vertikal, Anda dapat dengan mudah mengubahnya.

masukkan deskripsi gambar di sini

Taruh saja UIView di tempat kejadian; buat lebar apa pun yang Anda inginkan dan itu akan menjadi lebar garis putus-putus.

Cukup ubah kelas menjadi DottedVerticaldan Anda selesai. Ini akan ditampilkan seperti itu dengan benar di storyboard.

masukkan deskripsi gambar di sini

Perhatikan bahwa kode contoh yang diberikan untuk tinggi blok ("totalCount" dan seterusnya ...) menghasilkan blok dengan sempurna, ke piksel, cocok dengan ujung UIView yang membuat garis.

Pastikan untuk mencentang jawaban RobMayoff di bawah ini yang memberikan dua baris kode yang diperlukan untuk titik-bukan-blok.

Gendut
sumber
inilah cara yang fantastis untuk menggambar garis diagonal! :) stackoverflow.com/a/45228178/294884
Fattie
Terima kasih Fattie, saya menggunakan sedikit modifikasi untuk membuat titik dihitung sesuai ketinggian tampilan, biarkan totalDynamicDots = bounds.size.height / CGFloat (3); biarkan itemLength = fullHeight / totalDynamicDots
Nitesh
Bisakah kita memiliki versi horizontal ini? @Fattie
Mohmmad S

Jawaban:

97

Atur gaya tutup garis menjadi bulat dan atur panjang "on" menjadi angka kecil.

Contoh taman bermain cepat:

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

Hasil:

titik


Untuk objektif-C, menggunakan kelas contoh yang sama seperti dalam pertanyaan, cukup tambahkan

CGContextSetLineCap(cx, kCGLineCapRound);

sebelum panggilan ke CGContextStrokePath, dan ubah nilai raarray agar sesuai dengan kode Swift saya.

merampok mayoff
sumber
9
Informasi kuncinya ada di baris pertama (teks bahasa Inggris) jawaban saya. Sisanya adalah kuah.
merampok mayoff
5
Saya telah menemukan pengaturan pada panjang 0.01memberi Anda titik melingkar, sedangkan mereka sedikit memanjang saat menggunakan 0.
James P
Saya membuat gambar itu dengan mengambil cuplikan layar (pintasan seluruh sistem default: ⌘⇧4). Tidak pernah ada fasilitas penangkapan built-in di Xcode yang saya tahu.
merampok mayoff
2
Nasihat James P sangat berharga. Bug ini telah menyebabkan saya sangat sakit hati dan bekerja. Terima kasih James. Saya akan membuat semi-Answer, sehingga orang dapat melihatnya dengan lebih jelas.
Womble
1
Saya telah memperbarui jawaban saya dengan solusi bug dan sintaks Swift terbaru.
merampok mayoff
13

Versi Objective-C dari contoh Swift di atas:

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();
devgeek
sumber
11

Menggunakan ekstensi UIView, kompatibel dengan Swift 3.0, berikut ini seharusnya berfungsi:

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

}

Kemudian dalam fungsi yang berjalan setelah viewDidLoad, seperti viewDidLayoutSubviews, jalankan addDashedBorderfungsi pada tampilan yang dimaksud:

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

}
Alex
sumber
1
Ini menciptakan garis putus-putus (yaitu, persegi panjang), tetapi bagaimana Anda membuat garis putus-putus (yaitu, lingkaran)?
Crashalot
4

Halo teman-teman, solusi ini bekerja untuk saya dengan baik. Saya menemukan suatu tempat dan mengubah sedikit untuk mencegah peringatan konsol.

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

Inilah hasilnya:

hasil

Vadims Krutovs
sumber
3

Saya bekerja sedikit pada solusi yang diterima rob mayoff untuk dengan mudah menyesuaikan garis putus-putus:

  • ubah radius setiap lingkaran.
  • ubah jumlah spasi antara 2 lingkaran.
  • ubah jumlah pola yang akan dihasilkan.

Fungsi mengembalikan 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

    }
}

Dan inilah cara mendapatkan gambarnya:

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

Bukan jawaban lengkap, hanya jawaban yang sangat penting yang yang dikemukakan James P dalam komentar pada jawaban favorit:

Dia menulis:

Saya telah menemukan pengaturan pada panjang ke 0,01 memberi Anda titik melingkar, sedangkan mereka sedikit memanjang saat menggunakan 0.

Sebagai contoh,

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

Di swift 3.1 Anda dapat menggunakan kode di bawah ini:

context.setLineCap(.round)

Memiliki tiga gaya:

 /* Line cap styles. */

public enum CGLineCap : Int32 {

    case butt

    case round

    case square
}
shujucn
sumber
0

Bekerja dengan baik dengan kode di bawah ini,

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

Hei, mungkin sudah terlambat untuk menjawab pertanyaan itu. tetapi jika Anda setuju, saya ingin membagikan cara mudah untuk menyelesaikannya kepada pengembang yang mungkin akan menghadapi masalah itu di masa mendatang. jadi saya rasa itu solusi termudah menggunakan @IBDesignable. Anda hanya perlu membuat kelas itu

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

Dan kemudian tambahkan ke tampilan Anda di papan cerita seperti itu masukkan deskripsi gambar di sini

Setelah Anda selesai, Anda dapat menyesuaikan ruang antara lapisan dari baris ini let dashes: [CGFloat] = [0.0, itemLength * 1.1] -> Baris 39 di kelas DottedVertical. atau jika Anda ingin menyesuaikan lebar layer, Anda hanya perlu mengedit lebar tampilan garis dari storyboard Anda

Bahri Eskander
sumber
-1

Saya telah mengimplementasikan potongan kode berikut untuk menambahkan batas dengan gaya titik-titik di bagian bawah titleLabel( UILabel) di 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];

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

iAkshay
sumber
ini menghasilkan kotak, bukan lingkaran, saat saya menggunakan kode Anda.
haloB
coba ubah nilai moveToPoint, addLineToPoint, linewidth dan lineDashPattern sesuai kebutuhan Anda
iAkshay