วาดเส้นประ (ไม่ประ!) ด้วย IBDesignable ในปี 2560


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 ด้านล่างซึ่งจะให้รหัสสองบรรทัดที่จำเป็นสำหรับจุดที่ไม่บล็อก


นี่คือวิธีที่ยอดเยี่ยมในการวาดเส้นทแยงมุม! :) stackoverflow.com/a/45228178/294884
Fattie

ขอบคุณ Fattie ฉันใช้กับการปรับเปลี่ยนเล็กน้อยเพื่อให้จุดนับตามความสูงของมุมมองให้ totalDynamicDots = bounds.size.height / CGFloat (3); ให้ itemLength = fullHeight / totalDynamicDots
Nitesh

เรามีเวอร์ชันแนวนอนได้ไหม @Fattie
Mohmmad S

คำตอบ:


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

ผลลัพธ์:

จุด


สำหรับวัตถุประสงค์ -C โดยใช้คลาสตัวอย่างเดียวกับในคำถามให้เพิ่ม

CGContextSetLineCap(cx, kCGLineCapRound);

ก่อนการโทรCGContextStrokePathและเปลี่ยนraค่าอาร์เรย์ให้ตรงกับรหัส Swift ของฉัน


9
ข้อมูลสำคัญอยู่ในบรรทัดแรก (ข้อความภาษาอังกฤษ) ของคำตอบของฉัน ที่เหลือเป็นน้ำเกรวี่
rob mayoff

5
ฉันได้พบการตั้งค่าความยาวที่จะช่วยให้คุณมีจุดกลมในขณะที่พวกเขาจะยืดออกเล็กน้อยเมื่อใช้0.01 0
James P

ฉันสร้างภาพนั้นโดยการถ่ายภาพหน้าจอ (ทางลัดเริ่มต้นทั้งระบบ: ⌘⇧4) ไม่เคยมีอุปกรณ์จับภาพในตัวใน Xcode ที่ฉันรู้จัก
rob mayoff

2
คำแนะนำของ James P มีค่ามาก ข้อบกพร่องนี้ทำให้ฉันปวดหัวใจและทำงานมาก ขอบคุณเจมส์. ฉันจะสร้างกึ่งตอบเพื่อให้ทุกคนเห็นชัดเจนขึ้น
Womble

1
ฉันได้อัปเดตคำตอบของฉันด้วยวิธีแก้ไขข้อบกพร่องและไวยากรณ์ล่าสุดของ Swift
rob mayoff

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

การใช้ส่วนขยาย UIView เข้ากันได้กับ Swift 3.0 สิ่งต่อไปนี้ควรใช้งานได้:

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

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

ฉันทำงานเล็กน้อยกับโซลูชันที่ยอมรับของ rob mayoff เพื่อปรับแต่งเส้นประ:

  • เปลี่ยนรัศมีของแต่ละวงกลม
  • เปลี่ยนจำนวนช่องว่างระหว่าง 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

ไม่ใช่คำตอบที่สมบูรณ์เป็นเพียงgotcha ที่สำคัญมากที่ James P ยกขึ้นในความคิดเห็นเกี่ยวกับคำตอบที่ชื่นชอบ:

เขาเขียน:

ฉันพบว่าการตั้งค่าความยาวเป็น0.01จะให้จุดวงกลมแก่คุณในขณะที่พวกมันจะยาวขึ้นเล็กน้อยเมื่อใช้ 0

ตัวอย่างเช่น,

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

0

ใน swift 3.1 คุณสามารถใช้โค้ดด้านล่าง:

context.setLineCap(.round)

มีสามสไตล์:

 /* 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] -> บรรทัด 39 ในคลาส DottedVertical หรือหากคุณต้องการปรับแต่งความกว้างของเลเยอร์คุณเพียงแค่แก้ไขความกว้างมุมมองเส้นจากสตอรี่บอร์ดของคุณ


-1

ฉันได้ติดตั้งโค้ดต่อไปนี้เพื่อเพิ่มเส้นขอบด้วยลักษณะจุดที่ด้านล่างของtitleLabel( UILabel) ใน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

ลองเปลี่ยนค่า moveToPoint, addLineToPoint, linewidth และ lineDashPattern ตามความต้องการของคุณ
iAkshay
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.