A meno che non si stia cambiando la forma in modo dinamico, sarebbe probabilmente meglio semplicemente creare l'immagine in un editor di immagini. So che è facile creare quell'effetto in Photoshop, Illustrator o Fireworks.
Detto, disegnando un'ombra interna così con core grafico richiede diversi passaggi:
- clip alla forma (ad esempio utilizzando
CGContextClip
o CGContextClipToMask
).
- Creare un percorso o una maschera di tutto ma la forma.
- Impostare i parametri dell'ombra (utilizzando
CGContextSetShadowWithColor
).
- Riempire il percorso o maschera dal punto 2. Questo getta un'ombra all'interno della forma, e solo l'ombra è disegnato perché si troncato per la forma al punto 1.
Se fai tutto questo correttamente, è possibile ottenere un bel risultato come questo:
Ecco il codice che ho scritto a disegnare quella. L'ho scritto nel drawRect:
di una sottoclasse di visualizzazione personalizzata, ma puoi facilmente utilizzare questo codice per disegnare in qualsiasi contesto grafico.
- (void)drawRect:(CGRect)rect {
CGContextRef gc = UIGraphicsGetCurrentContext();
In primo luogo, crea un percorso che è solo un arco:
static CGFloat const kArcThickness = 20.0f;
CGRect arcBounds = CGRectInset(self.bounds, 10.0f, 10.0f);
CGPoint arcCenter = CGPointMake(CGRectGetMidX(arcBounds), CGRectGetMidY(arcBounds));
CGFloat arcRadius = 0.5f * (MIN(arcBounds.size.width, arcBounds.size.height) - kArcThickness);
UIBezierPath *arc = [UIBezierPath bezierPathWithArcCenter:arcCenter radius:arcRadius startAngle:-M_PI/3.0 endAngle:-2.0 * M_PI/3.0 clockwise:NO];
Avanti, chiedo core grafico per fare un nuovo percorso che è il contorno del percorso arc
. Si noti come lo chiedo per uno spessore del tratto di kArcThickness
e tondi tappi di linea:
CGPathRef shape = CGPathCreateCopyByStrokingPath(arc.CGPath, NULL, kArcThickness, kCGLineCapRound, kCGLineJoinRound, 10.0f);
ho bisogno anche l'inverso di quel percorso: un percorso che include tutti i punti tranne i punti in shape
. Succede (anche se non penso sia documentato) che CGContextCreateCopyByStrokingPath
e CGPathAddRect
disegnino in direzioni opposte. Quindi, se copio shape
e disegnare un enorme rettangolo intorno ad esso, il non-zero regola significa che il nuovo percorso sarà l'inverso di shape
:
CGMutablePathRef shapeInverse = CGPathCreateMutableCopy(shape);
CGPathAddRect(shapeInverse, NULL, CGRectInfinite);
ora posso davvero iniziare a disegnare.Innanzitutto, compilerò la forma con un colore grigio chiaro:
CGContextBeginPath(gc);
CGContextAddPath(gc, shape);
CGContextSetFillColorWithColor(gc, [UIColor colorWithWhite:.9 alpha:1].CGColor);
CGContextFillPath(gc);
Avanti In realtà eseguo i quattro passaggi sopra elencati. Devo salvare lo stato della grafica in modo da poter annullare i parametri di ritaglio e ombra quando ho finito.
CGContextSaveGState(gc); {
Fase 1: clip per la forma:
CGContextBeginPath(gc);
CGContextAddPath(gc, shape);
CGContextClip(gc);
Fase 2: Beh, ho fatto questo passo già quando ho creato shapeInverse
.
Fase 3: I impostare i parametri ombra:
CGContextSetShadowWithColor(gc, CGSizeZero, 7, [UIColor colorWithWhite:0 alpha:.25].CGColor);
Fase 4: I riempire la forma inversa di passaggio 2:
CGContextBeginPath(gc);
CGContextAddPath(gc, shapeInverse);
CGContextFillPath(gc);
Ora ripristinare lo stato grafica, che ripristina particolare il tracciato di ritaglio e disattiva i parametri ombra.
} CGContextRestoreGState(gc);
Infine, io ictus shape
con un grigio chiaro per rendere il più nitide bordo:
CGContextSetStrokeColorWithColor(gc, [UIColor colorWithWhite:.75 alpha:1].CGColor);
CGContextSetLineWidth(gc, 1);
CGContextSetLineJoin(gc, kCGLineCapRound);
CGContextBeginPath(gc);
CGContextAddPath(gc, shape);
CGContextStrokePath(gc);
Naturalmente mi pulisco quando ho finito:
CGPathRelease(shape);
CGPathRelease(shapeInverse);
}
Per forme più complesse, è possibile guardare my answer here e my answer here.
Ecco tutto il codice per semplificarne la copia:
- (void)drawRect:(CGRect)rect {
CGContextRef gc = UIGraphicsGetCurrentContext();
static CGFloat const kArcThickness = 20.0f;
CGRect arcBounds = CGRectInset(self.bounds, 10.0f, 10.0f);
CGPoint arcCenter = CGPointMake(CGRectGetMidX(arcBounds), CGRectGetMidY(arcBounds));
CGFloat arcRadius = 0.5f * (MIN(arcBounds.size.width, arcBounds.size.height) - kArcThickness);
UIBezierPath *arc = [UIBezierPath bezierPathWithArcCenter:arcCenter radius:arcRadius startAngle:-M_PI/3.0 endAngle:-2.0 * M_PI/3.0 clockwise:NO];
CGPathRef shape = CGPathCreateCopyByStrokingPath(arc.CGPath, NULL, kArcThickness, kCGLineCapRound, kCGLineJoinRound, 10.0f);
CGMutablePathRef shapeInverse = CGPathCreateMutableCopy(shape);
CGPathAddRect(shapeInverse, NULL, CGRectInfinite);
CGContextBeginPath(gc);
CGContextAddPath(gc, shape);
CGContextSetFillColorWithColor(gc, [UIColor colorWithWhite:.9 alpha:1].CGColor);
CGContextFillPath(gc);
CGContextSaveGState(gc); {
CGContextBeginPath(gc);
CGContextAddPath(gc, shape);
CGContextClip(gc);
CGContextSetShadowWithColor(gc, CGSizeZero, 7, [UIColor colorWithWhite:0 alpha:.25].CGColor);
CGContextBeginPath(gc);
CGContextAddPath(gc, shapeInverse);
CGContextFillPath(gc);
} CGContextRestoreGState(gc);
CGContextSetStrokeColorWithColor(gc, [UIColor colorWithWhite:.75 alpha:1].CGColor);
CGContextSetLineWidth(gc, 1);
CGContextSetLineJoin(gc, kCGLineCapRound);
CGContextBeginPath(gc);
CGContextAddPath(gc, shape);
CGContextStrokePath(gc);
CGPathRelease(shape);
CGPathRelease(shapeInverse);
}
Grazie Rob per il codice, è stato davvero utile .. – Shashank
Funziona fantastico! Molte grazie. Una cosa che dovevo fare era impostare self.backgroundColor per cancellare nella sottoclasse uiview personalizzata che aveva la magia drawRect. – Chris
@Shashank sei in grado di spostare quel cursore su quel percorso? Puoi per favore condividere il tuo codice? – NSCry