Cómo dibujar una única línea de punto en iOS

Me preguntaba ¿cuál es la mejor manera de dibujar una sola línea de puntos? Mi objective es dibujar esta línea en una tableViewCell para que se vea como el separador de células nativas. No quiero usar el separador nativo porque quiero hacerlo en un color diferente y en una posición diferente (no en la parte inferior …).

Al principio estaba usando un UIView de 1px y lo coloreé en gris. Pero en las pantallas Retina parece 2px. También intenté usar este método:

- (void)drawLine:(CGPoint)startPoint endPoint:(CGPoint)endPoint inColor:(UIColor *)color { CGMutablePathRef straightLinePath = CGPathCreateMutable(); CGPathMoveToPoint(straightLinePath, NULL, startPoint.x, startPoint.y); CGPathAddLineToPoint(straightLinePath, NULL, endPoint.x, endPoint.y); CAShapeLayer *shapeLayer = [CAShapeLayer layer]; shapeLayer.path = straightLinePath; UIColor *fillColor = color; shapeLayer.fillColor = fillColor.CGColor; UIColor *strokeColor = color; shapeLayer.strokeColor = strokeColor.CGColor; shapeLayer.lineWidth = 0.5f; shapeLayer.fillRule = kCAFillRuleNonZero; [self.layer addSublayer:shapeLayer]; } 

Funciona en el 60% de las veces por alguna razón. ¿Hay algún problema con eso? De todos modos, me gustaría saber de una mejor manera.

Gracias.

Hice lo mismo con una categoría UIView . Aquí están mis métodos:

 #define SEPARATOR_HEIGHT 0.5 - (void)addSeparatorLinesWithColor:(UIColor *)color { [self addSeparatorLinesWithColor:color edgeInset:UIEdgeInsetsZero]; } - (void)addSeparatorLinesWithColor:(UIColor *)color edgeInset:(UIEdgeInsets)edgeInset { UIView *topSeparatorView = [[UIView alloc] initWithFrame:CGRectMake(edgeInset.left, - SEPARATOR_HEIGHT, self.frame.size.width - edgeInset.left - edgeInset.right, SEPARATOR_HEIGHT)]; [topSeparatorView setBackgroundColor:color]; [self addSubview:topSeparatorView]; UIView *separatorView = [[UIView alloc] initWithFrame:CGRectMake(edgeInset.left, self.frame.size.height + SEPARATOR_HEIGHT, self.frame.size.width - edgeInset.left - edgeInset.right, SEPARATOR_HEIGHT)]; [separatorView setBackgroundColor:color]; [self addSubview:separatorView]; } 

Solo para agregar a la gran respuesta de Rémy, quizás sea aún más simple hacerlo. Haz una clase UILine.m

 @interface UILine:UIView @end @implementation UILine -(id)awakeFromNib { // careful, contentScaleFactor does NOT WORK in storyboard during initWithCoder. // example, float sortaPixel = 1.0/self.contentScaleFactor ... does not work. // instead, use mainScreen scale which works perfectly: float sortaPixel = 1.0/[UIScreen mainScreen].scale; UIView *topSeparatorView = [[UIView alloc] initWithFrame: CGRectMake(0, 0, self.frame.size.width, sortaPixel)]; topSeparatorView.userInteractionEnabled = NO; [topSeparatorView setBackgroundColor:self.backgroundColor]; [self addSubview:topSeparatorView]; self.backgroundColor = [UIColor clearColor]; self.userInteractionEnabled = NO; } @end 

En IB, coloca un UIView, haz clic en el inspector de identidad y cambia el nombre de la clase a UILine. Establezca el ancho que desea en IB. Establezca la altura en 1 o 2 píxeles, simplemente para que pueda verlo en IB. Establezca el color de fondo que desea en IB. Cuando ejecutas la aplicación, se convertirá en una línea de 1 píxel, ese ancho, en ese color. (Probablemente no deberías ser afectado por ninguna configuración predeterminada de autoresize en storyboard / xib, no pude romperla). Ya terminaste.

Nota: puede pensar “¿Por qué no simplemente cambiar el tamaño de UIView en el código en awakeFromNib?” Cambiar el tamaño de las vistas al cargar, en una aplicación de guión gráfico, es problemático. ¡Mire aquí las muchas preguntas al respecto!

Interesante gotchya : es probable que solo hagas que el UIView tenga una altura de 10 o 20 píxeles en el guión gráfico, simplemente para que puedas verlo. Por supuesto, desaparece en la aplicación y obtienes la bonita línea de un píxel. ¡Pero! ¡asegúrese de recordar self.userInteractionEnabled = NO , o podría superar sus otros botones, por ejemplo!


Solución 2016! https://stackoverflow.com/a/34766567/294884

 shapeLayer.lineWidth = 0.5f; 

Ese es un error común y es la razón por la que esto solo funciona algunas veces. A veces, esto se superpondrá a los píxeles en la pantalla con exactitud y, a veces, no. La forma de dibujar una línea de un solo punto que siempre funciona es dibujar un rectángulo de un punto de espesor en los límites enteros , y llenarlo . De esta forma, siempre coincidirá exactamente con los píxeles en la pantalla.

Para convertir de puntos a píxeles, si desea hacer eso, use el factor de escala de la vista.

Por lo tanto, esto siempre será de un píxel de ancho:

 CGContextFillRect(con, CGRectMake(0,0,desiredLength,1.0/self.contentScaleFactor)); 

Aquí hay una captura de pantalla que muestra la línea utilizada como separador, dibujada en la parte superior de cada celda:

enter image description here

La vista de tabla en sí no tiene separadores (como se muestra en el espacio en blanco debajo de las tres celdas existentes). Puede que no esté dibujando la línea en la posición, longitud y color que desea, pero esa es su preocupación, no la mía.

Método de AutoLayout:

Uso un viejo UIView simple y establezco su restricción de altura en 1 en el Interface Builder. Se adjunta al fondo a través de restricciones. El constructor de interfaz no le permite configurar la restricción de altura a 0.5, pero puede hacerlo en código.

Haga un conector para la restricción de altura, luego llame a esto:

 // Note: This will be 0.5 on retina screens self.dividerViewHeightConstraint.constant = 1.0/[UIScreen mainScreen].scale 

Trabajó para mi.

FWIW No creo que debamos admitir pantallas que no sean retina. Sin embargo, todavía estoy usando la escala de la pantalla principal para probar la aplicación en el futuro.

Debe tener en cuenta la escala debido a la retina y que no se está refiriendo a los píxeles de la pantalla. Vea Puntos Gráficos Core vs. Píxeles .

Además de la respuesta de Rémy Virin, usando Swift 3.0 Creating LineSeparator class:

 import UIKit class LineSeparator: UIView { override func awakeFromNib() { let sortaPixel: CGFloat = 1.0/UIScreen.main.scale let topSeparatorView = UIView() topSeparatorView.frame = CGRect(x: 0, y: 0, width: self.frame.size.width, height: sortaPixel) topSeparatorView.isUserInteractionEnabled = false topSeparatorView.backgroundColor = self.backgroundColor self.addSubview(topSeparatorView) self.backgroundColor = UIColor.clear self.isUserInteractionEnabled = false } }