2013-05-02 8 views
12

Desidero disegnare una linea a due colori con linee 1px di 2 colori diversi. Ho un sacco di codice quindi lo sto mettendo dopo aver provato a spiegare.Non è possibile disegnare il drawer di CALayer in righe 1px su display retina

Se uso UIView drawRect metodo funziona bene quando spengo antialiasing ma quando uso di drawLayerInContext strato che sia visualizzato una linea normale, o 2 binari di righe di 2px e antialiasing off o due mezze trasparente linee di 2px con antialiasing sopra.

sono riuscito ad ottenere un comportamento simile che il metodo drawRect del UIView con la creazione di un'immagine con un contesto personalizzato dove posso specificare una scala:

UIGraphicsBeginImageContextWithOptions([self bounds].size, NO, 0.f); // 0.f for scale means "scale for device's main screen". 

Vorrei solo sapere perché non ho ricevuto la stesso comportamento in drawInContext e se ci fosse un modo per ottenere un comportamento simile.

Qui è il codice che disegna la linea di colore doppia:

void DRAW_DOUBLE_LINE(CGContextRef ctx, CGPoint startPoint, CGPoint endPoint, UIColor* topColor, UIColor* bottomColor) 
{ 
    UIGraphicsPushContext(ctx); 

    UIBezierPath *topLine = [[UIBezierPath alloc] init]; 
    CGPoint topLineStartPoint = startPoint; 
    CGPoint topLineEndPoint = endPoint; 
    [topLine moveToPoint:topLineStartPoint]; 
    [topLine addLineToPoint:topLineEndPoint]; 
    [topColor setStroke]; 
    topLine.lineWidth = 0.5; 
    [topLine stroke]; 

    UIBezierPath *bottomLine = [[UIBezierPath alloc] init]; 
    CGPoint bottomLineStartPoint = topLineStartPoint; 
    bottomLineStartPoint.y +=0.5; 
    CGPoint bottomLineEndPoint = topLineEndPoint; 
    bottomLineEndPoint.y +=0.5; 
    [bottomLine moveToPoint:bottomLineStartPoint]; 
    [bottomLine addLineToPoint:bottomLineEndPoint]; 
    [bottomColor setStroke]; 
    bottomLine.lineWidth = 0.5; 
    [bottomLine stroke]; 

    UIGraphicsPopContext(); 
} 

Con il metodo di UIViewdrawRect ottengo questo:

 
| Points y coordinate | Antialiasing | Result       | 
| ------------------- | ------------ | ------------------------------- | 
| 5     | NO   | 2 lines of 1 px: Bingo!   | 
| 5.25    | NO   | 2 lines of 1 px: Bingo!   | 
| 5.5     | NO   | 2 lines of 1 px: Bingo!   | 
| 5     | YES   | 3 half transparent lines of 1px | 
| 5.25    | YES   | 2 lines of 1 px: Bingo!   | 
| 5.5     | YES   | 3 half transparent lines of 1px | 

In un CALayer con drawInContext ottengo questi risultati

 
| Points y coordinate | Antialiasing | Result       | 
| ------------------- | ------------ | ------------------------------- | 
| 5     | NO   | 2 lines of 2 px     | 
| 5.25    | NO   | 1 line of 2 px     | 
| 5.5     | NO   | 1 line of 2 px     | 
| 5     | YES   | 2 half transparent lines of 2px | 
| 5.25    | YES   | 1 half transparent line of 2px | 
| 5.5     | YES   | 2 half transparent lines of 2px | 

Utilizzando il mio contesto personalizzato ottengo questo:

 
| Points y coordinate | Antialiasing | Result       | 
| ------------------- | ------------ | ------------------------------- | 
| 5     | NO   | 2 lines of 1 px: Bingo!  | 
| 5.25    | NO   | 2 lines of 1 px: Bingo!  | 
| 5.5     | NO   | 2 lines of 1 px: Bingo!  | 
| 5     | YES   | 3 half transparent lines of 1px | 
| 5.25    | YES   | 2 lines of 1 px: Bingo!  | 
| 5.5     | YES   | 3 half transparent lines of 1px | 

Codice in drawRect realizzazione:

- (void)drawRect:(CGRect)rect 
{ 
    CGContextRef context = UIGraphicsGetCurrentContext(); 

    CGPoint startPoint = CGPointMake(0, 5); 
    CGPoint endPoint = CGPointMake(CGRectGetMaxX(self.bounds),5); 

    UIColor* topLineColor = [UIColor whiteColor]; 
    UIColor* bottomLineColor = [UIColor blackColor]; 

    DRAW_DOUBLE_LINE(context, startPoint, endPoint, topLineColor, bottomLineColor); 
} 

Codice in drawInContext realizzazione:

-(void)drawInContext:(CGContextRef)ctx{ 
    CGPoint startPoint = CGPointMake(0, 5); 
    CGPoint endPoint = CGPointMake(CGRectGetMaxX(self.bounds),5); 

    UIColor* topLineColor = [UIColor whiteColor]; 
    UIColor* bottomLineColor = [UIColor blackColor]; 

    DRAW_DOUBLE_LINE(ctx, startPoint, endPoint, topLineColor, bottomLineColor); 
} 

codice per l'attuazione contesto personalizzato nel metodo di CALayer display:

-(void)display{ 

    if ([UIScreen instancesRespondToSelector:@selector(scale)]) { 
     UIGraphicsBeginImageContextWithOptions([self bounds].size, NO, 0.f); // 0.f for scale means "scale for device's main screen". 
    } else { 
     UIGraphicsBeginImageContext([self bounds].size); 
    } 

    CGContextRef context = UIGraphicsGetCurrentContext(); 

    CGPoint startPoint = CGPointMake(0, 5.25); 
    CGPoint endPoint = CGPointMake(CGRectGetMaxX(self.bounds),5.25); 
    UIColor* topLineColor = [UIColor whiteColor]; 
    UIColor* bottomLineColor = [UIColor blackColor]; 

    DRAW_DOUBLE_LINE(ctx, startPoint, endPoint, topLineColor, bottomLineColor); 

    UIImage *coloredImg = UIGraphicsGetImageFromCurrentImageContext(); 
    UIGraphicsEndImageContext(); 

    self.contents = (id)coloredImg.CGImage; 
} 
+0

Hai provato cambiando 'contentsScale'? – cahn

risposta

0

Suggerirei invece di disegnare linee, semplicemente disegnare rettangoli pieni della dimensione desiderata. Una delle parti più difficili con risoluzioni diverse sono le posizioni dei punti di inizio e fine delle linee. Ma disegnare rettangoli è molto più facile.

Le coordinate per i rettangoli sono sempre uniformi, poiché si mira al bordo e non al centro di un pixel.

0

Disegna e riempie i rettangoli anziché le linee. Le coordinate per i rettangoli dovrebbero essere sempre al margine di un pixel e non al centro.

1

NON USARE 0,5 come larghezza.

sostituire 0.5 con 1.0/[UIScreen mainScreen].scale

Quando si disegna sul livello che si può ottenere 1 pixel linea di larghezza.

0

Il modo migliore che ho trovato per ottenere linee 1px precise/nitide su entrambe le retina/non-retina e mantenerle come 1px anche se lo zoom è di scrivere uno shader. Nel mio caso stavo usando OpenGL, ma credo che potreste farlo con Metal for CALayers.Il processo di base consiste nel disegnare un rettangolo di altezza zero (o larghezza zero se la linea è verticale) e quindi nello shader spingere quei punti verso l'esterno del numero desiderato di pixel.

ho avuto l'idea da qui: https://www.mapbox.com/blog/drawing-antialiased-lines/