Search code examples
iosswiftcalayerradial-gradientscover

How to cut out a hole with radial gradient in view


I have a transparent light hole view, a view overlay super view, and there is a hole gradient round view in the cover view, the super view is still visiable. I have no idea to implement the gradient light hole view, anyone can help me

enter image description here

EDIT ============

I try to add cover view to the full screen, the cover view background color is clear., and add custom CALayer to the cover view layer as sublayer.

the cover view implementation:

@implementation CoverView

   - (instancetype)initWithFrame:(CGRect)frame {
     self = [super initWithFrame:frame];
     if (self) {
     self.backgroundColor = [UIColor clearColor];
     }
     return self;
   }

    - (void)setGradientHoleFrame:(CGRect)gradientHoleFrame {
     if (!CGRectEqualToRect(_gradientHoleFrame, gradientHoleFrame)) {
    _gradientHoleFrame = gradientHoleFrame;
      [self loadRadialGradientLayer];
     }
   }

   - (void)loadRadialGradientLayer {
    RadialGradientLayer *layer = [[RadialGradientLayer alloc] init];
    layer.frame = self.bounds;
     layer.raidalGradientFrame = self.gradientHoleFrame;
     [layer setNeedsDisplay];
     [self.layer addSublayer:layer];
    }

@end

the custom radial gradient layer:

CGFloat const GRADIENT_WIDTH = 10.0f;

@implementation RadialGradientLayer

- (void)setRaidalGradientFrame:(CGRect)raidalGradientFrame {
  if (!CGRectEqualToRect(_raidalGradientFrame, raidalGradientFrame)) {
    _raidalGradientFrame = raidalGradientFrame;
    [self setNeedsDisplay];
  }
}

- (void)drawInContext:(CGContextRef)context {
  CGContextSaveGState(context);
  CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
  CGFloat colours[8] = { 0.0f, 0.0f, 0.0f, 0.0f,     // Clear region colour.
                         0.0f, 0.0f, 0.0f, 0.8 };   // Blur region colour.
  CGFloat locations[2] = { 0.0f, 1.0f };
  CGGradientRef gradient = CGGradientCreateWithColorComponents(colorSpace, colours, locations, 2);
  CGPoint center = CGPointMake(self.raidalGradientFrame.origin.x + self.raidalGradientFrame.size.width / 2,
                               self.raidalGradientFrame.origin.y + self.raidalGradientFrame.size.height / 2);
  CGFloat radius = MIN(self.raidalGradientFrame.size.width / 2, self.raidalGradientFrame.size.height / 2) + GRADIENT_WIDTH;
  CGContextDrawRadialGradient(context, gradient, center, 0.0, center, radius, kCGGradientDrawsAfterEndLocation);

  CGGradientRelease(gradient);
  CGColorSpaceRelease(colorSpace);
}

@end

and I user it:

 CoverView *view = [[CoverView alloc] initWithFrame:[UIScreen mainScreen].bounds];
  // for get hole frame
  CGRect rect = [self.coinPointView.superview convertRect:self.coinPointView.frame toView:view];
  view.gradientHoleFrame = rect;
  [self.tabBarController.view addSubview:view];

Finally, I got result below.

thanks for @gbk and @matt

enter image description here


Solution

  • You can play around a little bit and get

    enter image description here

    To achieve such result I prepared sample code in playground - just copy-paste it and try.

    import UIKit
    import PlaygroundSupport
    
    class RadialGradientLayer: CALayer {
    
        required override init() {
            super.init()
            needsDisplayOnBoundsChange = true
        }
    
        required init?(coder aDecoder: NSCoder) {
            super.init(coder: aDecoder)
        }
    
        required override init(layer: Any) {
            super.init(layer: layer)
        }
    
        //default colors
        public var colors = [UIColor.red.cgColor, UIColor.clear.cgColor]
    
        override func draw(in ctx: CGContext) {
            ctx.saveGState()
    
            let colorSpace = CGColorSpaceCreateDeviceRGB()
            var locations = [CGFloat]()
            for i in 0...colors.count-1 {
                locations.append(CGFloat(i) / CGFloat(colors.count))
            }
            let gradient = CGGradient(colorsSpace: colorSpace, colors: colors as CFArray, locations: locations)
            let center = CGPoint(x: bounds.width / 2.0, y: bounds.height / 2.0)
            let radius = min(bounds.width, bounds.height)
            ctx.drawRadialGradient(gradient!, startCenter: center, startRadius: 0.0, endCenter: center, endRadius: radius, options: CGGradientDrawingOptions(rawValue: 0))
        }    
    }
    
    let view = UIView(frame: CGRect(x: 0, y: 0, width: 375, height: 300))
    view.backgroundColor = UIColor.green
    
    let label = UILabel(frame: view.bounds)
    label.text = "test"
    label.font = UIFont.systemFont(ofSize: 30)
    label.textAlignment = .center
    view.addSubview(label)
    
    let gradientLayer = RadialGradientLayer()
    gradientLayer.frame = view.bounds
    gradientLayer.colors = [UIColor.clear.cgColor, UIColor.black.cgColor]
    gradientLayer.setNeedsDisplay()
    
    view.layer.addSublayer(gradientLayer)
    
    
    view