84 votes

Comment dessiner une ligne de manière programmatique à partir d'un contrôleur de vue ?

J'ai un UIViewController . Comment dessiner une ligne dans une de ses vues créées par programme ?

3 votes

Non, vous ne pouvez pas. Le dessin doit être fait par la vue et non par le contrôleur. Vous devez changer votre conception.

1 votes

@xlc Vous pouvez le faire à partir d'un VC, comme le montre la réponse de Rob. Si vous voulez vraiment être utile, expliquez ce qu'il y a de mal dans la technique du bézier.

0 votes

Pouvons-nous voir un peu de code ? Je suis d'accord, c'est une vraie question, mais je (peut-être que nous) répondons mieux à un code démontrant ce dont vous parlez.

190voto

Rob Points 70987

Il existe deux techniques courantes.

  1. Utilisation de CAShapeLayer :

    • Créer un UIBezierPath (remplacez les coordonnées par ce que vous voulez) :

      UIBezierPath *path = [UIBezierPath bezierPath];
      [path moveToPoint:CGPointMake(10.0, 10.0)];
      [path addLineToPoint:CGPointMake(100.0, 100.0)];
    • Créer un CAShapeLayer qui utilise ce UIBezierPath :

      CAShapeLayer *shapeLayer = [CAShapeLayer layer];
      shapeLayer.path = [path CGPath];
      shapeLayer.strokeColor = [[UIColor blueColor] CGColor];
      shapeLayer.lineWidth = 3.0;
      shapeLayer.fillColor = [[UIColor clearColor] CGColor];
    • Ajoutez que CAShapeLayer à la couche de votre vue :

      [self.view.layer addSublayer:shapeLayer];

    Dans les versions précédentes de Xcode, vous deviez ajouter manuellement QuartzCore.framework dans la section "Link Binary with Libraries" de votre projet. et importer le <QuartzCore/QuartzCore.h> dans votre fichier .m, mais ce n'est plus nécessaire (si vous avez activé les paramètres de construction "Enable Modules" et "Link Frameworks Automatically").

  2. L'autre approche consiste à sous-classer UIView et ensuite utiliser CoreGraphics les appels dans le drawRect méthode :

    • Créer un UIView et définissez une sous-classe drawRect qui trace votre ligne.

      Vous pouvez le faire avec Core Graphics :

      - (void)drawRect:(CGRect)rect {
          CGContextRef context = UIGraphicsGetCurrentContext();
      
          CGContextSetStrokeColorWithColor(context, [[UIColor blueColor] CGColor]);
          CGContextSetLineWidth(context, 3.0);
          CGContextMoveToPoint(context, 10.0, 10.0);
          CGContextAddLineToPoint(context, 100.0, 100.0);
          CGContextDrawPath(context, kCGPathStroke);
      }

      Ou en utilisant UIKit :

      - (void)drawRect:(CGRect)rect {
          UIBezierPath *path = [UIBezierPath bezierPath];
          [path moveToPoint:CGPointMake(10.0, 10.0)];
          [path addLineToPoint:CGPointMake(100.0, 100.0)];
          path.lineWidth = 3;
          [[UIColor blueColor] setStroke];
          [path stroke];
      }
    • Ensuite, vous pouvez soit utiliser cette classe de vue comme classe de base pour votre NIB/storyboard ou votre vue, soit demander à votre contrôleur de vue de l'ajouter de manière programmatique en tant que sous-vue :

      PathView *pathView = [[PathView alloc] initWithFrame:self.view.bounds];
      pathView.backgroundColor = [UIColor clearColor];
      
      [self.view addSubview: pathView];

Les rendus de Swift des deux approches ci-dessus sont les suivants :

  1. CAShapeLayer :

    // create path
    
    let path = UIBezierPath()
    path.move(to: CGPoint(x: 10, y: 10))
    path.addLine(to: CGPoint(x: 100, y: 100))
    
    // Create a `CAShapeLayer` that uses that `UIBezierPath`:
    
    let shapeLayer = CAShapeLayer()
    shapeLayer.path = path.cgPath
    shapeLayer.strokeColor = UIColor.blue.cgColor
    shapeLayer.fillColor = UIColor.clear.cgColor
    shapeLayer.lineWidth = 3
    
    // Add that `CAShapeLayer` to your view's layer:
    
    view.layer.addSublayer(shapeLayer)
  2. UIView sous-classe :

    class PathView: UIView {
    
        var path: UIBezierPath?           { didSet { setNeedsDisplay() } }
        var pathColor: UIColor = .blue    { didSet { setNeedsDisplay() } }
    
        override func draw(_ rect: CGRect) {
            // stroke the path
    
            pathColor.setStroke()
            path?.stroke()
        }
    
    }

    Et ajoutez-le à votre hiérarchie de vues :

    let pathView = PathView()
    pathView.translatesAutoresizingMaskIntoConstraints = false
    view.addSubview(pathView)
    
    NSLayoutConstraint.activate([
        pathView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
        pathView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
        pathView.topAnchor.constraint(equalTo: view.topAnchor),
        pathView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
    ])
    
    pathView.backgroundColor = .clear
    
    let path = UIBezierPath()
    path.move(to: CGPoint(x: 10, y: 10))
    path.addLine(to: CGPoint(x: 100, y: 100))
    path.lineWidth = 3
    
    pathView.path = path

    Au-dessus, j'ajoute PathView de manière programmatique, mais vous pouvez également l'ajouter via IB, en définissant simplement son paramètre path de manière programmatique.

2 votes

@Rob : Cela fonctionne parfaitement bien. Je voudrais vous demander si j'ai une distance linéaire AB et un angle de AB, alors comment obtenir les points de départ et d'arrivée dans cette situation ? Merci de m'aider.

1 votes

J'ai ajouté l'uigesture à ma vue, et j'ai touché le point de départ et le point de déplacement. Si j'ajoute cela dans le commentaire ci-dessus, la ligne droite fonctionne correctement, mais lorsque je touche le côté du point de départ lui-même, il occupe la couleur.

1 votes

Lorsque vous utilisez la méthode CAShapeLayer, vous pouvez (et vous voudrez probablement) définir les capuchons de ligne sur arrondis afin que les lignes aient l'air lisses lorsqu'elles sont connectées, shapeLayer.lineCap = kCALineCapRound;

17voto

Jeff Ames Points 317

Créez une UIView et ajoutez-la en tant que sous-vue de la vue de votre contrôleur de vue. Vous pouvez modifier la hauteur ou la largeur de cette sous-vue pour qu'elle soit très petite et qu'elle ressemble à une ligne. Si vous devez dessiner une ligne diagonale, vous pouvez modifier la propriété transform de la sous-vue.

Par exemple, dessiner une ligne horizontale noire. Cette fonction est appelée à partir de l'implémentation du contrôleur de vue.

UIView *lineView = [[UIView alloc] initWithFrame:CGRectMake(0,0, self.view.frame.size.width, 1)];
lineView.backgroundColor = [UIColor blackColor];
[self.view addSubview:lineView];

0 votes

Je serais enclin à convenir que pour un élément séparateur (par exemple une ligne entre des éléments d'IU) c'est le chemin de moindre résistance. L'alternative consistant à effectuer un rendu dans un bitmap pour soutenir une couche CoreAnimation ou à implémenter un gestionnaire de dessin dans une vue personnalisée est beaucoup plus complexe. Cela accélère probablement mieux le matériel aussi, à condition que ce ne soit qu'une ligne.

5 votes

Je suis curieux de connaître le raisonnement qui explique pourquoi c'est une idée terrible. J'aimerais éviter tout problème que cette solution pourrait causer dans mon propre code.

0 votes

@sangony J'utilise aussi cette technique dans mon code. Je me demande également pourquoi c'est une idée terrible ? Veuillez m'aider à l'expliquer.

9voto

smallduck Points 799

Voici une technique sympa qui pourrait vous être utile : Utilisation de blocs pour le dessin afin d'éviter la sous-classification en Objective-C

Incluez la sous-classe de vue polyvalente de l'article dans votre projet, et voici le type de code que vous pouvez placer dans votre contrôleur de vue pour créer une vue à la volée qui dessine une ligne :

DrawView* drawableView = [[[DrawView alloc] initWithFrame:CGRectMake(0,0,320,50)] autorelease];
drawableView.drawBlock = ^(UIView* v,CGContextRef context)
{
  CGPoint startPoint = CGPointMake(0,v.bounds.size.height-1);
  CGPoint endPoint = CGPointMake(v.bounds.size.width,v.bounds.size.height-1);

  CGContextSetStrokeColorWithColor(context, [UIColor grayColor].CGColor);
  CGContextSetLineWidth(context, 1);
  CGContextMoveToPoint(context, startPoint.x + 0.5, startPoint.y + 0.5);
  CGContextAddLineToPoint(context, endPoint.x + 0.5, endPoint.y + 0.5);
  CGContextStrokePath(context);
};
[self.view addSubview:drawableView];

6voto

khunshan Points 78

Vous pouvez utiliser UIImageView pour dessiner des lignes.

Il permet cependant de sauter les sous-classes. Et comme je suis peu enclin à Core Graphics peut encore l'utiliser. Vous pouvez simplement le mettre dans - ViewDidLoad

  UIGraphicsBeginImageContext(self.view.frame.size);
  [self.myImageView.image drawInRect:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
  CGContextSetLineCap(UIGraphicsGetCurrentContext(), kCGLineCapRound);
  CGContextSetLineWidth(UIGraphicsGetCurrentContext(), brush);

  CGContextMoveToPoint(UIGraphicsGetCurrentContext(), 50, 50);
  CGContextAddLineToPoint(UIGraphicsGetCurrentContext(), 200, 200);
  CGContextStrokePath(UIGraphicsGetCurrentContext());
  CGContextFlush(UIGraphicsGetCurrentContext());
  self.myImageView.image = UIGraphicsGetImageFromCurrentImageContext();
  UIGraphicsEndImageContext();

En complément de la réponse de Rob, pour un quicke, la troisième approche est d'utiliser une UIImageView - couvrir avec elle - la vue de xib. (C'est l'apparence par défaut de UIImageView lorsqu'il est glissé sur xib dans xcode 5)

Santé et +1 !

2voto

Nathan Day Points 3672

Vous ne devriez pas vraiment, mais si pour une raison quelconque, cela a un sens pour vous, vous pourriez créer une sous-classe de UIView, appelée DelegateDrawView par exemple, qui prend un délégué qui implémente une méthode telle que

- (void)delegateDrawView:(DelegateDrawView *)aDelegateDrawView drawRect:(NSRect)dirtyRect

et ensuite dans les méthodes - [DelegateDrawView drawRect:] vous devez appeler votre méthode de délégué.

Mais pourquoi voudriez-vous mettre du code de vue dans votre contrôleur.

Il est préférable de créer une sous-classe de UIView, qui trace une ligne entre deux de ses coins, vous pouvez avoir une propriété pour définir les deux et ensuite positionner la vue où vous le souhaitez à partir de votre contrôleur de vue.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X