Trừ khi bạn sắp thay đổi hình dạng động, có thể bạn nên tạo hình ảnh trong trình chỉnh sửa hình ảnh. Tôi biết thật dễ dàng để tạo hiệu ứng đó trong Photoshop, Illustrator hoặc Fireworks.
Điều đó nói rằng, vẽ một inner shadow như thế với Core Graphics đòi hỏi một số bước:
- Clip với hình dạng (ví dụ sử dụng
CGContextClip
hoặc CGContextClipToMask
).
- Tạo đường dẫn hoặc mặt nạ của mọi thứ nhưng hình dạng.
- Đặt thông số bóng của bạn (sử dụng
CGContextSetShadowWithColor
).
- Điền đường dẫn hoặc mặt nạ từ bước 2. Đây đã phủ bóng đen bên trong hình dạng, và chỉ cái bóng được rút ra bởi vì bạn gài vào hình dạng trong bước 1.
Nếu bạn làm tất cả điều đó một cách chính xác, bạn có thể có được một kết quả tốt đẹp như thế này:
Dưới đây là đoạn code tôi đã viết để vẽ đó. Tôi đã viết nó trong drawRect:
của một lớp con xem tùy chỉnh, nhưng bạn có thể dễ dàng sử dụng mã này để vẽ vào bất kỳ ngữ cảnh đồ họa nào.
- (void)drawRect:(CGRect)rect {
CGContextRef gc = UIGraphicsGetCurrentContext();
Trước tiên, tôi tạo ra một con đường đó chỉ là một vòng cung:
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];
Tiếp theo, tôi hỏi Lõi đồ họa để tạo ra một con đường mới đó là phác thảo của con đường arc
. Lưu ý làm thế nào tôi hỏi nó cho một bề dày nét của kArcThickness
và vòng mũ dòng:
CGPathRef shape = CGPathCreateCopyByStrokingPath(arc.CGPath, NULL, kArcThickness, kCGLineCapRound, kCGLineJoinRound, 10.0f);
Tôi cũng cần nghịch đảo của con đường đó: một con đường mà bao gồm tất cả các điểm trừ các điểm trong shape
. Nó sẽ xảy ra (mặc dù tôi không nghĩ rằng nó được ghi lại) rằng CGContextCreateCopyByStrokingPath
và CGPathAddRect
vẽ theo các hướng ngược nhau. Vì vậy, nếu tôi sao chép shape
và vẽ một hình chữ nhật khổng lồ xung quanh nó, những khác không quanh co cai trị có nghĩa là con đường mới sẽ là nghịch đảo của shape
:
CGMutablePathRef shapeInverse = CGPathCreateMutableCopy(shape);
CGPathAddRect(shapeInverse, NULL, CGRectInfinite);
Bây giờ tôi thực sự có thể bắt đầu vẽ.Trước tiên, tôi sẽ điền vào hình dạng với màu xám nhạt:
CGContextBeginPath(gc);
CGContextAddPath(gc, shape);
CGContextSetFillColorWithColor(gc, [UIColor colorWithWhite:.9 alpha:1].CGColor);
CGContextFillPath(gc);
Tiếp theo tôi thực sự thực hiện bốn bước tôi liệt kê ở trên. Tôi phải lưu trạng thái đồ họa để tôi có thể hoàn tác các thông số clipping và shadow khi hoàn thành.
CGContextSaveGState(gc); {
Bước 1: clip vào hình dạng:
CGContextBeginPath(gc);
CGContextAddPath(gc, shape);
CGContextClip(gc);
Bước 2: Vâng, tôi đã làm bước này đã khi tôi tạo shapeInverse
.
Bước 3: Tôi thiết lập các thông số bóng:
CGContextSetShadowWithColor(gc, CGSizeZero, 7, [UIColor colorWithWhite:0 alpha:.25].CGColor);
Bước 4: Tôi điền vào hình dạng nghịch đảo từ bước 2:
CGContextBeginPath(gc);
CGContextAddPath(gc, shapeInverse);
CGContextFillPath(gc);
Bây giờ tôi khôi phục lại trạng thái đồ họa, mà đặc biệt là phục hồi lại cắt đường dẫn và bỏ các thông số bóng.
} CGContextRestoreGState(gc);
Cuối cùng, tôi sẽ đột quỵ shape
với một màu xám nhạt để làm cho crisper cạnh:
CGContextSetStrokeColorWithColor(gc, [UIColor colorWithWhite:.75 alpha:1].CGColor);
CGContextSetLineWidth(gc, 1);
CGContextSetLineJoin(gc, kCGLineCapRound);
CGContextBeginPath(gc);
CGContextAddPath(gc, shape);
CGContextStrokePath(gc);
Tất nhiên là tôi dọn dẹp khi tôi đang thực hiện:
CGPathRelease(shape);
CGPathRelease(shapeInverse);
}
Đối hình dạng phức tạp hơn, bạn có thể xem my answer here và my answer here.
Dưới đây là tất cả các mã cùng nhau để dễ dàng sao chép:
- (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);
}
Cảm ơn Rob cho mã này, nó đã thực sự hữu ích .. – Shashank
Làm việc tuyệt vời! Cảm ơn rất nhiều. Một điều tôi cần làm là đặt self.backgroundColor để xóa trong lớp con uiview tùy chỉnh có ma thuật drawRect đó. – Chris
@Shashank bạn có thể di chuyển thanh trượt đó qua đường dẫn đó không? Bạn có thể vui lòng chia sẻ mã của bạn không? – NSCry