Search code examples
objective-cuser-interfaceuiviewcalayer

How can I achieve luminescent shadows?


enter image description here

  1. The entire mockup has a shadow which reminds me of Philip's Ambilight TVs.
  2. Look at the "Trailer and Photos" section all tiles have this type of shadow.

This effect is truly fascinating to watch and I'd like to incorporate it into my app.

Here's what I've thought:

  1. Take a screenshot of the view and enlarge it to 110%
  2. Place it behind the view.
  3. Blur the screenshot.
  4. Apply an inner white shadow.

Any suggestions?


Solution

  • Your thoughts are correct, but there is no need to apply inner shadow, it's possible to add transparent insets to the screenshot, and when blurred, that would create gradient you're looking for.

    The following code should do the job:

    - (void)addLuminescentShadowToView:(UIView*)contentView
    {
        //the bigger radius of the blur
        //the more spread shadow will get
        CGFloat blurRadius = 15.0f;
    
        //in order to shadow becomes transparent on edeges
        //we're adding some transparent insets to the snapshot
        CGFloat insetsSize = 2.0f * blurRadius;
    
        //getting superview of content view
        //it's important to do it before getting snapshot
        //as during snapshot parent of the content view
        //might be temporary changed
        //superview will be used later to insert shadow
        UIView* contentViewSuperview = contentView.superview;
    
        //code for following methods were sourced from
        //http://code.tutsplus.com/tutorials/adding-blur-effects-on-ios--cms-21488 and modified
        //taking snapshot of content view
        //method is modified to add transparent insets to the snapshot
        UIImage* snapshot = [self takeSnapshotOfView: contentView withTransparentInsets: insetsSize];
    
        //applying blur to the snapshot
        UIImage* blurredSnapshot = [self blurWithCoreImage: snapshot
                                                withRadius: blurRadius];
    
        //creating view that displays shadow
        UIImageView* shadowView = [[UIImageView alloc] initWithImage: blurredSnapshot];
        shadowView.center = contentView.center;
    
        //you could control appearence of the effect by modifying scale and alpha as well
        //scale is not uniform to prevent spreading of the shadow by height, as shown on your example
        shadowView.transform = CGAffineTransformMakeScale(1.05f, 0.95f);
        shadowView.alpha = 1.0f;
    
        //inserting shadow below content
        [contentViewSuperview insertSubview:shadowView belowSubview:contentView];
    }
    
    - (UIImage *)takeSnapshotOfView:(UIView *)view withTransparentInsets:(CGFloat)insets
    {
        //creating bitmap context
        CGRect contextRect;
        contextRect.origin = CGPointZero;
        contextRect.size = CGSizeMake(view.frame.size.width + 2.0f * insets, view.frame.size.height + 2.0f * insets);
    
        UIGraphicsBeginImageContext(contextRect.size);
        CGContextClearRect(UIGraphicsGetCurrentContext(), contextRect);
    
        //rendering views hierarchy to the context
        [view drawViewHierarchyInRect:CGRectMake(insets, insets, view.frame.size.width, view.frame.size.height) afterScreenUpdates:YES];
    
        //getting result image
        UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
    
        return image;
    }
    
    - (UIImage *)blurWithCoreImage:(UIImage *)sourceImage withRadius:(CGFloat)radius
    {
        CIImage *inputImage = [CIImage imageWithCGImage:sourceImage.CGImage];
    
        //apply gaussian blur filter
        CIFilter *gaussianBlurFilter = [CIFilter filterWithName: @"CIGaussianBlur"];
        [gaussianBlurFilter setValue: inputImage forKey:@"inputImage"];
        [gaussianBlurFilter setValue: @(radius) forKey:@"inputRadius"];
    
        CIContext *context = [CIContext contextWithOptions:nil];
    
        //creating output UIImage
        CGImageRef cgImage = [context createCGImage:gaussianBlurFilter.outputImage fromRect:[inputImage extent]];
        UIImage* outputImage = [UIImage imageWithCGImage:cgImage scale:1.0f orientation:UIImageOrientationUp];
    
        CFRelease(cgImage);
    
        return outputImage;
    }