How to cut out a hole with radial gradient in view

1k views Asked by At

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

1

There are 1 answers

1
hbk On BEST ANSWER

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