【问题标题】:Inner Shadow in UILabelUILabel 中的内阴影
【发布时间】:2026-02-06 20:40:01
【问题描述】:

是否可以创建这样一个带有内阴影和外阴影的 UILabel?

alt text http://dl.getdropbox.com/u/80699/Bildschirmfoto%202010-07-12%20um%2021.28.57.png

我只知道shadowColorshadowOffset

放大:

alt text http://dl.getdropbox.com/u/80699/Bildschirmfoto%202010-07-12%20um%2021.39.56.png

谢谢!

【问题讨论】:

  • 很久以前我也想知道这一点来模拟 UITextField 并使用 Quartz (如建议的那样)但存在一些性能问题,我很确定为 UILabel 渲染的边界设置动画那样的阴影看起来不太好。它会显得迟钝。就我而言,我最终使用了拉伸的 UIImage:
  • 您问题中的图片链接已损坏

标签: iphone objective-c uilabel


【解决方案1】:

dmaclach 的答案仅适用于可以轻松反转的形状。我的解决方案是适用于任何形状和文本的自定义视图。它需要 iOS 4 并且与分辨率无关。

首先,对代码的作用进行图形说明。这里的形状是一个圆形。

代码用白色阴影绘制文本。如果不需要,可以进一步重构代码,因为阴影需要以不同的方式屏蔽。如果您在旧版本的 iOS 上需要它,则必须替换该块并使用(烦人的)CGBitmapContext。

- (UIImage*)blackSquareOfSize:(CGSize)size {
  UIGraphicsBeginImageContextWithOptions(size, NO, 0);  
  [[UIColor blackColor] setFill];
  CGContextFillRect(UIGraphicsGetCurrentContext(), CGRectMake(0, 0, size.width, size.height));
  UIImage *blackSquare = UIGraphicsGetImageFromCurrentImageContext();
  UIGraphicsEndImageContext();
  return blackSquare;
}


- (CGImageRef)createMaskWithSize:(CGSize)size shape:(void (^)(void))block {
  UIGraphicsBeginImageContextWithOptions(size, NO, 0);  
  block();
  CGImageRef shape = [UIGraphicsGetImageFromCurrentImageContext() CGImage];
  UIGraphicsEndImageContext();  
    CGImageRef mask = CGImageMaskCreate(CGImageGetWidth(shape),
                                      CGImageGetHeight(shape),
                                      CGImageGetBitsPerComponent(shape),
                                      CGImageGetBitsPerPixel(shape),
                                      CGImageGetBytesPerRow(shape),
                                      CGImageGetDataProvider(shape), NULL, false);
  return mask;
}


- (void)drawRect:(CGRect)rect {
  UIFont *font = [UIFont fontWithName:@"HelveticaNeue-Bold" size:40.0f];
  CGSize fontSize = [text_ sizeWithFont:font];

  CGImageRef mask = [self createMaskWithSize:rect.size shape:^{
    [[UIColor blackColor] setFill];
    CGContextFillRect(UIGraphicsGetCurrentContext(), rect);
    [[UIColor whiteColor] setFill];
    // custom shape goes here
    [text_ drawAtPoint:CGPointMake((self.bounds.size.width/2)-(fontSize.width/2), 0) withFont:font];
    [text_ drawAtPoint:CGPointMake((self.bounds.size.width/2)-(fontSize.width/2), -1) withFont:font];
  }];

  CGImageRef cutoutRef = CGImageCreateWithMask([self blackSquareOfSize:rect.size].CGImage, mask);
  CGImageRelease(mask);
  UIImage *cutout = [UIImage imageWithCGImage:cutoutRef scale:[[UIScreen mainScreen] scale] orientation:UIImageOrientationUp];
  CGImageRelease(cutoutRef);  

  CGImageRef shadedMask = [self createMaskWithSize:rect.size shape:^{
    [[UIColor whiteColor] setFill];
    CGContextFillRect(UIGraphicsGetCurrentContext(), rect);
    CGContextSetShadowWithColor(UIGraphicsGetCurrentContext(), CGSizeMake(0, 1), 1.0f, [[UIColor colorWithWhite:0.0 alpha:0.5] CGColor]);
    [cutout drawAtPoint:CGPointZero];
  }];

  // create negative image
  UIGraphicsBeginImageContextWithOptions(rect.size, NO, 0);
  [[UIColor blackColor] setFill];
  // custom shape goes here
  [text_ drawAtPoint:CGPointMake((self.bounds.size.width/2)-(fontSize.width/2), -1) withFont:font];
  UIImage *negative = UIGraphicsGetImageFromCurrentImageContext();
  UIGraphicsEndImageContext(); 

  CGImageRef innerShadowRef = CGImageCreateWithMask(negative.CGImage, shadedMask);
  CGImageRelease(shadedMask);
  UIImage *innerShadow = [UIImage imageWithCGImage:innerShadowRef scale:[[UIScreen mainScreen] scale] orientation:UIImageOrientationUp];
  CGImageRelease(innerShadowRef);

  // draw actual image
  [[UIColor whiteColor] setFill];
  [text_ drawAtPoint:CGPointMake((self.bounds.size.width/2)-(fontSize.width/2), -0.5) withFont:font];
  [[UIColor colorWithWhite:0.76 alpha:1.0] setFill];
  [text_ drawAtPoint:CGPointMake((self.bounds.size.width/2)-(fontSize.width/2), -1) withFont:font];  

  // finally apply shadow
  [innerShadow drawAtPoint:CGPointZero];
}

【讨论】:

【解决方案2】:

虽然史蒂夫的回答确实有效,但它并没有扩展到我的特定情况,所以我最终通过在剪辑我的上下文后对 CGPath 应用阴影来创建内部阴影:

CGContextRef context = UIGraphicsGetCurrentContext();

// clip context so shadow only shows on the inside
CGPathRef roundedRect = [UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:4].CGPath;
CGContextAddPath(context, roundedRect);
CGContextClip(context);

CGContextAddPath(context, roundedRect);
CGContextSetShadowWithColor(UIGraphicsGetCurrentContext(), CGSizeMake(0, 0), 3, [UIColor colorWithWhite:0 alpha:1].CGColor);
CGContextSetStrokeColorWithColor(context, [UIColor colorWithWhite:0 alpha:1].CGColor);
CGContextStrokePath(context);

结果:

您可以更改阴影偏移和模糊以更改阴影样式。如果需要更暗,也可以依次添加,然后描边相同的CGPath,这样阴影就会堆积起来。

【讨论】:

  • 嘿 Sam,这很棒,对我来说效果很好,但你知道如何在没有边框的情况下获得同样的效果吗?或者也许只是一个不透明度大约是这个的一半的边框?
【解决方案3】:

我使用 FXLabel 作为内阴影,看这里https://github.com/nicklockwood/FXLabel

【讨论】:

  • 哇,好棒的 git!这就像 UILabel 上的类固醇!谢谢!
【解决方案4】:

是的,我有一篇关于它的博客文章here。简短的回答是从 here 的子类 UILabel 借用 JTAInnerShadowLayer,并通过覆盖 + (Class)layerClass 来更改其层类,使其返回 JTAInnerShadowLayer 类,如下所示:

+ (Class)layerClass
{
  return [JTAInnerShadowLayer class];
}

然后在图层的 init 方法中设置 JTAInnerShadowLayer,如下所示:

[self setBackgroundColor:[UIColor clearColor]];
JTAInnerShadowLayer *innerShadow = (JTAInnerShadowLayer *)[self layer];
[innerShadow setClipForAnyAlpha:YES];
[innerShadow setOutsideShadowSize:CGSizeMake(0.0, 1.0) radius:1.0];
[innerShadow setInsideShadowSize:CGSizeMake (0.0, 4.0) radius:6.0];
/* Uncomment this to make the label also draw the text (won't work well
   with black text!*/
//[innerShadow drawOriginalImage];

(或者只是借用 JTAIndentLabel 类)。

【讨论】:

    【解决方案5】:

    您必须进行自己的自定义绘图才能获得内部阴影。目前没有办法使用标准的 UILabel 来做到这一点。

    这里有一个关于用 Quartz 做内阴影的合理解释。

    Inner shadow in Core Graphics

    【讨论】:

      【解决方案6】:

      试试PaintCode试用版!对于初学者,您将学习如何创建自定义图纸。这是很不错的! :3 你可以用它来练习或学习,但不要一直用它,你还应该学习如何自己画图。

      免责声明:我不是代言人,我只是对我的发现感到惊讶。 ;)

      【讨论】: