内部阴影效果UIView层?

我有以下CALayer:

CAGradientLayer *gradient = [CAGradientLayer layer]; gradient.frame = CGRectMake(8, 57, 296, 30); gradient.cornerRadius = 3.0f; gradient.colors = [NSArray arrayWithObjects:(id)[RGB(130, 0, 140) CGColor], (id)[RGB(108, 0, 120) CGColor], nil]; [self.layer insertSublayer:gradient atIndex:0]; 

我想添加一个内部的阴影效果,但我不太清楚如何做到这一点。 我想我会被要求绘制drawRect,但是这将添加在其他UIView对象的顶层,因为它应该是一些button后面的酒吧,所以我不知道该怎么做?

我可以添加另一个图层,但是又不知道如何实现内部的阴影效果(如下所示:

在这里输入图像说明

帮助赞赏…

对于其他人想知道如何使用Core Graphics按照Costique的build议来绘制内部阴影,那么这就是:(在iOS上根据需要进行调整)

在你的drawRect方法中…

 CGRect bounds = [self bounds]; CGContextRef context = UIGraphicsGetCurrentContext(); CGFloat radius = 0.5f * CGRectGetHeight(bounds); // Create the "visible" path, which will be the shape that gets the inner shadow // In this case it's just a rounded rect, but could be as complex as your want CGMutablePathRef visiblePath = CGPathCreateMutable(); CGRect innerRect = CGRectInset(bounds, radius, radius); CGPathMoveToPoint(visiblePath, NULL, innerRect.origin.x, bounds.origin.y); CGPathAddLineToPoint(visiblePath, NULL, innerRect.origin.x + innerRect.size.width, bounds.origin.y); CGPathAddArcToPoint(visiblePath, NULL, bounds.origin.x + bounds.size.width, bounds.origin.y, bounds.origin.x + bounds.size.width, innerRect.origin.y, radius); CGPathAddLineToPoint(visiblePath, NULL, bounds.origin.x + bounds.size.width, innerRect.origin.y + innerRect.size.height); CGPathAddArcToPoint(visiblePath, NULL, bounds.origin.x + bounds.size.width, bounds.origin.y + bounds.size.height, innerRect.origin.x + innerRect.size.width, bounds.origin.y + bounds.size.height, radius); CGPathAddLineToPoint(visiblePath, NULL, innerRect.origin.x, bounds.origin.y + bounds.size.height); CGPathAddArcToPoint(visiblePath, NULL, bounds.origin.x, bounds.origin.y + bounds.size.height, bounds.origin.x, innerRect.origin.y + innerRect.size.height, radius); CGPathAddLineToPoint(visiblePath, NULL, bounds.origin.x, innerRect.origin.y); CGPathAddArcToPoint(visiblePath, NULL, bounds.origin.x, bounds.origin.y, innerRect.origin.x, bounds.origin.y, radius); CGPathCloseSubpath(visiblePath); // Fill this path UIColor *aColor = [UIColor redColor]; [aColor setFill]; CGContextAddPath(context, visiblePath); CGContextFillPath(context); // Now create a larger rectangle, which we're going to subtract the visible path from // and apply a shadow CGMutablePathRef path = CGPathCreateMutable(); //(when drawing the shadow for a path whichs bounding box is not known pass "CGPathGetPathBoundingBox(visiblePath)" instead of "bounds" in the following line:) //-42 cuould just be any offset > 0 CGPathAddRect(path, NULL, CGRectInset(bounds, -42, -42)); // Add the visible path (so that it gets subtracted for the shadow) CGPathAddPath(path, NULL, visiblePath); CGPathCloseSubpath(path); // Add the visible paths as the clipping path to the context CGContextAddPath(context, visiblePath); CGContextClip(context); // Now setup the shadow properties on the context aColor = [UIColor colorWithRed:0.0f green:0.0f blue:0.0f alpha:0.5f]; CGContextSaveGState(context); CGContextSetShadowWithColor(context, CGSizeMake(0.0f, 1.0f), 3.0f, [aColor CGColor]); // Now fill the rectangle, so the shadow gets drawn [aColor setFill]; CGContextSaveGState(context); CGContextAddPath(context, path); CGContextEOFillPath(context); // Release the paths CGPathRelease(path); CGPathRelease(visiblePath); 

所以,基本上有以下几个步骤:

  1. 创build你的path
  2. 设置所需的填充颜色,将此path添加到上下文,并填充上下文
  3. 现在创build一个可以绑定可见path的较大的矩形。 在closures此path之前,添加可见path。 然后closurespath,以便创build一个可视path与其相减的形状。 您可能需要调查填充方法(偶数/奇数的非零卷绕),具体取决于您创build这些path的方式。 从本质上来说,为了让子path在“加减”的时候将它们加在一起,就需要在相反的方向上绘制它们(或者更确切地说是构造它们),一个顺时针旋转,另一个逆时针旋转。
  4. 然后,您需要将您的可见path设置为上下文中的剪切path,以便您不在屏幕之外绘制任何东西。
  5. 然后在上下文中设置阴影,其中包括偏移,模糊和颜色。
  6. 然后填充大孔的形状。 颜色并不重要,因为如果你做得对,你就不会看到这种颜色,只有阴影。

我知道我迟到了,但是这会帮助我在旅途中早日find…

为了在信贷到期时给予贷款,这实质上是Daniel Thorpe对Costique从更大区域减去较小区域的解决scheme的修改。 这个版本是为那些使用图层组成而不是覆盖-drawRect:

CAShapeLayer类可以用来达到相同的效果:

 CAShapeLayer* shadowLayer = [CAShapeLayer layer]; [shadowLayer setFrame:[self bounds]]; // Standard shadow stuff [shadowLayer setShadowColor:[[UIColor colorWithWhite:0 alpha:1] CGColor]]; [shadowLayer setShadowOffset:CGSizeMake(0.0f, 0.0f)]; [shadowLayer setShadowOpacity:1.0f]; [shadowLayer setShadowRadius:5]; // Causes the inner region in this example to NOT be filled. [shadowLayer setFillRule:kCAFillRuleEvenOdd]; // Create the larger rectangle path. CGMutablePathRef path = CGPathCreateMutable(); CGPathAddRect(path, NULL, CGRectInset(bounds, -42, -42)); // Add the inner path so it's subtracted from the outer path. // someInnerPath could be a simple bounds rect, or maybe // a rounded one for some extra fanciness. CGPathAddPath(path, NULL, someInnerPath); CGPathCloseSubpath(path); [shadowLayer setPath:path]; CGPathRelease(path); [[self layer] addSublayer:shadowLayer]; 

在这一点上,如果你的父层没有遮住它的边界,你会看到图层边缘附近的遮罩层的额外区域。 如果直接复制示例,这将是42像素的黑色。 要摆脱它,你可以简单地使用另一个CAShapeLayer具有相同的path,并将其设置为阴影层的蒙版:

 CAShapeLayer* maskLayer = [CAShapeLayer layer]; [maskLayer setPath:someInnerPath]; [shadowLayer setMask:maskLayer]; 

我自己并没有对此进行基准testing,但是我怀疑将这种方法与光栅化结合使用会-drawRect:更加-drawRect:

通过在边界之外制作一个大的矩形path,减去一个边界大小的矩形path,并用一个“普通”阴影填充结果path,可以用Core Graphics绘制一个内部阴影。

但是,由于需要将其与渐变图层相结合,因此我认为更简单的方法是创build一个内部阴影的9部分透明PNG图像,并将其拉伸至合适的大小。 9部分阴影图像看起来像这样(它的大小是21×21像素):

替代文字

 CALayer *innerShadowLayer = [CALayer layer]; innerShadowLayer.contents = (id)[UIImage imageNamed: @"innershadow.png"].CGImage; innerShadowLayer.contentsCenter = CGRectMake(10.0f/21.0f, 10.0f/21.0f, 1.0f/21.0f, 1.0f/21.0f); 

然后设置innerShadowLayer的框架,它应该适当地拉伸阴影。

一个循环的方式,但它避免了必须使用图像(阅读:容易改变颜色,阴影半径等),它只是几行代码。

  1. 添加一个UIImageView作为你想要阴影的UIView的第一个子视图。 我使用IB,但是可以通过编程来实现。

  2. 假设对UIImageView的引用是“innerShadow”

`

 [[innerShadow layer] setMasksToBounds:YES]; [[innerShadow layer] setCornerRadius:12.0f]; [[innerShadow layer] setBorderColor:[UIColorFromRGB(180, 180, 180) CGColor]]; [[innerShadow layer] setBorderWidth:1.0f]; [[innerShadow layer] setShadowColor:[UIColorFromRGB(0, 0, 0) CGColor]]; [[innerShadow layer] setShadowOffset:CGSizeMake(0, 0)]; [[innerShadow layer] setShadowOpacity:1]; [[innerShadow layer] setShadowRadius:2.0]; 

警告:你必须有一个边界,否则阴影不显示。 [UIColor clearColor]不起作用。 在这个例子中,我使用了一种不同的颜色,但是你可以把它弄得和阴影开始的颜色一样。 🙂

请参阅下面有关UIColorFromRGBmacros的bbrame的注释。

迟到总比不到好…

这是另一种方法,可能不会比已发布的更好,但它很好,很简单 –

 -(void)drawInnerShadowOnView:(UIView *)view { UIImageView *innerShadowView = [[UIImageView alloc] initWithFrame:view.bounds]; innerShadowView.contentMode = UIViewContentModeScaleToFill; innerShadowView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; [view addSubview:innerShadowView]; [innerShadowView.layer setMasksToBounds:YES]; [innerShadowView.layer setBorderColor:[UIColor lightGrayColor].CGColor]; [innerShadowView.layer setShadowColor:[UIColor blackColor].CGColor]; [innerShadowView.layer setBorderWidth:1.0f]; [innerShadowView.layer setShadowOffset:CGSizeMake(0, 0)]; [innerShadowView.layer setShadowOpacity:1.0]; // this is the inner shadow thickness [innerShadowView.layer setShadowRadius:1.5]; } 

在Swift中使用CALayer的简化版本:

 import UIKit final class FrameView : UIView { init() { super.init(frame: CGRect.zero) backgroundColor = UIColor.white } @available(*, unavailable) required init?(coder decoder: NSCoder) { fatalError("unavailable") } override func layoutSubviews() { super.layoutSubviews() addInnerShadow() } private func addInnerShadow() { let innerShadow = CALayer() innerShadow.frame = bounds // Shadow path (1pt ring around bounds) let path = UIBezierPath(rect: innerShadow.bounds.insetBy(dx: -1, dy: -1)) let cutout = UIBezierPath(rect: innerShadow.bounds).reversing() path.append(cutout) innerShadow.shadowPath = path.cgPath innerShadow.masksToBounds = true // Shadow properties innerShadow.shadowColor = UIColor(white: 0, alpha: 1).cgColor // UIColor(red: 0.71, green: 0.77, blue: 0.81, alpha: 1.0).cgColor innerShadow.shadowOffset = CGSize.zero innerShadow.shadowOpacity = 1 innerShadow.shadowRadius = 3 // Add layer.addSublayer(innerShadow) } } 

请注意,InnerShadow图层不应具有不透明的背景颜色,因为它将在阴影前呈现。

而不是通过drawRect绘制内部阴影或将UIView添加到视图。 您可以直接将CALayer添加到边界,例如:如果我想在UIView V底部的内部阴影效果。

 innerShadowOwnerLayer = [[CALayer alloc]init]; innerShadowOwnerLayer.frame = CGRectMake(0, V.frame.size.height+2, V.frame.size.width, 2); innerShadowOwnerLayer.backgroundColor = [UIColor whiteColor].CGColor; innerShadowOwnerLayer.shadowColor = [UIColor blackColor].CGColor; innerShadowOwnerLayer.shadowOffset = CGSizeMake(0, 0); innerShadowOwnerLayer.shadowRadius = 10.0; innerShadowOwnerLayer.shadowOpacity = 0.7; [V.layer addSubLayer:innerShadowOwnerLayer]; 

这为目标UIView添加一个底部的内部阴影

这里是一个swift版本,改变startPointendPoint使它在每一边。

  let layer = CAGradientLayer() layer.startPoint = CGPointMake(0.5, 0.0); layer.endPoint = CGPointMake(0.5, 1.0); layer.colors = [UIColor(white: 0.1, alpha: 1.0).CGColor, UIColor(white: 0.1, alpha: 0.5).CGColor, UIColor.clearColor().CGColor] layer.locations = [0.05, 0.2, 1.0 ] layer.frame = CGRectMake(0, 0, self.view.frame.width, 60) self.view.layer.insertSublayer(layer, atIndex: 0) 

我很晚了,但我想回到社区..这是我写的一个方法来删除UITextField背景图像,因为我提供了一个静态库和没有资源…我用这个四个UITextField实例的PINinput屏幕,可以在ViewController中显示一个原始字符或(BOOL)[self isUsingBullets]或(BOOL)[self usingAsterisk]。 应用程序是iPhone / iPhone视网膜/ iPad / iPad的视网膜,所以我不必提供四个图像…

 #import <QuartzCore/QuartzCore.h> - (void)setTextFieldInnerGradient:(UITextField *)textField { [textField setSecureTextEntry:self.isUsingBullets]; [textField setBackgroundColor:[UIColor blackColor]]; [textField setTextColor:[UIColor blackColor]]; [textField setBorderStyle:UITextBorderStyleNone]; [textField setClipsToBounds:YES]; [textField.layer setBorderColor:[[UIColor blackColor] CGColor]]; [textField.layer setBorderWidth:1.0f]; // make a gradient off-white background CAGradientLayer *gradient = [CAGradientLayer layer]; CGRect gradRect = CGRectInset([textField bounds], 3, 3); // Reduce Width and Height and center layer gradRect.size.height += 2; // minimise Bottom shadow, rely on clipping to remove these 2 pts. gradient.frame = gradRect; struct CGColor *topColor = [UIColor colorWithWhite:0.6f alpha:1.0f].CGColor; struct CGColor *bottomColor = [UIColor colorWithWhite:0.9f alpha:1.0f].CGColor; // We need to use this fancy __bridge object in order to get the array we want. gradient.colors = [NSArray arrayWithObjects:(__bridge id)topColor, (__bridge id)bottomColor, nil]; [gradient setCornerRadius:4.0f]; [gradient setShadowOffset:CGSizeMake(0, 0)]; [gradient setShadowColor:[[UIColor whiteColor] CGColor]]; [gradient setShadowOpacity:1.0f]; [gradient setShadowRadius:3.0f]; // Now we need to Blur the edges of this layer "so it blends" // This rasterizes the view down to 4x4 pixel chunks then scales it back up using bilinear filtering... // it's EXTREMELY fast and looks ok if you are just wanting to blur a background view under a modal view. // To undo it, just set the rasterization scale back to 1.0 or turn off rasterization. [gradient setRasterizationScale:0.25]; [gradient setShouldRasterize:YES]; [textField.layer insertSublayer:gradient atIndex:0]; if (self.usingAsterisks) { [textField setFont:[UIFont systemFontOfSize:80.0]]; } else { [textField setFont:[UIFont systemFontOfSize:40.0]]; } [textField setTextAlignment:UITextAlignmentCenter]; [textField setEnabled:NO]; } 

我希望这能帮助别人,因为这个论坛帮助了我。

这是您从PaintCode导出的解决scheme:

 -(void) drawRect:(CGRect)rect { CGContextRef context = UIGraphicsGetCurrentContext(); //// Shadow Declarations UIColor* shadow = UIColor.whiteColor; CGSize shadowOffset = CGSizeMake(0, 0); CGFloat shadowBlurRadius = 10; //// Rectangle Drawing UIBezierPath* rectanglePath = [UIBezierPath bezierPathWithRect: self.bounds]; [[UIColor blackColor] setFill]; [rectanglePath fill]; ////// Rectangle Inner Shadow CGContextSaveGState(context); UIRectClip(rectanglePath.bounds); CGContextSetShadowWithColor(context, CGSizeZero, 0, NULL); CGContextSetAlpha(context, CGColorGetAlpha([shadow CGColor])); CGContextBeginTransparencyLayer(context, NULL); { UIColor* opaqueShadow = [shadow colorWithAlphaComponent: 1]; CGContextSetShadowWithColor(context, shadowOffset, shadowBlurRadius, [opaqueShadow CGColor]); CGContextSetBlendMode(context, kCGBlendModeSourceOut); CGContextBeginTransparencyLayer(context, NULL); [opaqueShadow setFill]; [rectanglePath fill]; CGContextEndTransparencyLayer(context); } CGContextEndTransparencyLayer(context); CGContextRestoreGState(context); } 

查看Chris Emery 在Quartz里的“ Inner Shadows”这篇伟大的文章,它解释了PaintCode是如何绘制内部阴影的,并给出了一个整洁的代码片段:

 - (void)drawInnerShadowInContext:(CGContextRef)context withPath:(CGPathRef)path shadowColor:(CGColorRef)shadowColor offset:(CGSize)offset blurRadius:(CGFloat)blurRadius { CGContextSaveGState(context); CGContextAddPath(context, path); CGContextClip(context); CGColorRef opaqueShadowColor = CGColorCreateCopyWithAlpha(shadowColor, 1.0); CGContextSetAlpha(context, CGColorGetAlpha(shadowColor)); CGContextBeginTransparencyLayer(context, NULL); CGContextSetShadowWithColor(context, offset, blurRadius, opaqueShadowColor); CGContextSetBlendMode(context, kCGBlendModeSourceOut); CGContextSetFillColorWithColor(context, opaqueShadowColor); CGContextAddPath(context, path); CGContextFillPath(context); CGContextEndTransparencyLayer(context); CGContextRestoreGState(context); CGColorRelease(opaqueShadowColor); } 

这里有一些代码可以为你做这个。 如果您更改视图中的图层(通过覆盖+ (Class)layerClass ),则可以在JTAInnerShadowLayer中将缩进图层上的内部阴影设置在init方法中,它将为您完成工作。 如果您还想绘制原始内容,请确保在缩进图层上调用setDrawOriginalImage:yes 。 有一篇关于这个如何工作的博客文章。

使用渐变层:

 UIView * mapCover = [UIView new]; mapCover.frame = map.frame; [view addSubview:mapCover]; CAGradientLayer * vertical = [CAGradientLayer layer]; vertical.frame = mapCover.bounds; vertical.colors = [NSArray arrayWithObjects:(id)[UIColor whiteColor].CGColor, (id)[[UIColor whiteColor] colorWithAlphaComponent:0.0f].CGColor, (id)[[UIColor whiteColor] colorWithAlphaComponent:0.0f].CGColor, (id)[UIColor whiteColor].CGColor, nil]; vertical.locations = @[@0.01,@0.1,@0.9,@0.99]; [mapCover.layer insertSublayer:vertical atIndex:0]; CAGradientLayer * horizontal = [CAGradientLayer layer]; horizontal.frame = mapCover.bounds; horizontal.colors = [NSArray arrayWithObjects:(id)[UIColor whiteColor].CGColor, (id)[[UIColor whiteColor] colorWithAlphaComponent:0.0f].CGColor, (id)[[UIColor whiteColor] colorWithAlphaComponent:0.0f].CGColor, (id)[UIColor whiteColor].CGColor, nil]; horizontal.locations = @[@0.01,@0.1,@0.9,@0.99]; horizontal.startPoint = CGPointMake(0.0, 0.5); horizontal.endPoint = CGPointMake(1.0, 0.5); [mapCover.layer insertSublayer:horizontal atIndex:0]; 
Interesting Posts