Vẽ đường chấm (không đứt nét!), Với IBDesignable vào năm 2017


86

Thật dễ dàng để vẽ một đường đứt nét với UIKit. Vì thế:

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

nhập mô tả hình ảnh ở đây

Có cách nào để vẽ một đường chấm chính xác?

nhập mô tả hình ảnh ở đây

Có ý kiến ​​gì không?


Vì câu hỏi này thực sự cũ và không ai đưa ra được @IBDesignablegiải pháp đầy đủ , đây là ...

Hy vọng nó sẽ tiết kiệm cho ai đó một số đánh máy.

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

Tôi đã làm cho nó theo chiều dọc, bạn có thể dễ dàng thay đổi.

nhập mô tả hình ảnh ở đây

Chỉ cần đặt một UIView trong cảnh; làm cho nó bất kỳ chiều rộng nào bạn muốn và đó sẽ là chiều rộng của đường chấm.

Chỉ cần thay đổi lớp thành DottedVerticalvà bạn đã hoàn tất. Nó sẽ hiển thị như vậy đúng trong bảng phân cảnh.

nhập mô tả hình ảnh ở đây

Lưu ý rằng mã ví dụ được cung cấp cho chiều cao của các khối ("totalCount", v.v.) dẫn đến các khối hoàn hảo, đến pixel, khớp với các phần cuối của UIView đang tạo dòng.

Hãy nhớ đánh dấu vào câu trả lời của RobMayoff bên dưới để cung cấp hai dòng mã cần thiết cho các dấu chấm-không-khối.


đây là một cách tuyệt vời để vẽ các đường chéo! :) stackoverflow.com/a/45228178/294884
Fattie

Cảm ơn Fattie, tôi đã sử dụng với một chút sửa đổi để làm cho số chấm được tính theo chiều cao của chế độ xem, hãy để totalDynamicDots = bounds.size.height / CGFloat (3); let itemLength = fullHeight / totalDynamicDots
Nitesh

chúng ta có thể có một phiên bản ngang của cái này không? @Fattie
Mohmmad S

Câu trả lời:


97

Đặt kiểu nắp dòng thành tròn và đặt độ dài “bật” thành một số nhỏ.

Ví dụ về sân chơi Swift:

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

Kết quả:

dấu chấm


Đối với mục tiêu-C, sử dụng cùng một lớp ví dụ như trong câu hỏi, chỉ cần thêm

CGContextSetLineCap(cx, kCGLineCapRound);

trước lệnh gọi tới CGContextStrokePathvà thay đổi các ragiá trị mảng để khớp với mã Swift của tôi.


9
Thông tin quan trọng nằm trong dòng đầu tiên (văn bản tiếng Anh) trong câu trả lời của tôi. Phần còn lại là nước thịt.
cướp mayoff

5
Tôi thấy việc thiết lập độ dài trên để 0.01cung cấp cho bạn một chấm tròn, trong khi chúng hơi dài ra khi sử dụng 0.
James P

Tôi đã tạo hình ảnh đó bằng cách chụp ảnh màn hình (phím tắt mặc định trên toàn hệ thống: ⌘⇧4). Tôi chưa bao giờ biết đến một phương tiện chụp ảnh tích hợp sẵn trong Xcode.
cướp mayoff

2
Lời khuyên của James P là vô giá. Con bọ này đã khiến tôi đau tim và làm việc rất nhiều. Cảm ơn James. Tôi sẽ tạo một câu trả lời bán phần, để mọi người có thể thấy rõ hơn.
Womble

1
Tôi đã cập nhật câu trả lời của mình với cách giải quyết lỗi và cú pháp Swift mới nhất.
cướp mayoff

13

Phiên bản Objective-C của ví dụ Swift ở trên:

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

Sử dụng tiện ích mở rộng UIView, tương thích với Swift 3.0, những điều sau sẽ hoạt động:

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

}

Sau đó, trong một hàm chạy sau viewDidLoad, như viewDidLayoutSubviews, chạy addDashedBorderhàm trên dạng xem được đề cập:

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
Điều này tạo ra một đường đứt nét (tức là hình chữ nhật), nhưng làm thế nào để bạn tạo một đường chấm (tức là, hình tròn)?
Crashalot

4

Xin chào các bạn giải pháp này đã làm việc cho tôi tốt. Tôi đã tìm thấy ở đâu đó và đã thay đổi một chút để ngăn cảnh báo trên bảng điều khiển.

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

Đây là kết quả:

kết quả


3

Tôi làm việc một chút về giải pháp được chấp nhận của rob mayoff để dễ dàng tùy chỉnh đường chấm:

  • thay đổi bán kính của mỗi vòng tròn.
  • thay đổi số khoảng cách giữa 2 vòng tròn.
  • thay đổi số lượng mẫu để tạo.

Hàm trả về một 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

    }
}

Và đây là cách lấy hình ảnh:

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

2

Không phải là một câu trả lời đầy đủ, chỉ là một câu trả lời rất quan trọng mà James P nêu ra trong một bình luận về câu trả lời yêu thích:

Ông đã viết:

Tôi thấy việc đặt độ dài trên thành 0,01 sẽ cho bạn một chấm tròn, trong khi chúng hơi dài ra khi sử dụng 0.

Ví dụ,

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

0

Trong swift 3.1, bạn có thể sử dụng mã dưới đây:

context.setLineCap(.round)

Có ba phong cách:

 /* Line cap styles. */

public enum CGLineCap : Int32 {

    case butt

    case round

    case square
}

0

Hoạt động tốt với mã bên dưới,

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

0

Này, có lẽ đã quá muộn để trả lời câu hỏi đó. nhưng nếu bạn đồng ý, tôi muốn chia sẻ một cách dễ dàng để giải quyết vấn đề đó cho các nhà phát triển có thể sẽ gặp phải vấn đề đó trong tương lai. vì vậy tôi đoán rằng giải pháp đơn giản nhất bằng cách sử dụng @IBDesignable. Bạn chỉ cần tạo lớp đó

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

Và sau đó thêm nó vào chế độ xem của bạn trong bảng phân cảnh như vậy nhập mô tả hình ảnh ở đây

Khi bạn đã hoàn tất, bạn tùy chỉnh lạnh không gian giữa các lớp từ dòng này let dashes: [CGFloat] = [0.0, itemLength * 1.1] -> Dòng 39 trong lớp DottedVertical. hoặc nếu bạn muốn tùy chỉnh chiều rộng của lớp, bạn chỉ cần chỉnh sửa chiều rộng chế độ xem dòng từ bảng phân cảnh của bạn


-1

Tôi đã triển khai đoạn mã sau để thêm đường viền với kiểu chấm chấm ở cuối titleLabel( UILabel) trong 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];

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


điều này tạo ra hình vuông, không phải hình tròn, khi tôi sử dụng mã của bạn.
xin chàoB

hãy thử thay đổi moveToPoint, addLineToPoint, độ rộng vạch phổ và lineDashPattern giá trị theo yêu cầu của bạn
iAkshay
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.