UIView con una línea discontinua

Lo que tengo:

enter image description here

Para crear esta línea, básicamente tengo una UIView y hago lo siguiente:

 void setLayerToLineFromAToB(CALayer *layer, CGPoint a, CGPoint b, CGFloat lineWidth) { CGPoint center = { 0.5 * (ax + bx), 0.5 * (ay + by) }; CGFloat length = sqrt((ax - bx) * (ax - bx) + (ay - by) * (ay - by)); CGFloat angle = atan2(ay - by, ax - bx); layer.position = center; layer.bounds = (CGRect) { {0, 0}, { length + lineWidth, lineWidth } }; layer.transform = CATransform3DMakeRotation(angle, 0, 0, 1); } 

Nota: Este código se encontró aquí en stackoverflow, así que si alguien puede darme la referencia, lo agradecería.

Lo que quiero:

enter image description here

Ok, entonces lo “único” que necesito es crear este patrón en UIView . Sé que puedo hacer esto usando Quartz2D (se puede encontrar una forma sencilla de hacerlo aquí ). Pero quiero hacerlo manipulando el CALayer y no yendo al método de sorteo. ¿Por qué? Debido a la transformación que estoy haciendo en mi UIView , no puedo dibujar correctamente usando el método de draw .

Editar 1:

Solo para ilustrar mi problema:

enter image description here

Normalmente, lo que tienes es UIView y luego básicamente UIView algo (en este caso, una línea simple). La solución que encontré para deshacerme del área “gris” fue, en lugar de dibujar algo, simplemente transformar UIView . Funciona bien, si quieres una línea completamente llena, el problema viene cuando quieres una línea punteada.

Compruebe UIBezierPath setLineDash:count:phase: method :

 - (void)setLineDash:(const CGFloat *)pattern count:(NSInteger)count phase:(CGFloat)phase` method. 

Esto le permite dibujar líneas punteadas.

  1. Primero agrega una CAShapeLayer . UIView como subcapa a su UIView . Tiene una propiedad de path .
  2. Ahora haz un objeto de UIBezierPath . Dibuja la línea usando setLineDash .

Por ejemplo:

  UIBezierPath *path = [UIBezierPath bezierPath]; //draw a line [path moveToPoint:yourStartPoint]; //add yourStartPoint here [path addLineToPoint:yourEndPoint];// add yourEndPoint here [path stroke]; CGFloat dashPattern[] = {2.0f,6.0f,4.0f,2.0f}; //make your pattern here [path setLineDash:dashPattern count:4 phase:3]; UIColor *fill = [UIColor blueColor]; shapelayer.strokeStart = 0.0; shapelayer.strokeColor = fill.CGColor; shapelayer.lineWidth = 5.0; shapelayer.lineJoin = kCALineJoinMiter; shapelayer.lineDashPattern = [NSArray arrayWithObjects:[NSNumber numberWithInt:10],[NSNumber numberWithInt:7], nil]; shapelayer.lineDashPhase = 3.0f; shapelayer.path = path.CGPath; 

Nota: Esta respuesta proporciona una pista para que pueda improvisar de acuerdo con sus requisitos.

Nota : El código de Prince realmente me ayudó, así que le daré +10 para las sugerencias. Pero al final, agrego para venir con mi propio código. También agregaré un contexto para que pueda ser útil para futuros lectores


El código final fue así:

 -(void)updateLine{ // Important, otherwise we will be adding multiple sub layers if ([[[self layer] sublayers] objectAtIndex:0]) { self.layer.sublayers = nil; } CAShapeLayer *shapeLayer = [CAShapeLayer layer]; [shapeLayer setBounds:self.bounds]; [shapeLayer setPosition:self.center]; [shapeLayer setFillColor:[[UIColor clearColor] CGColor]]; [shapeLayer setStrokeColor:[[UIColor blackColor] CGColor]]; [shapeLayer setLineWidth:3.0f]; [shapeLayer setLineJoin:kCALineJoinRound]; [shapeLayer setLineDashPattern: [NSArray arrayWithObjects:[NSNumber numberWithInt:10], [NSNumber numberWithInt:5],nil]]; // Setup the path CGMutablePathRef path = CGPathCreateMutable(); CGPathMoveToPoint(path, NULL, beginPoint.center.x, beginPoint.center.y); CGPathAddLineToPoint(path, NULL, endPoint.center.x, endPoint.center.y); [shapeLayer setPath:path]; CGPathRelease(path); [[self layer] addSublayer:shapeLayer]; } 

En mi caso, beginPoint y endPoint son movibles por el usuario, usando KVO. Entonces, cuando uno de ellos se mueve:

 -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { if ([keyPath isEqual:@"position"]) { [self updateLine]; } } 

Jugué mucho con el código de Prince. Intenté el método draw: que agrega una línea delgada entre la línea punteada (un poco raro …) y también lo intenté con initWithFrame: Por sí mismo, su código, sin ninguna modificación, me daría este tipo de errores en la consola:

 : CGContextSaveGState: invalid context 0x0 : CGContextSetLineWidth: invalid context 0x0 : CGContextSetLineJoin: invalid context 0x0 : CGContextSetLineCap: invalid context 0x0 : CGContextSetMiterLimit: invalid context 0x0 : CGContextSetFlatness: invalid context 0x0 : CGContextAddPath: invalid context 0x0 : CGContextDrawPath: invalid context 0x0 : CGContextRestoreGState: invalid context 0x0 

Swift 2.2

dejando esto aquí para ahorrarle tiempo a otros …

 extension UIView { func addDashedLine(color: UIColor = UIColor.lightGrayColor()) { layer.sublayers?.filter({ $0.name == "DashedTopLine" }).map({ $0.removeFromSuperlayer() }) self.backgroundColor = UIColor.clearColor() let cgColor = color.CGColor let shapeLayer: CAShapeLayer = CAShapeLayer() let frameSize = self.frame.size let shapeRect = CGRect(x: 0, y: 0, width: frameSize.width, height: frameSize.height) shapeLayer.name = "DashedTopLine" shapeLayer.bounds = shapeRect shapeLayer.position = CGPoint(x: frameSize.width / 2, y: frameSize.height / 2) shapeLayer.fillColor = UIColor.clearColor().CGColor shapeLayer.strokeColor = cgColor shapeLayer.lineWidth = 1 shapeLayer.lineJoin = kCALineJoinRound shapeLayer.lineDashPattern = [4, 4] let path: CGMutablePathRef = CGPathCreateMutable() CGPathMoveToPoint(path, nil, 0, 0) CGPathAddLineToPoint(path, nil, self.frame.width, 0) shapeLayer.path = path self.layer.addSublayer(shapeLayer) } } 

Aquí está la versión de Swift 3 de la respuesta de Alexandre G https://stackoverflow.com/a/38194152/1800489

 extension UIView { func addDashedLine(color: UIColor = .lightGray) { layer.sublayers?.filter({ $0.name == "DashedTopLine" }).map({ $0.removeFromSuperlayer() }) backgroundColor = .clear let shapeLayer = CAShapeLayer() shapeLayer.name = "DashedTopLine" shapeLayer.bounds = bounds shapeLayer.position = CGPoint(x: frame.width / 2, y: frame.height / 2) shapeLayer.fillColor = UIColor.clear.cgColor shapeLayer.strokeColor = color.cgColor shapeLayer.lineWidth = 1 shapeLayer.lineJoin = kCALineJoinRound shapeLayer.lineDashPattern = [4, 4] let path = CGMutablePath() path.move(to: CGPoint.zero) path.addLine(to: CGPoint(x: frame.width, y: 0)) shapeLayer.path = path layer.addSublayer(shapeLayer) } } 

En primer lugar, todo el mérito recae en RuiAAPeres y Prince, solo estoy encapsulando sus respuestas en un objeto UIView que otros pueden incluir en sus proyectos y usar

 #import  /** * Simple UIView for a dotted line */ @interface H3DottedLine : UIView /** * Set the line's thickness */ @property (nonatomic, assign) CGFloat thickness; /** * Set the line's color */ @property (nonatomic, copy) UIColor *color; /** * Set the length of the dash */ @property (nonatomic, assign) CGFloat dashedLength; /** * Set the gap between dashes */ @property (nonatomic, assign) CGFloat dashedGap; @end @implementation H3DottedLine #pragma mark - Object Lifecycle - (instancetype)init { self = [super init]; if (self) { // Set Default Values _thickness = 1.0f; _color = [UIColor whiteColor]; _dashedGap = 1.0f; _dashedLength = 5.0f; } return self; } #pragma mark - View Lifecycle - (void)layoutSubviews { // Note, this object draws a straight line. If you wanted the line at an angle you simply need to adjust the start and/or end point here. [self updateLineStartingAt:self.frame.origin andEndPoint:CGPointMake(self.frame.origin.x+self.frame.size.width, self.frame.origin.y)]; } #pragma mark - Setters - (void)setThickness:(CGFloat)thickness { _thickness = thickness; [self setNeedsLayout]; } - (void)setColor:(UIColor *)color { _color = [color copy]; [self setNeedsLayout]; } - (void)setDashedGap:(CGFloat)dashedGap { _dashedGap = dashedGap; [self setNeedsLayout]; } - (void)setDashedLength:(CGFloat)dashedLength { _dashedLength = dashedLength; [self setNeedsLayout]; } #pragma mark - Draw Methods -(void)updateLineStartingAt:(CGPoint)beginPoint andEndPoint:(CGPoint)endPoint { // Important, otherwise we will be adding multiple sub layers if ([[[self layer] sublayers] objectAtIndex:0]) { self.layer.sublayers = nil; } CAShapeLayer *shapeLayer = [CAShapeLayer layer]; [shapeLayer setBounds:self.bounds]; [shapeLayer setPosition:self.center]; [shapeLayer setFillColor:[UIColor clearColor].CGColor]; [shapeLayer setStrokeColor:self.color.CGColor]; [shapeLayer setLineWidth:self.thickness]; [shapeLayer setLineJoin:kCALineJoinRound]; [shapeLayer setLineDashPattern:@[@(self.dashedLength), @(self.dashedGap)]]; // Setup the path CGMutablePathRef path = CGPathCreateMutable(); CGPathMoveToPoint(path, NULL, beginPoint.x, beginPoint.y); CGPathAddLineToPoint(path, NULL, endPoint.x, endPoint.y); [shapeLayer setPath:path]; CGPathRelease(path); [[self layer] addSublayer:shapeLayer]; } @end 

Dash Line en Swift4 • Xcode 9

Crate una CAShapeLayer y use lineDashPattern

 extension UIView { func addDashedBorder() { //Create a CAShapeLayer let shapeLayer = CAShapeLayer() shapeLayer.strokeColor = UIColor.red.cgColor shapeLayer.lineWidth = 2 // passing an array with the values [2,3] sets a dash pattern that alternates between a 2-user-space-unit-long painted segment and a 3-user-space-unit-long unpainted segment shapeLayer.lineDashPattern = [2,3] let path = CGMutablePath() path.addLines(between: [CGPoint(x: 0, y: 0), CGPoint(x: self.frame.width, y: 0)]) shapeLayer.path = path layer.addSublayer(shapeLayer) } } 

Uso:

 dashView.addDashedBorder() 

Salida:

enter image description here

La respuesta aceptada tiene un problema de coordenadas. La línea se dibujará a cierta distancia más abajo. Y no puedo entender por qué y cuánta distancia aumenta en la coordenada Y.

Hay una forma de dibujar una línea punteada con la coordenada correcta:

 -(void)drawRect:(CGRect)rect { CGContextBeginPath(cx); CGContextRef cx = UIGraphicsGetCurrentContext(); CGContextSetLineWidth(cx, _thickness); CGContextSetStrokeColorWithColor(cx, _color.CGColor); CGFloat dash[] = {_dashedLength,_dashedGap}; CGContextSetLineDash(cx, 0, dash, 2); // nb "2" == ra count // CGContextSetLineCap(cx, kCGLineCapRound); CGContextMoveToPoint(cx, 0, _thickness); CGContextAddLineToPoint(cx, self.bounds.size.width, _thickness); CGContextStrokePath(cx); CGContextClosePath(cx); } 

Esta respuesta proviene de la línea Dibujar puntos (¡no discontinua!), Con IBDesignable en 2017 . ¡¡¡NO OLVIDE establecer el color de fondo en blanco cuando quiera una línea discontinua negra !! Por defecto, la vista tiene un color de fondo negro, y el color de la línea también es negro, así que pensé que era una línea continua. Me costó medio día descubrirlo. T_T