web-dev-qa-db-fra.com

Dessin de UIBezierPath sur le code généré par UIView

J'ai un UIView ajouté dans le code au moment de l'exécution.

Je veux y dessiner un UIBezierPath, mais cela signifie-t-il que je dois remplacer le drawRect pour UIView?

Ou y a-t-il une autre façon de dessiner sur le fait sur mesure UIView?

Voici le code pour générer le 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;

Et voici la fonction pour créer et retourner 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;
}
61
Itzik984

Il n'y a pas si longtemps, je ne savais même pas comment prononcer Bézier, encore moins comment utiliser les chemins de Bézier pour créer une forme personnalisée. Ce qui suit est ce que j'ai appris. Il s'avère qu'ils ne sont pas aussi effrayants qu'ils le paraissent au début.

Comment dessiner un chemin de Bézier dans une vue personnalisée

Ce sont les principales étapes:

  1. Concevez le contour de la forme souhaitée.
  2. Divisez le tracé en segments de lignes, d'arcs et de courbes.
  3. Construisez ce chemin par programmation.
  4. Dessinez le chemin soit dans drawRect, soit à l'aide de CAShapeLayer.

Contour de forme de conception

Vous pouvez faire n'importe quoi, mais à titre d'exemple, j'ai choisi la forme ci-dessous. Il pourrait s'agir d'une touche contextuelle sur un clavier.

enter image description here

Diviser le chemin en segments

Examinez votre dessin de forme et divisez-le en éléments plus simples de lignes (pour les lignes droites), d'arcs (pour les cercles et les coins arrondis) et de courbes (pour toute autre chose).

Voici à quoi ressemblerait notre exemple de conception:

enter image description here

  • Les noirs sont des segments de droite
  • Bleu clair sont des segments d'arc
  • Les rouges sont des courbes
  • Les points orange sont les points de contrôle des courbes
  • Les points verts sont les points entre les segments de chemin
  • Les lignes en pointillés représentent le rectangle de délimitation
  • Les nombres en bleu foncé sont les segments dans l'ordre dans lequel ils seront ajoutés par programme

Construire le chemin par programmation

Nous allons commencer arbitrairement dans le coin inférieur gauche et travailler dans le sens des aiguilles d'une montre. Je vais utiliser la grille de l'image pour obtenir les valeurs x et y des points. Je vais tout codifier ici, mais bien sûr, vous ne feriez pas cela dans un vrai projet.

Le processus de base est:

  1. Créer un nouveau UIBezierPath
  2. Choisissez un point de départ sur le chemin avec moveToPoint
  3. Ajouter des segments au chemin
    • ligne: addLineToPoint
    • arc: addArcWithCenter
    • courbe: addCurveToPoint
  4. Fermez le chemin avec closePath

Voici le code pour faire le chemin dans l'image ci-dessus.

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
}

Remarque: Certains des codes ci-dessus peuvent être réduits en ajoutant une ligne et un arc dans une seule commande (car l'arc a un point de départ implicite). Voir ici pour plus de détails.

Dessiner le chemin

Nous pouvons dessiner le chemin soit dans une couche, soit dans drawRect.

Méthode 1: Tracez un chemin dans une couche

Notre classe personnalisée ressemble à ceci. Nous ajoutons notre chemin Bézier à un nouveau CAShapeLayer lorsque la vue est initialisée.

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

Et créer notre vue dans le contrôleur de vue comme ceci

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)

}

On a...

enter image description here

Hmm, c'est un peu petit parce que j'ai codé en dur tous les nombres. Je peux cependant redimensionner la taille du chemin, comme ceci:

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

enter image description here

Méthode 2: Tracez le chemin dans draw

L'utilisation de draw est plus lente que de dessiner sur le calque. Cette méthode n'est donc pas recommandée si vous n'en avez pas besoin.

Voici le code révisé pour notre vue personnalisée:

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

ce qui nous donne le même résultat ...

enter image description here

Une étude plus approfondie

Je vraiment recommande de regarder les documents suivants. Ce sont finalement ce qui a rendu les chemins de Bézier compréhensibles pour moi. (Et m'a appris à le prononcer:/bɛ zi ei /.)

223
Suragch

Ce serait plus facile si vous utilisiez un CAShapeLayer, comme ceci:

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

Et définir sa path:

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

Enfin ajoutez-le:

[[self.view layer] addSublayer:shapeView];
53
Rui Peres

Vous pouvez utiliser un CAShapeLayer pour le faire.

Comme ça...

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];

Cela va ensuite ajouter et dessiner le chemin sans avoir à remplacer drawRect.

15
Fogmeister

Il y a plusieurs façons d'accomplir ce que vous souhaitez. Ceux que j'ai le plus souvent rencontrés sont: remplacez drawRect, dessinez votre forme dans un CAShapeLayer , puis ajoutez-la en tant que sous-couche à votre vue, ou tracez votre chemin dans un autre contexte , enregistrez-le en tant qu’image, puis ajoutez-le à votre vue.

Tous ces choix sont raisonnables, et celui qui convient le mieux dépend de nombreux autres facteurs, tels que l'ajout continu de formes, la fréquence d'appellation, etc.

5
Travis

Comme les autres affiches l'ont souligné, l'utilisation d'un calque de forme est un bon choix.

Les couches de forme a sont susceptibles de vous offrir de meilleures performances que de remplacer drawRect.

Si vous voulez dessiner vous-même votre chemin, alors oui, vous devez remplacer drawRect pour votre classe de vue personnalisée.

2
Duncan C

Oui, vous devez écraser le drawrect si vous voulez dessiner quelque chose.Créer un UIBezierPath peut être fait n’importe où, mais pour dessiner quelque chose, vous devez le faire dans la méthode drawrect

Vous devriez appeler setNeedsDisplay si vous écrasez drawRect dans une sous-classe de UIView, qui est essentiellement une vue personnalisée qui dessine quelque chose à l'écran, telle que des lignes, une image, un rectangle.

1
Lithu T.V