【问题标题】:Inner shadow effect on UIView layer?UIView层的内阴影效果?
【发布时间】:2011-05-24 19:06:37
【问题描述】:

我有以下 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 对象之上,因为它应该是一些按钮后面的栏,所以我不知道该怎么做?

我可以再添加一层,但又不知道如何实现内阴影效果(像这样:

帮助感谢...

【问题讨论】:

    标签: iphone core-graphics calayer


    【解决方案1】:

    我知道我参加这个聚会迟到了,但这会帮助我在旅行中尽早找到......

    为了在应得的地方给予肯定,这实质上是对 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];
    

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

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

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

    【讨论】:

    • someInnerPath?请您再解释一下。
    • @Moe 可以是任何你想要的任意 CGPath。 [[UIBezierPath pathWithRect:[shadowLayer bounds]] CGPath] 是最简单的选择。
    • 我得到了一个黑色(外部)矩形,用于正确绘制内部阴影的 shadowLayer.path。我怎样才能摆脱它(黑色外矩形)?看起来你只能在上下文中设置 fillColor 而你不使用一个。
    • @Olivier 我无法完全描述您的问题,但这可能是某处其他代码的副作用。你的意思是你得到了外部内部阴影?
    • 这很好用!我上传到 github 并添加了一些内容。试试看:)github.com/inamiy/YIInnerShadowView
    【解决方案2】:

    对于其他想知道如何按照 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);
    

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

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

    【讨论】:

    • 谢谢,但是可以调整半径吗?它目前基于边界,但我想改为基于设定的半径(如 5.0f)。使用上面的代码,它的舍入太多了。
    • @runmad 好吧,你可以创建任何你想要的可见的 CGPath,这里使用的例子就是为了简洁而选择的例子。如果您想创建一个圆角矩形,那么您可以执行以下操作:CGPath visiblePath = [UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:radius].CGPath 希望对您有所帮助。
    • @DanielThorpe:+1 的好答案。我修复了圆角矩形路径代码(更改半径时你的代码坏了)并简化了外部矩形路径代码。希望你不要介意。
    • 如何正确设置 4 个方向的内阴影,而不仅仅是 2 个?
    • @Protocole 您可以将偏移量设置为 {0,0},但使用阴影半径,例如 4.f。
    【解决方案3】:

    有一个简单的解决方案 - 只需绘制正常的阴影并旋转,就像这样

    @objc func shadowView() -> UIView {
            let shadowView = UIView(frame: .zero)
            shadowView.backgroundColor = .white
            shadowView.layer.shadowColor = UIColor.grey.cgColor
            shadowView.layer.shadowOffset = CGSize(width: 0, height: 2)
            shadowView.layer.shadowOpacity = 1.0
            shadowView.layer.shadowRadius = 4
            shadowView.layer.compositingFilter = "multiplyBlendMode"
            return shadowView
        }
    
    func idtm_addBottomShadow() {
            let shadow = shadowView()
            shadow.transform = transform.rotated(by: 180 * CGFloat(Double.pi))
            shadow.transform = transform.rotated(by: -1 * CGFloat(Double.pi))
            shadow.translatesAutoresizingMaskIntoConstraints = false
            addSubview(shadow)
            NSLayoutConstraint.activate([
                shadow.leadingAnchor.constraint(equalTo: leadingAnchor),
                shadow.trailingAnchor.constraint(equalTo: trailingAnchor),
                shadow.bottomAnchor.constraint(equalTo: bottomAnchor),
                shadow.heightAnchor.constraint(equalToConstant: 1),
                ])
        }
    

    【讨论】:

      【解决方案4】:

      在 Swift 中使用 CALayer 的可扩展解决方案

      使用所描述的InnerShadowLayer,您还可以仅为特定边缘启用内部阴影,不包括其他边缘。 (例如,您只能在视图的左边缘和上边缘启用内部阴影)

      然后您可以使用以下方法将InnerShadowLayer 添加到您的视图中:

      init(...) {
      
          // ... your initialization code ...
      
          super.init(frame: .zero)
          layer.addSublayer(shadowLayer)
      }
      
      public override func layoutSubviews() {
          super.layoutSubviews()
          shadowLayer.frame = bounds
      }
      

      InnerShadowLayer 实现

      /// Shadow is a struct defining the different kinds of shadows
      public struct Shadow {
          let x: CGFloat
          let y: CGFloat
          let blur: CGFloat
          let opacity: CGFloat
          let color: UIColor
      }
      
      /// A layer that applies an inner shadow to the specified edges of either its path or its bounds
      public class InnerShadowLayer: CALayer {
          private let shadow: Shadow
          private let edge: UIRectEdge
      
          public init(shadow: Shadow, edge: UIRectEdge) {
              self.shadow = shadow
              self.edge = edge
              super.init()
              setupShadow()
          }
      
          required init?(coder aDecoder: NSCoder) {
              fatalError("init(coder:) has not been implemented")
          }
      
          public override func layoutSublayers() {
              updateShadow()
          }
      
          private func setupShadow() {
              shadowColor = shadow.color.cgColor
              shadowOpacity = Float(shadow.opacity)
              shadowRadius = shadow.blur / 2.0
              masksToBounds = true
          }
      
          private func updateShadow() {
              shadowOffset = {
                  let topWidth: CGFloat = 0
                  let leftWidth = edge.contains(.left) ? shadow.y / 2 : 0
                  let bottomWidth: CGFloat = 0
                  let rightWidth = edge.contains(.right) ? -shadow.y / 2 : 0
      
                  let topHeight = edge.contains(.top) ? shadow.y / 2 : 0
                  let leftHeight: CGFloat = 0
                  let bottomHeight = edge.contains(.bottom) ? -shadow.y / 2 : 0
                  let rightHeight: CGFloat = 0
      
                  return CGSize(width: [topWidth, leftWidth, bottomWidth, rightWidth].reduce(0, +),
                                height: [topHeight, leftHeight, bottomHeight, rightHeight].reduce(0, +))
              }()
      
              let insets = UIEdgeInsets(top: edge.contains(.top) ? -bounds.height : 0,
                                        left: edge.contains(.left) ? -bounds.width : 0,
                                        bottom: edge.contains(.bottom) ? -bounds.height : 0,
                                        right: edge.contains(.right) ? -bounds.width : 0)
              let path = UIBezierPath(rect: bounds.inset(by: insets))
              let cutout = UIBezierPath(rect: bounds).reversing()
              path.append(cutout)
              shadowPath = path.cgPath
          }
      }
      
      

      【讨论】:

        【解决方案5】:

        这是我在 Swift 4.2 中的解决方案。你想试试吗?

        final class ACInnerShadowLayer : CAShapeLayer {
        
          var innerShadowColor: CGColor? = UIColor.black.cgColor {
            didSet { setNeedsDisplay() }
          }
        
          var innerShadowOffset: CGSize = .zero {
            didSet { setNeedsDisplay() }
          }
        
          var innerShadowRadius: CGFloat = 8 {
            didSet { setNeedsDisplay() }
          }
        
          var innerShadowOpacity: Float = 1 {
            didSet { setNeedsDisplay() }
          }
        
          override init() {
            super.init()
        
            masksToBounds = true
            contentsScale = UIScreen.main.scale
        
            setNeedsDisplay()
          }
        
          override init(layer: Any) {
              if let layer = layer as? InnerShadowLayer {
                  innerShadowColor = layer.innerShadowColor
                  innerShadowOffset = layer.innerShadowOffset
                  innerShadowRadius = layer.innerShadowRadius
                  innerShadowOpacity = layer.innerShadowOpacity
              }
              super.init(layer: layer)
          }
        
          required init?(coder aDecoder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
          }
        
          override func draw(in ctx: CGContext) {
            ctx.setAllowsAntialiasing(true)
            ctx.setShouldAntialias(true)
            ctx.interpolationQuality = .high
        
            let colorspace = CGColorSpaceCreateDeviceRGB()
        
            var rect = bounds
            var radius = cornerRadius
        
            if borderWidth != 0 {
              rect = rect.insetBy(dx: borderWidth, dy: borderWidth)
              radius -= borderWidth
              radius = max(radius, 0)
            }
        
            let innerShadowPath = UIBezierPath(roundedRect: rect, cornerRadius: radius).cgPath
            ctx.addPath(innerShadowPath)
            ctx.clip()
        
            let shadowPath = CGMutablePath()
            let shadowRect = rect.insetBy(dx: -rect.size.width, dy: -rect.size.width)
            shadowPath.addRect(shadowRect)
            shadowPath.addPath(innerShadowPath)
            shadowPath.closeSubpath()
        
            if let innerShadowColor = innerShadowColor, let oldComponents = innerShadowColor.components {
              var newComponets = Array<CGFloat>(repeating: 0, count: 4) // [0, 0, 0, 0] as [CGFloat]
              let numberOfComponents = innerShadowColor.numberOfComponents
        
              switch numberOfComponents {
              case 2:
                newComponets[0] = oldComponents[0]
                newComponets[1] = oldComponents[0]
                newComponets[2] = oldComponents[0]
                newComponets[3] = oldComponents[1] * CGFloat(innerShadowOpacity)
              case 4:
                newComponets[0] = oldComponents[0]
                newComponets[1] = oldComponents[1]
                newComponets[2] = oldComponents[2]
                newComponets[3] = oldComponents[3] * CGFloat(innerShadowOpacity)
              default:
                break
              }
        
              if let innerShadowColorWithMultipliedAlpha = CGColor(colorSpace: colorspace, components: newComponets) {
                ctx.setFillColor(innerShadowColorWithMultipliedAlpha)
                ctx.setShadow(offset: innerShadowOffset, blur: innerShadowRadius, color: innerShadowColorWithMultipliedAlpha)
                ctx.addPath(shadowPath)
                ctx.fillPath(using: .evenOdd)
              }
            } 
          }
        }
        

        【讨论】:

        • 如果我没有将它用作单独的类,而是像在我的代码中使用一样,当我得到这个时,上下文 (ctx) 为零:let ctx = UIGraphicsGetCurrentContext
        • @MohsinKhubaibAhmed 您可以通过UIGraphicsGetCurrentContext 方法获取当前上下文,以便在某些视图将其上下文推送到堆栈时获取。
        • @Arco 我在旋转设备时遇到了一些麻烦。我添加了“覆盖便利初始化(层:任何){self.init()}”。现在没有错误显示!
        • 添加了 init(layer: Any) 来修复崩溃。
        【解决方案6】:

        这段代码对我有用

        class InnerDropShadowView: UIView {
            override func draw(_ rect: CGRect) {
                //Drawing code
                let context = UIGraphicsGetCurrentContext()
                //// Shadow Declarations
                let shadow: UIColor? = UIColor.init(hexString: "a3a3a3", alpha: 1.0) //UIColor.black.withAlphaComponent(0.6) //UIColor.init(hexString: "d7d7da", alpha: 1.0)
                let shadowOffset = CGSize(width: 0, height: 0)
                let shadowBlurRadius: CGFloat = 7.5
                //// Rectangle Drawing
                let rectanglePath = UIBezierPath(rect: bounds)
                UIColor.groupTableViewBackground.setFill()
                rectanglePath.fill()
                ////// Rectangle Inner Shadow
                context?.saveGState()
                UIRectClip(rectanglePath.bounds)
                context?.setShadow(offset: CGSize.zero, blur: 0, color: nil)
                context?.setAlpha((shadow?.cgColor.alpha)!)
                context?.beginTransparencyLayer(auxiliaryInfo: nil)
                do {
                    let opaqueShadow: UIColor? = shadow?.withAlphaComponent(1)
                    context?.setShadow(offset: shadowOffset, blur: shadowBlurRadius, color: opaqueShadow?.cgColor)
                    context!.setBlendMode(.sourceOut)
                    context?.beginTransparencyLayer(auxiliaryInfo: nil)
                    opaqueShadow?.setFill()
                    rectanglePath.fill()
                    context!.endTransparencyLayer()
                }
                context!.endTransparencyLayer()
                context?.restoreGState()
            }
        }
        

        【讨论】:

          【解决方案7】:

          在 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 图层不应具有不透明的背景颜色,因为这将呈现在阴影前面。

          【讨论】:

          • 最后一行包含“层”。这是哪里来的?
          • @CharlieSeligman 这是父层,可以是任何层。您可以使用自定义层或视图层(UIView 具有层属性)。
          • 应该是let innerShadow = CALayer(); innerShadow.frame = bounds。如果没有适当的界限,它就不会绘制适当的阴影。还是谢谢
          • @noir_eagle 是的,尽管您可能希望在 layoutSubviews() 中设置它以保持同步
          • 对!在layoutSubviews()draw(_ rect)
          【解决方案8】:

          有点迂回的方式,但它避免了必须使用图像(阅读:易于更改颜色、阴影半径等)并且只有几行代码。

          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] 不起作用。在示例中,我使用了不同的颜色,但您可以将其弄乱以使其具有与阴影开头相同的颜色。 :)

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

          【讨论】:

          • 我忽略了它,但假设您会在添加图像视图时这样做 - 确保将框架设置为与父 UIView 相同的矩形。如果您使用的是 IB,如果您要更改父视图的框架,请将 struts 和 springs 设置为具有视图的阴影大小。在代码中应该有一个调整大小的掩码,你可以或者做同样的事情,AFAIK。
          • 这是目前最简单的方法,但请注意 CALayer 阴影方法仅适用于 iOS 3.2 及更高版本。我支持 3.1,所以我将这些属性设置在 if ([layer respondsToSelector:@selector(setShadowColor:)]) {
          • 这似乎对我不起作用。至少在 xcode 4.2 和 ios 模拟器 4.3 上。为了使阴影出现,我必须添加背景颜色......此时阴影只出现在外面。
          • @Andrea - 请记住我上面提到的警告。我认为背景颜色或边框可能具有“给它添加阴影的东西”的相同效果。至于它出现在外面,如果 UIImageView 不是您想要内部阴影的那个的子视图,那可能就是它 - 我必须查看您的代码才能看到。
          • 只是为了纠正我之前的陈述......代码确实有效......我错过了一些东西,但不幸的是我现在不记得了。 :) 所以...感谢分享此代码 sn-p。
          【解决方案9】:

          使用渐变层:

          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];
          

          【讨论】:

            【解决方案10】:

            查看Chris Emery 撰写的Inner Shadows in Quartz 的精彩文章,其中解释内部阴影是如何由PaintCode 绘制的,并给出了干净整洁的代码 sn-p:

            - (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);
            }
            

            【讨论】:

              【解决方案11】:

              这里是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)
              

              【讨论】:

              • 为我工作!!谢谢。
              【解决方案12】:

              这是我从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);
              }
              

              【讨论】:

                【解决方案13】:

                迟到总比没有好...

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

                -(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];
                }
                

                【讨论】:

                • @SomaMan 是否可以仅在特定一侧设置阴影?就像只在顶部或顶部/底部或顶部/右侧等..
                【解决方案14】:

                而不是通过drawRect绘制内阴影或将UIView添加到View。你可以直接在边框上添加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 添加一个底部内阴影

                【讨论】:

                  【解决方案15】:

                  有一些代码here 可以为您执行此操作。如果您将视图中的图层(通过覆盖+ (Class)layerClass)更改为 JTAInnerShadowLayer,那么您可以在 init 方法中设置缩进图层上的内部阴影,它会为您完成工作。如果您还想绘制原始内容,请确保在缩进层上调用setDrawOriginalImage:yes。有一篇关于其工作原理的博文here

                  【讨论】:

                  • @MiteshDobareeya 刚刚测试了两个链接,它们似乎工作正常(包括在私人标签中)。哪个链接给您带来了问题?
                  • 你能看看这个内部阴影代码的实现吗?它仅适用于 ViewDidAppear 方法。并显示一些闪烁。 drive.google.com/open?id=1VtCt7UFYteq4UteT0RoFRjMfFnbibD0E
                  【解决方案16】:

                  我参加聚会很晚了,但我想回馈社区.. 这是我在提供静态库和无资源时编写的删除 UITextField 背景图像的方法... 我将它用于四个 UITextField 实例的 PIN 输入屏幕,这些实例可以在 ViewController 中显示一个原始字符或 (BOOL)[self isUsingBullets] 或 (BOOL)[self usingAsterisks]。 应用程序适用于 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];
                  }
                  

                  我希望这对某人有所帮助,因为这个论坛对我有所帮助。

                  【讨论】:

                    【解决方案17】:

                    可以使用 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);
                    

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

                    【讨论】:

                    • 是的,我想你是对的。只是希望图层尽可能平坦。我可以在 Photoshop 中创建具有内部阴影和渐变外观的图像,但在使用图像时,我遇到了设备上 100% 匹配的颜色问题。
                    • 是的,这是所有渐变和阴影的问题,我只是无法在 iOS 上 1:1 重现这些 Photoshop 效果,我尽力了。
                    猜你喜欢
                    • 1970-01-01
                    • 2016-06-14
                    • 2017-07-31
                    • 1970-01-01
                    • 2017-01-23
                    • 2015-06-13
                    • 1970-01-01
                    • 1970-01-01
                    相关资源
                    最近更新 更多