2014-03-27 4 views
12

Ich fragte mich, was ist der beste Weg, um eine einzige Punktlinie zu zeichnen? Mein Ziel ist es, diese Zeile in eine TableViewCell zu zeichnen, damit sie genau wie das native Zellentrennzeichen aussieht. Ich möchte nicht den nativen Separator verwenden, weil ich in einer anderen Farbe und in einer anderen Position (nicht die Unterseite ..) machen möchte.Wie zeichne ich eine einzelne Punktlinie in iOS

Zuerst benutzte ich eine 1px UIView und färbte sie in grau. Aber in Retina Displays sieht es aus wie 2px. auch versucht, mit dieser Methode:

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

Es funktioniert aus irgendeinem Grunde wie 60% der Zeit in .. Ist etwas falsch? Wie auch immer, ich würde mich freuen, von einem besseren Weg zu hören.

Danke.

+0

Haben Sie versucht, die Alpha in einem 1px UIView zu ändern? es wird aussehen, als wäre es dünner. – streem

+0

Ich bin auf der Suche nach einem generischen Weg, und trotzdem brauche ich eine einfarbige Farbe:/ – Lirik

+1

Obwohl dies eine großartige QA war - ** hier ist eine viel modernere Lösung ** http://StackOverflow.com/a/ 34766567/294884 – Fattie

Antwort

17

Ich tat das gleiche mit einer UIView Kategorie. Hier sind meine Methoden:

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

Gerade Rémy große Antwort hinzuzufügen, ist es vielleicht sogar einfacher, dies zu tun. Eine Klasse 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 

In IB, fallen in einem UIView, klicken Sie auf Identität Inspektor und benennen Sie die Klasse zu einem UILine. Stellen Sie die Breite in IB ein. Stellen Sie die Höhe auf 1 oder 2 Pixel ein - einfach so, dass Sie es in IB sehen können. Setzen Sie die Hintergrundfarbe, die Sie in IB wollen. Wenn Sie die App ausführen, wird sie zu einer 1-Pixel-Linie, die Breite, in dieser Farbe. (Sie sollten wahrscheinlich nicht von den standardmäßigen Autoresize-Einstellungen in Storyboard/Xib betroffen sein, ich konnte es nicht zum Abbruch bringen.) Sie sind fertig.

Hinweis: Sie können denken, "Warum nicht einfach Größe der UIView in Code in watchFromNib?" Die Größe der Ansichten beim Laden in einer Storyboard-App ist problematisch - siehe die vielen Fragen dazu!

Interessante Gotchya: Es ist wahrscheinlich, dass Sie nur die UIView, sagen wir, 10 oder 20 Pixel hoch auf dem Storyboard machen, einfach, damit Sie es sehen können. Natürlich verschwindet es in der App und Sie erhalten die hübsche Pixelzeile. Aber! Denken Sie daran, sich daran zu erinnern, self.userInteractionEnabled = NO, oder es könnte über Ihre anderen, sagen wir, Knöpfe!


2016 Lösung! https://stackoverflow.com/a/34766567/294884

+0

das ist nicht das, was wir 'draw' nennen – Laszlo

+2

@ TussLászló ja, aber das Problem zu lösen. –

+1

Dies ist genau Zeichnung. Ascii Kunst ist Zeichnung. Sie würden oft sagen "Ich habe das Eingabeformular mit Quadraten, Ansichten, UIButtons, drawRect und anderen Techniken gezeichnet". Danke für die tolle Antwort, Rémy! – Fattie

2
shapeLayer.lineWidth = 0.5f; 

Das ist ein weit verbreiteter Fehler ist und ist der Grund, dies einen Teil der Zeit nur funktioniert. Manchmal überlappen sich die Pixel auf dem Bildschirm genau und manchmal nicht. Die Methode zum Zeichnen einer Einzelpunktlinie, die immer funktioniert, ist das Zeichnen eines Ein-Punkt-dicken Rechtecks ​​ auf Integer Grenzen und füllen es. Auf diese Weise wird immer genau die Pixel auf dem Bildschirm übereinstimmen.

Um von Punkten zu Pixeln zu konvertieren, verwenden Sie den Maßstabsfaktor der Ansicht, wenn Sie dies tun möchten.

So wird dies immer ein Pixel breit sein:

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

Hier ist ein Screenshot der Linie als Trennzeichen verwendet wird, an der Spitze jeder Zelle gezogen:

enter image description here

Die Tabellenansicht selbst hat keine Trennzeichen (wie der Leerraum unter den drei vorhandenen Zellen zeigt). Ich zeichne nicht die Linie in der Position, Länge und Farbe, die du willst, aber das ist deine Sorge, nicht meine.

+0

Ok, ich habe versucht, diese Codezeilen (genannt Form drawRect: Methode) - und nichts ist gezeichnet, kann das Problem sehen ?: CGContextRef context = UIGraphicsGetCurrentContext(); CGContextFillRect (Kontext, CGRectMake (startPoint.x, startPoint.y, Breite, 1.0/self.contentScaleFactor)); – Lirik

+1

Es funktioniert. Ich habe einen Screenshot hinzugefügt. – matt

+0

@matt, weißt du zufällig: wenn man den "UIView height" -Trick benutzt, erwähnt Rémy unten. (Vermutlich mit 1.0/self.contentScaleFactor und nicht nur mit "1/2" - es sei denn, Sie sind ausschließlich für die Retina zuständig.) Tatsächlich. Wird das ** nicht ganzzahlige Randproblem leiden? Ich konnte die nicht-ganzzahlige Grenze nicht "ausstellen", aber ich habe es vielleicht nicht hart genug versucht :) Ich denke, wenn Sie eine UIView vom Interface Builder einfügen, wird sie tatsächlich immer auf einer Integer-Grenze liegen (denke ich). Danke, wenn Sie wissen, Prost – Fattie

2

Automatisches Layout-Methode:

Ich benutze eine einfache alte UIView und stellen Sie die Höhe Beschränkung auf 1 in Interface Builder. Über Beschränkungen an den Boden angehängt. Mit dem Builder für Schnittstellen können Sie die Höhenkonstruktion nicht auf 0,5 festlegen. Sie können dies jedoch im Code tun.

Sie einen Anschluss für die Höhenbeschränkung, dann nennen dies:

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

für mich gearbeitet.

FWIW Ich glaube nicht, dass wir Nicht-Retina-Bildschirme mehr unterstützen müssen. Allerdings benutze ich immer noch den Hauptbildschirm, um die App zukunftssicher zu machen.

0

Zusatz Rémy Virin Antwort, mit Swift 3.0 LineSeparator Klasse erstellen:

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