Tracer une ligne de séparation en conjonction avec la layout automatique

Je suis réimplementig une sorte de UISplitViewController . Maintenant, j'ai besoin de dessiner une ligne de séparation entre le maître et la vue détaillée. Maintenant, j'ai quelques questions à ce sujet:

  • La ligne de séparation doit-elle être sur le controller de vue lui-même ou doit-elle être une vue séparée?
  • Qu'en est-il de la disposition automatique? La définition d'un cadre n'est pas autorisée.

J'ai vu des solutions pour CALayer / CAShapeLayer , drawRect (CoreGraphics) ou en utilisant la couleur d'arrière-plan d'un UIView / UILabel . Le dernier devrait coûter trop de performance selon les gens là-bas.

D'un côté, il est confortable de dessiner la ligne dans UITableViewController lui-même. Ou devrait-on créer un UIView distinct? Si UIView un UIView séparé il y aura beaucoup plus de contraintes et cela compliquera les choses (il faudrait définir des largeurs séparées) … Il devrait aussi s'adapter aux changements d'orientation (par exemple la taille des changements UITableViewController -> la ligne de séparation devrait aussi resize).

Comment puis-je append une telle règle de division? Une telle règle de division peut être vue ici: La ligne de séparateur divise le contrôleur de vue principale et de détail

Si vous avez besoin d'append une vraie ligne d'un pixel , ne vous trompez pas avec une image. C'est presque impossible. Utilisez simplement ceci:

 @interface UILine : UIView @end @implementation UILine - (void)awakeFromNib { CGFloat sortaPixel = 1 / [UIScreen mainScreen].scale; // recall, the following... // CGFloat sortaPixel = 1 / self.contentScaleFactor; // ...does NOT work when loading from storyboard! UIView *line = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.frame.size.width, sortaPixel)]; line.userInteractionEnabled = NO; line.backgroundColor = self.backgroundColor; line.autoresizingMask = UIViewAutoresizingFlexibleWidth; [self addSubview:line]; self.backgroundColor = [UIColor clearColor]; self.userInteractionEnabled = NO; } @end 

Comment utiliser:

En fait, dans le storyboard, faites simplement un UIView qui est à l'endroit exact, et la largeur exacte que vous voulez . (N'hésitez pas à utiliser les contraintes / autolayout comme d'habitude.)

Faites en sorte que la vue indique cinq pixels de haut, simplement pour que vous puissiez la voir clairement, tout en travaillant.

Faites le sumt de l'UIView exactement où vous voulez la ligne d'un seul pixel. Faire l'UIView la couleur désirée de la ligne.

Changez la class en UILine . Au moment de l'exécution , il dessine une ligne parfaite d'un seul pixel à l'location exact sur tous les appareils.

(Pour une class de ligne verticale, modifiez simplement le CGRectMake.)

J'espère que cela aide!

J'ai poussé l'excellente réponse de @ joe-blow un peu plus loin et j'ai créé une vue qui n'est pas seulement rendue dans IB mais aussi dont la largeur de ligne et la couleur de ligne peuvent être modifiées via l'inspecteur de IB. Il suffit d'append un UIView à votre storyboard, de le dimensionner de manière appropriée et de changer la class en LineView .

 import UIKit @IBDesignable class LineView: UIView { @IBInspectable var lineWidth: CGFloat = 1.0 @IBInspectable var lineColor: UIColor? { didSet { lineCGColor = lineColor?.CGColor } } var lineCGColor: CGColorRef? override func drawRect(rect: CGRect) { // Draw a line from the left to the right at the midpoint of the view's rect height. let midpoint = self.bounds.size.height / 2.0 let context = UIGraphicsGetCurrentContext() CGContextSetLineWidth(context, lineWidth) if let lineCGColor = self.lineCGColor { CGContextSetStrokeColorWithColor(context, lineCGColor) } else { CGContextSetStrokeColorWithColor(context, UIColor.blackColor().CGColor) } CGContextMoveToPoint(context, 0.0, midpoint) CGContextAddLineToPoint(context, self.bounds.size.width, midpoint) CGContextStrokePath(context) } } 

Inspecteur pour LineView

LineView rendu dans storybard

Mise à jour de la réponse @mharper à Swift 3.x

 import UIKit @IBDesignable class LineView: UIView { @IBInspectable var lineWidth: CGFloat = 1.0 @IBInspectable var lineColor: UIColor? { didSet { lineCGColor = lineColor?.cgColor } } var lineCGColor: CGColor? override func draw(_ rect: CGRect) { // Draw a line from the left to the right at the midpoint of the view's rect height. let midpoint = self.bounds.size.height / 2.0 if let context = UIGraphicsGetCurrentContext() { context.setLineWidth(lineWidth) if let lineCGColor = self.lineCGColor { context.setStrokeColor(lineCGColor) } else { context.setStrokeColor(UIColor.black.cgColor) } context.move(to: CGPoint(x: 0.0, y: midpoint)) context.addLine(to: CGPoint(x: self.bounds.size.width, y: midpoint)) context.strokePath() } } } 

Nouveau code

Cela devrait fonctionner pour les lignes horizontales et verticales:

 /// <summary> /// Used as separator line between UIView elements with a given color. /// </summary> public class DividerView : UIView { private UIColor color; public DividerView () { this.color = UIColor.Black; } public DividerView (UIColor color) { this.color = color; } public override void Draw (CGRect rect) { base.Draw (rect); // get graphics context CGContext context = UIGraphics.GetCurrentContext (); // set up drawing atsortingbutes color.SetStroke (); color.SetFill (); // assumption: we can determine if the line is horizontal/vertical based on it's size nfloat lineWidth = 0; nfloat xStartPosition = 0; nfloat yStartPosition = 0; nfloat xEndPosition = 0; nfloat yEndPosition = 0; if (rect.Width > rect.Height) { // horizontal line lineWidth = rect.Height; xStartPosition = rect.X; // Move the path down by half of the line width so it doesn't straddle pixels. yStartPosition = rect.Y + lineWidth * 0.5f; xEndPosition = rect.X + rect.Width; yEndPosition = yStartPosition; } else { // vertical line lineWidth = rect.Width; // Move the path down by half of the line width so it doesn't straddle pixels. xStartPosition = rect.X + lineWidth * 0.5f; yStartPosition = rect.Y; xEndPosition = xStartPosition; yEndPosition = rect.Y + rect.Height; } // start point context.MoveTo (xStartPosition, yStartPosition); // end point context.AddLineToPoint (xEndPosition, yEndPosition); context.SetLineWidth (lineWidth); // draw the path context.DrawPath (CGPathDrawingMode.Stroke); } } 

Réponse originale

L'utilisation de frameworks dans un projet Auto Layout ne me semble pas adaptée. De plus, j'aurais besoin des images réelles après que la layout automatique ait été appliquée et que je devrais dessiner une autre vue par-dessus. Dans une layout fixe basée sur des frameworks, il n'y a pas de problème, mais pas ici. C'est pourquoi j'ai choisi l'apporach suivant pour le moment:

J'ai créé une sous-class de UIView et écrasé drawRect comme dans la ligne Draw dans UIView ou comment dessinez -vous une ligne par programmation à partir d'un controller de vue? . Voici une autre option.

Parce que j'utilise C #, je donne les exemples de code dans ce langage de programmation. Dans les liens que j'ai publiés, vous pouvez get la version Objective-C si vous le souhaitez.

DividerView :

 using System; using MonoTouch.Foundation; using MonoTouch.UIKit; using MonoTouch.CoreGraphics; using System.CodeDom.Comstackr; using System.Drawing; namespace ContainerProject { public class DividerView : UIView { public DividerView () { } public override void Draw (RectangleF rect) { base.Draw (rect); // get graphics context CGContext context = UIGraphics.GetCurrentContext (); // set up drawing atsortingbutes UIColor.Black.SetStroke (); UIColor.Black.SetFill (); context.SetLineWidth (rect.Width); // start point context.MoveTo (rect.X, 0.0f); // end point context.AddLineToPoint (rect.X, rect.Y + rect.Height); // draw the path context.DrawPath (CGPathDrawingMode.Stroke); } } } 

Dans viewDidLoad de ma class conteneur, DividerView avec DividerView separator = new DividerView (); .

Mise en page automatique :

Ensuite, j'utilise Auto Layout pour mettre en page sa position (tout dans viewDidLoad ):

 separator.TranslatesAutoresizingMaskIntoConstraints = false; separatorTop = NSLayoutConstraint.Create (separator, NSLayoutAtsortingbute.Top, NSLayoutRelation.Equal, TopLayoutGuide, NSLayoutAtsortingbute.Bottom, 1, 0); separatorBottom = NSLayoutConstraint.Create (separator, NSLayoutAtsortingbute.Bottom, NSLayoutRelation.Equal, BottomLayoutGuide, NSLayoutAtsortingbute.Top, 1, 0); separatorRight = NSLayoutConstraint.Create (separator, NSLayoutAtsortingbute.Right, NSLayoutRelation.Equal, documentListController.View, NSLayoutAtsortingbute.Left, 1, 0); separatorWidth = NSLayoutConstraint.Create (separator, NSLayoutAtsortingbute.Width, NSLayoutRelation.Equal, null, NSLayoutAtsortingbute.NoAtsortingbute, 1, 1); this.View.AddSubview (separator); 

Ici les contraintes sont ajoutées:

 public override void UpdateViewConstraints () { if (!hasLoadedConstraints) { this.View.AddConstraints (new NSLayoutConstraint[] { separatorTop, separatorBottom, separatorRight, separatorWidth }); hasLoadedConstraints = true; } base.UpdateViewConstraints (); } 

Résultat :

Cette approche semble fonctionner. Maintenant, ma ligne de séparation chevauche ma vue de détail (seulement le premier point). On pourrait adapter les valeurs ou changer les contraintes du maître et de la vue détaillée pour qu'il y ait de la place entre celles du séparateur.

Alternatives :

On pourrait également append une sous-vue à la vue de contenu de chaque UITableViewCell . Des exemples peuvent être trouvés ici ou ici .