Xcoder.tips/Behind-Uivi…

IOS 8 apple has brought us native frosted glass effect support, UIVisualEffectView.

UIVisualEffectView *effectView = [[UIVisualEffectView alloc] initWithEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleDark]].Copy the code

However, the API is very limited, only two effects and a few styles can be changed, and the blur effect is mostly black and white, and the blur degree is not adjustable. But sometimes designers who don’t care about implementation will ask for a little bit less blur in some places and so on, and they’ll do it themselves with Gaussian blur, and then the question is, can you really do that?

We can see that there are three views behind a UIVisualEffectView using view inspection tools such as Lookin:

  • _UIVisualEffectBackdropView
  • _UIVisualEffectSubview
  • _UIVisualEffectContentView

_UIVisualEffectBackdropView is really fuzzy effect, _UIVisualEffectSubview is adjust the black and white. And _UIVisualEffectBackdropView layerClass for:

@interface UICABackdropLayer : CABackdropLayer
@end
Copy the code

All it does is make a copy of the contents of the view underneath it that it blocks. The real blur effects are made by CALayer’s filters mentioned in the previous article.

Now that we know how it works, we can customize a uiVisualEffectView-like view and adjust the degree of blur to look like this:

IB_DESIGNABLE
@interface RTBackdropView : UIView
@property (nonatomic) IBInspectable CGFloat blurRadius;
@property (nonatomic) IBInspectable CGFloat saturation;
@end

@implementation RTBackdropView

+ (Class)layerClass
{
    return NSClassFromString(@"CABackdropLayer");
}

- (instancetype)initWithCoder:(NSCoder *)coder
{
    self = [super initWithCoder:coder];
    if (self) {
        self.blurRadius = 30;
        self.saturation = 2;
    }
    return self;
}

- (instancetype)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        self.blurRadius = 30;
        self.saturation = 2;
    }
    return self;
}

- (void)setBlurRadius:(CGFloat)blurRadius
{
    if(_blurRadius ! = blurRadius) { _blurRadius = blurRadius; [self_updateFilters]; }} - (void)setSaturation:(CGFloat)saturation
{
    if(_saturation ! = saturation) { _saturation = saturation; [self_updateFilters]; }} - (void)_updateFilters {
    self.layer.filters = @[
        ({
            CIFilter *sat = [NSClassFromString(@"CAFilter") filterWithName:@"colorSaturate"];
            [sat setValue:@(self.saturation) forKey:@"inputAmount"];
            [sat setValue:@YES forKey:@"inputNormalizeEdges"]; sat; ({}),CIFilter *blur = [NSClassFromString(@"CAFilter") filterWithName:@"gaussianBlur"];
            blur.name = @"blur";    // Note that the name is useful!
            [blur setValue:@(self.blurRadius) forKey:@"inputRadius"]; blur; })]; }@end
Copy the code

Is that it? We can also add animation!

    Blur is the name of the filter
    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"filters.blur.inputRadius"];
    animation.fromValue = @0;
    animation.toValue = @20;
    animation.duration = 0.5;
    animation.autoreverses = YES;
    animation.removedOnCompletion = NO;
    animation.repeatCount = FLT_MAX;
    [view.layer addAnimation:animation forKey:@"Blur"];
Copy the code