Za pomocą UIKit łatwo jest narysować linię przerywaną . Więc:
CGFloat dashes[] = {4, 2};
[path setLineDash:dashes count:2 phase:0];
[path stroke];
Czy istnieje sposób na narysowanie prawdziwej przerywanej linii?
Jakieś pomysły?
Ponieważ to pytanie jest naprawdę stare i nikt nie umieścił pełnego @IBDesignable
rozwiązania, oto jest ...
Mam nadzieję, że oszczędza to komuś pisania.
@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()
}
}
Zrobiłem to pionowo, możesz łatwo zmienić.
Po prostu umieść UIView na scenie; ustaw dowolną szerokość, a będzie to szerokość przerywanej linii.
Po prostu zmień klasę na DottedVertical
i gotowe. Będzie to poprawnie renderowane w scenorysie.
Zauważ, że przykładowy kod podany dla wysokości bloków ("totalCount" itd.) Daje w wyniku bloki idealnie, co do piksela, pasujące do końców UIView, które tworzy linię.
Pamiętaj, aby zaznaczyć poniżej odpowiedź RobMayoffa, która zawiera dwa potrzebne wiersze kodu dla kropek-nie-bloków.
Odpowiedzi:
Ustaw styl zakończenia linii na zaokrąglony i ustaw długość „włączenia” na niewielką liczbę.
Przykład szybkiego placu zabaw:
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()
Wynik:
Dla celu-C, używając tej samej przykładowej klasy co w pytaniu, po prostu dodaj
CGContextSetLineCap(cx, kCGLineCapRound);
przed wywołaniem funkcji
CGContextStrokePath
i zmieńra
wartości tablicy, aby pasowały do mojego kodu Swift.źródło
0.01
daje okrągłą kropkę, podczas gdy są one nieco wydłużone podczas używania0
.Wersja Objective-C powyższego przykładu Swift:
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();
źródło
Korzystając z rozszerzenia UIView, zgodnego ze Swift 3.0, powinny działać następujące elementy:
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) } }
Następnie w funkcji, która działa po
viewDidLoad
, na przykładviewDidLayoutSubviews
, uruchomaddDashedBorder
funkcję w danym widoku: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) } }
źródło
Witam, to rozwiązanie zadziałało dobrze. Znalazłem gdzieś i trochę zmieniłem, aby zapobiec ostrzeżeniom konsoli.
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 } }
Oto wynik:
źródło
Trochę pracuję nad rozwiązaniem przyjętym przez Rob Mayoffa, aby łatwo dostosować przerywaną linię:
Funkcja zwraca 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 } }
A oto jak uzyskać obraz:
UIImage.dottedLine(radius: 100, space: 2, numberOfPattern: 1)
źródło
Nie jest to pełna odpowiedź, tylko bardzo ważna odpowiedź, którą James P poruszył w komentarzu do ulubionej odpowiedzi:
On napisał:
Na przykład,
let dashes: [CGFloat] = [0.001, path.lineWidth * 2]
źródło
W Swift 3.1 możesz użyć poniższego kodu:
Masz trzy style:
/* Line cap styles. */ public enum CGLineCap : Int32 { case butt case round case square }
źródło
Działa dobrze z poniższym kodem,
layer.path = linePath.cgPath layer.lineWidth = 3 layer.lineDashPattern = [1,layer.lineWidth*2] as [NSNumber] layer.lineCap = "round"
źródło
Hej, może jest już za późno, aby odpowiedzieć na to pytanie. ale jeśli się zgodzisz, chciałbym podzielić się prostym sposobem rozwiązania tego problemu z programistami, którzy być może zmierzą się z tym problemem w przyszłości. więc myślę, że najłatwiejsze rozwiązanie przy użyciu @IBDesignable. Musisz tylko stworzyć tę klasę
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() } }
A następnie dołącz go do swojego widoku w scenorysie w ten sposób
Gdy skończysz, dostosuj przestrzeń między warstwami z tej linii
let dashes: [CGFloat] = [0.0, itemLength * 1.1]
-> Linia 39 w klasie DottedVertical. lub jeśli chcesz dostosować szerokość warstwy, której potrzebujesz, aby edytować szerokość widoku linii z serii ujęćźródło
Zaimplementowałem następujący fragment kodu, aby dodać obramowanie ze stylem kropkowanym na dole
titleLabel
(UILabel
) wviewDidAppear
: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];
Odniesienie: https://gist.github.com/kaiix/4070967
źródło