Jeśli nie chcesz zmieniać kształtu dynamicznie, prawdopodobnie lepiej byłoby po prostu stworzyć obraz w edytorze graficznym. Wiem, że łatwo jest stworzyć ten efekt w programach Photoshop, Illustrator lub Fireworks.
Mimo to, w oparciu o wewnętrzną cień podobnego do rdzenia graficznego wymaga kilku czynności:
- klips do kształtu (na przykład za pomocą
CGContextClip
lub CGContextClipToMask
).
- Utwórz ścieżkę lub maskę wszystkiego, , ale kształt.
- Ustaw parametry cienia (używając
CGContextSetShadowWithColor
).
- Wypełnij ścieżkę lub maskę z kroku 2. To rzuca cień wewnątrz kształtu i tylko cień jest rysowany bo przycięty do kształtu w kroku 1.
Jeśli zrobisz wszystko to prawidłowo, można uzyskać ładny wynik tak:
Oto kod pisałem wyciągnąć to. Napisałem go w drawRect:
niestandardowej podklasy widoku, ale można z łatwością użyć tego kodu do rysowania w dowolnym kontekście graficznym.
- (void)drawRect:(CGRect)rect {
CGContextRef gc = UIGraphicsGetCurrentContext();
Najpierw utworzyć ścieżkę, która jest po prostu łuku:
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];
Dalej pytam rdzeń graficzny, aby utworzyć nową ścieżkę, która jest zarys ścieżki arc
. Zauważ, jak pytam go o grubości kreski kArcThickness
i okrągłych czapkach liniowych:
CGPathRef shape = CGPathCreateCopyByStrokingPath(arc.CGPath, NULL, kArcThickness, kCGLineCapRound, kCGLineJoinRound, 10.0f);
Muszę też odwrotność tej ścieżki: ścieżkę, która obejmuje każdy punkt wyjątkiem punktów w shape
. Tak się dzieje (chociaż nie sądzę, że jest to udokumentowane), że CGContextCreateCopyByStrokingPath
i CGPathAddRect
rysują w przeciwnych kierunkach. Więc jeśli mogę skopiować shape
i czerpać ogromną prostokąt wokół niego, nie-zerowy uzwojenia reguły oznacza, że nowa droga będzie odwrotnością shape
:
CGMutablePathRef shapeInverse = CGPathCreateMutableCopy(shape);
CGPathAddRect(shapeInverse, NULL, CGRectInfinite);
Teraz mogę zacząć rysować.Najpierw wypełnię kształt jasnoszarym kolorem:
CGContextBeginPath(gc);
CGContextAddPath(gc, shape);
CGContextSetFillColorWithColor(gc, [UIColor colorWithWhite:.9 alpha:1].CGColor);
CGContextFillPath(gc);
Następnie faktycznie wykonuję cztery kroki wymienione powyżej. Muszę zapisać stan grafiki, aby móc cofnąć parametry przycinania i cienia, kiedy skończę.
CGContextSaveGState(gc); {
Krok 1: klips do kształtu:
CGContextBeginPath(gc);
CGContextAddPath(gc, shape);
CGContextClip(gc);
Krok 2: Cóż, zrobiłem już ten etap, kiedy stworzył shapeInverse
.
Etap 3: ustawić parametry ukrytego:
CGContextSetShadowWithColor(gc, CGSizeZero, 7, [UIColor colorWithWhite:0 alpha:.25].CGColor);
Krok 4: wypełnić odwrotny kształt z etapu 2:
CGContextBeginPath(gc);
CGContextAddPath(gc, shapeInverse);
CGContextFillPath(gc);
teraz przywrócić stan grafiki, które specyficznie przywraca ścieżkę przycinającą i powoduje usunięcie ustawień parametrów cienia.
} CGContextRestoreGState(gc);
Wreszcie będę udar shape
z jasnoszarego aby ostrzejszy krawędzi:
CGContextSetStrokeColorWithColor(gc, [UIColor colorWithWhite:.75 alpha:1].CGColor);
CGContextSetLineWidth(gc, 1);
CGContextSetLineJoin(gc, kCGLineCapRound);
CGContextBeginPath(gc);
CGContextAddPath(gc, shape);
CGContextStrokePath(gc);
Oczywiście ja oczyścić kiedy skończę:
CGPathRelease(shape);
CGPathRelease(shapeInverse);
}
Dla bardziej złożone kształty, możesz spojrzeć na my answer here i my answer here.
Oto cały kod razem do łatwego kopiowania:
- (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);
}
Dzięki Rob kodu, to było naprawdę pomocny .. – Shashank
Works fantastyczne! Wielkie dzięki. Jedną rzeczą, którą musiałem zrobić, to ustawić self.backgroundColor, aby wyczyścić niestandardową podklasę uiview, która miała tę magię drawRect. – Chris
@Shashank Czy możesz przesunąć suwak nad tą ścieżką? Czy możesz podzielić się swoim kodem? – NSCry