ios iphone objective-c uiview uibezierpath

ios - Dibujando UIBezierPath en el código UIView generado



iphone objective-c (6)

Tengo un UIView agregado en el código en tiempo de ejecución.

Quiero dibujar un UIBezierPath en él, pero ¿esto significa que tengo que anular drawRect para UIView?

¿O hay otra forma de atraerlo en el UIView personalizado?

Aquí está el código para generar UIView :

UIView* shapeView = [[UIView alloc]initWithFrame:CGRectMake(xOrigin,yOrigin+(i*MENU_BLOCK_FRAME_HEIGHT), self.shapeScroll.frame.size.width, MENU_BLOCK_FRAME_HEIGHT)]; shapeView.clipsToBounds = YES;

Y aquí está la función para crear y devolver un UIBezierPath :

- (UIBezierPath*)createPath { UIBezierPath* path = [[UIBezierPath alloc]init]; [path moveToPoint:CGPointMake(100.0, 50.0)]; [path addLineToPoint:CGPointMake(200.0,50.0)]; [path addLineToPoint:CGPointMake(200.0, 200.0)]; [path addLineToPoint:CGPointMake(100.0, 200.0)]; [path closePath]; return path; }


No hace mucho tiempo que ni siquiera sabía cómo pronunciar Bézier, y mucho menos saber cómo usar las rutas de Bézier para crear una forma personalizada. Lo siguiente es lo que he aprendido. Resulta que no son tan atemorizantes como parecen al principio.

Cómo dibujar una ruta Bézier en una vista personalizada

Estos son los pasos principales:

  1. Diseña el contorno de la forma que quieras.
  2. Divida la ruta del contorno en segmentos de líneas, arcos y curvas.
  3. Construye ese camino programáticamente.
  4. Dibuje la ruta ya sea en drawRect o usando un CAShapeLayer .

Diseño de contorno de forma

Podrías hacer cualquier cosa, pero como ejemplo, he elegido la forma a continuación. Podría ser una tecla emergente en un teclado.

Divide el camino en segmentos

Mire hacia atrás en su diseño de forma y divídalo en elementos más simples de líneas (para líneas rectas), arcos (para círculos y esquinas redondeadas) y curvas (para cualquier otra cosa).

Aquí está el aspecto de nuestro diseño de ejemplo:

  • Negro son segmentos de línea
  • Azul claro son segmentos de arco
  • Rojo son curvas
  • Los puntos anaranjados son los puntos de control para las curvas
  • Los puntos verdes son los puntos entre los segmentos de ruta
  • Las líneas punteadas muestran el rectángulo delimitador
  • Los números azul oscuro son los segmentos en el orden en que se agregarán mediante programación

Construye el camino programáticamente

Arbitrariamente comenzaremos en la esquina inferior izquierda y trabajaremos en el sentido de las agujas del reloj. Usaré la cuadrícula en la imagen para obtener los valores xey de los puntos. Codificaré todo aquí, pero por supuesto no harías eso en un proyecto real.

El proceso básico es:

  1. Crea un nuevo UIBezierPath
  2. Elija un punto de inicio en la ruta con moveToPoint
  3. Agregue segmentos a la ruta
    • línea: addLineToPoint
    • arco: addArcWithCenter
    • curva: addCurveToPoint
  4. Cierre el camino con closePath

Aquí está el código para hacer la ruta en la imagen de arriba.

func createBezierPath() -> UIBezierPath { // create a new path let path = UIBezierPath() // starting point for the path (bottom left) path.move(to: CGPoint(x: 2, y: 26)) // ********************* // ***** Left side ***** // ********************* // segment 1: line path.addLine(to: CGPoint(x: 2, y: 15)) // segment 2: curve path.addCurve(to: CGPoint(x: 0, y: 12), // ending point controlPoint1: CGPoint(x: 2, y: 14), controlPoint2: CGPoint(x: 0, y: 14)) // segment 3: line path.addLine(to: CGPoint(x: 0, y: 2)) // ********************* // ****** Top side ***** // ********************* // segment 4: arc path.addArc(withCenter: CGPoint(x: 2, y: 2), // center point of circle radius: 2, // this will make it meet our path line startAngle: CGFloat(M_PI), // π radians = 180 degrees = straight left endAngle: CGFloat(3*M_PI_2), // 3π/2 radians = 270 degrees = straight up clockwise: true) // startAngle to endAngle goes in a clockwise direction // segment 5: line path.addLine(to: CGPoint(x: 8, y: 0)) // segment 6: arc path.addArc(withCenter: CGPoint(x: 8, y: 2), radius: 2, startAngle: CGFloat(3*M_PI_2), // straight up endAngle: CGFloat(0), // 0 radians = straight right clockwise: true) // ********************* // ***** Right side **** // ********************* // segment 7: line path.addLine(to: CGPoint(x: 10, y: 12)) // segment 8: curve path.addCurve(to: CGPoint(x: 8, y: 15), // ending point controlPoint1: CGPoint(x: 10, y: 14), controlPoint2: CGPoint(x: 8, y: 14)) // segment 9: line path.addLine(to: CGPoint(x: 8, y: 26)) // ********************* // **** Bottom side **** // ********************* // segment 10: line path.close() // draws the final line to close the path return path }

Nota: Algunos de los códigos anteriores se pueden reducir agregando una línea y un arco en un solo comando (ya que el arco tiene un punto de inicio implícito). Mira aquí para más detalles.

Dibuja el camino

Podemos dibujar el camino en una capa o en drawRect .

Método 1: dibujar la ruta en una capa

Nuestra clase personalizada se ve así. CAShapeLayer nuestra ruta de Bezier a un nuevo CAShapeLayer cuando la vista se inicializa.

import UIKit class MyCustomView: UIView { override init(frame: CGRect) { super.init(frame: frame) setup() } required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) setup() } func setup() { // Create a CAShapeLayer let shapeLayer = CAShapeLayer() // The Bezier path that we made needs to be converted to // a CGPath before it can be used on a layer. shapeLayer.path = createBezierPath().cgPath // apply other properties related to the path shapeLayer.strokeColor = UIColor.blue.cgColor shapeLayer.fillColor = UIColor.white.cgColor shapeLayer.lineWidth = 1.0 shapeLayer.position = CGPoint(x: 10, y: 10) // add the new layer to our custom view self.layer.addSublayer(shapeLayer) } func createBezierPath() -> UIBezierPath { // see previous code for creating the Bezier path } }

Y creando nuestra vista en View Controller de esta manera

override func viewDidLoad() { super.viewDidLoad() // create a new UIView and add it to the view controller let myView = MyCustomView() myView.frame = CGRect(x: 100, y: 100, width: 50, height: 50) myView.backgroundColor = UIColor.yellow view.addSubview(myView) }

Obtenemos...

Hmm, eso es un poco pequeño porque codifiqué todos los números. Sin embargo, puedo escalar el tamaño de la ruta, así:

let path = createBezierPath() let scale = CGAffineTransform(scaleX: 2, y: 2) path.apply(scale) shapeLayer.path = path.cgPath

Método 2: dibujar camino en draw

Usar draw es más lento que dibujar en la capa, por lo que este no es el método recomendado si no lo necesitas.

Aquí está el código revisado para nuestra vista personalizada:

import UIKit class MyCustomView: UIView { override func draw(_ rect: CGRect) { // create path (see previous code) let path = createBezierPath() // fill let fillColor = UIColor.white fillColor.setFill() // stroke path.lineWidth = 1.0 let strokeColor = UIColor.blue strokeColor.setStroke() // Move the path to a new location path.apply(CGAffineTransform(translationX: 10, y: 10)) // fill and stroke the path (always do these last) path.fill() path.stroke() } func createBezierPath() -> UIBezierPath { // see previous code for creating the Bezier path } }

que nos da el mismo resultado ...

Estudio adicional

Realmente recomiendo mirar los siguientes materiales. Son lo que finalmente hizo que los caminos de Bézier fueran comprensibles para mí. (Y me enseñó a pronunciarlo: / bɛ zi eɪ /.)


Como señalaron los otros carteles, usar una capa de forma es una buena forma de hacerlo.

Las capas de forma a le darán un mejor rendimiento que la anulación de drawRect.

Si desea dibujar su ruta usted mismo, entonces sí, necesita anular drawRect para su clase de vista personalizada.


Sí, tienes que anular el drawrect si quieres dibujar algo. Crear un UIBezierPath se puede hacer en cualquier lugar, pero para dibujar algo tienes que hacerlo dentro del método drawrect

Debería llamar a setNeedsDisplay si anula drawRect en una subclase de UIView que es básicamente una vista personalizada que dibuja algo en la pantalla, como líneas, imágenes, rectángulos.


Sería más fácil si usara CAShapeLayer , así:

CAShapeLayer *shapeView = [[CAShapeLayer alloc] init];

Y establece su path :

[shapeView setPath:[self createPath].CGPath];

Finalmente agrégalo:

[[self.view layer] addSublayer:shapeView];


Puede usar CAShapeLayer para hacer esto.

Me gusta esto...

CAShapeLayer *shapeLayer = [CAShapeLayer layer]; shapeLayer.path = [self createPath].CGPath; shapeLayer.strokeColor = [UIColor redColor].CGColor; //etc... shapeLayer.lineWidth = 2.0; //etc... shapeLayer.position = CGPointMake(100, 100); //etc... [self.layer addSublayer:shapeLayer];

Esto luego agregará y dibujará la ruta sin tener que anular drawRect .


Hay múltiples formas de lograr lo que deseas. Los que más he visto son: anula drawRect, dibuja tu forma en un CAShapeLayer y luego agrégala como una subcapa a tu vista, o dibuja tu camino en otro contexto , guárdalo como una imagen y luego agrégalo a tu ver.

Todas estas son elecciones razonables, y cuál es la mejor depende de muchos otros factores, como por ejemplo, agregar continuamente formas, con qué frecuencia se lo llama, etc.