UIView图层的内阴影效果?

run*_*mad 91 iphone core-graphics calayer

我有以下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];
Run Code Online (Sandbox Code Playgroud)

我想为它添加内部阴影效果,但我不太清楚如何做到这一点.我想我会被要求在drawRect中绘制,但是这会在其他UIView对象之上添加图层,因为它应该是一些按钮背后的条形图,所以我不知道该怎么做?

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

在此输入图像描述

帮助赞赏......

Dan*_*rpe 106

对于其他任何想知道如何根据Costique建议使用Core Graphics绘制内部阴影的人,那么这是如何:(在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);
Run Code Online (Sandbox Code Playgroud)

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

  1. 创建你的路径
  2. 设置所需的填充颜色,将此路径添加到上下文中,然后填充上下文
  3. 现在创建一个可以绑定可见路径的更大的矩形.在关闭此路径之前,请添加可见路径.然后关闭路径,以便创建一个从中减去可见路径的形状.您可能想要研究填充方法(偶数/奇数的非零绕组),具体取决于您创建这些路径的方式.本质上,为了在将子路径添加到一起时将子路径"减去",您需要在相反的方向上绘制它们(或者更确切地说构造它们),一个是顺时针方向,另一个是逆时针方向.
  4. 然后,您需要将可见路径设置为上下文中的剪切路径,这样您就不会在屏幕外部绘制任何内容.
  5. 然后在上下文中设置阴影,包括偏移,模糊和颜色.
  6. 然后用它上面的孔填充大的形状.颜色无关紧要,因为如果你做的一切都正确,你就不会看到这种颜色,只看到阴影.

  • @DanielThorpe:+1给出了很好的答案.我修复了圆角矩形路径代码(在更改半径时损坏了它)并简化了外部矩形路径代码.希望你不介意. (4认同)
  • @runmad嗯,你可以创建任何你想要的可见CGPath,这里使用的例子只是一个例子,为了简洁而选择.如果您想创建一个圆角矩形,那么您可以执行以下操作:CGPath visiblePath = [UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:radius] .CGPath希望有所帮助. (2认同)

Mat*_*ing 46

我知道我参加这个派对已经很晚了,但是这会帮助我在旅行的早期找到...

为了给予信用到期的信用,这实质上是对Daniel Thorpe对Costique从较大区域减去较小区域的解决方案的阐述的修改.此版本适用于使用图层合成而非覆盖的用户-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];
Run Code Online (Sandbox Code Playgroud)

此时,如果您的父图层没有遮盖其边界,您将在图层边缘周围看到遮罩图层的额外区域.如果你直接复制了这个例子,这将是42像素的黑色.要摆脱它,您可以简单地使用CAShapeLayer具有相同路径的另一个并将其设置为阴影层的蒙版:

CAShapeLayer* maskLayer = [CAShapeLayer layer];
[maskLayer setPath:someInnerPath];
[shadowLayer setMask:maskLayer];
Run Code Online (Sandbox Code Playgroud)

我自己没有对此进行基准测试,但我怀疑将这种方法与光栅化结合使用比覆盖更有效-drawRect:.

  • 这非常好!我上传到github有一些补充.试一试:) https://github.com/inamiy/YIInnerShadowView (11认同)
  • @Moe它可以是你想要的任意CGPath.`[[UIBezierPath pathWithRect:[shadowLayer bounds]] CGPath]`是最简单的选择. (4认同)
  • someInnerPath?你能解释一下吗? (3认同)

Cos*_*que 35

通过在边界外创建一个大的矩形路径,减去边界大小的矩形路径并用"正常"阴影填充生成的路径,可以使用Core Graphics绘制内部阴影.

但是,由于您需要将其与渐变层结合使用,我认为更简单的解决方案是创建内部阴影的9部分透明PNG图像并将其拉伸到正确的大小.9部分阴影图像看起来像这样(其大小为21x21像素):

替代文字

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);
Run Code Online (Sandbox Code Playgroud)

然后设置innerShadowLayer的框架,它应该正确拉伸阴影.


Pat*_*pel 28

在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)
    }
}
Run Code Online (Sandbox Code Playgroud)

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


jin*_*ula 23

圆形的方式,但它避免了必须使用图像(阅读:易于更改颜色,阴影半径等),它只有几行代码.

  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];
Run Code Online (Sandbox Code Playgroud)

警告:你必须有一个边框,否则阴影不会出现.[UIColor clearColor]不起作用.在示例中,我使用不同的颜色,但您可以使用它来使其具有与阴影开头相同的颜色.:)

请参阅下面关于UIColorFromRGB宏的bbrame评论.


Som*_*Man 17

迟到总比不到好...

这是另一种方法,可能没有比已发布的方法更好,但它很好而且简单 -

-(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];
}
Run Code Online (Sandbox Code Playgroud)


Jer*_*ang 8

而不是通过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];
Run Code Online (Sandbox Code Playgroud)

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


Wil*_* Hu 6

这是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)
Run Code Online (Sandbox Code Playgroud)


小智 5

这是您从PaintCode导出的解决方案:

-(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);
}
Run Code Online (Sandbox Code Playgroud)