2

How can I can add a gradient view that has blur effects in Swift? I can add a gradient layer (CAGradientLayer) quite easily to a view. I can also add a blur view (UIVisualEffectView) separately.

How can I combine both to create a blur view that also has a gradient element where by full blur fading to no blur?

An example of a similiar effect:

Example of what I'm looking for

nakano531
  • 498
  • 7
  • 13
Ranknoodle
  • 847
  • 12
  • 28

2 Answers2

1

If you know CoreImage, you can easily chain two filters together, one from the CICategoryBlur and one from the category CICategoryGradient.

Here's an example of usage. TO chain, just take the output of the first filter as the input of the next:

func convertImageToBW(image:UIImage) -> UIImage {

    let filter = CIFilter(name: "CIPhotoEffectMono")

    // convert UIImage to CIImage and set as input

    let ciInput = CIImage(image: image)
    filter?.setValue(ciInput, forKey: "inputImage")

    // get output CIImage, render as CGImage first to retain proper UIImage scale

    let ciOutput = filter?.outputImage
    let ciContext = CIContext()
    let cgImage = ciContext.createCGImage(ciOutput!, from: (ciOutput?.extent)!)

    return UIImage(cgImage: cgImage!)
}

The link above is to the Apple documentation.

  • And if you're looking to make the blur amount continuously vary across your gradient, you can use [CIMaskedVariableBlur](https://developer.apple.com/library/content/documentation/GraphicsImaging/Reference/CoreImageFilterReference/index.html#//apple_ref/doc/filter/ci/CIMaskedVariableBlur), with the same gradient as a mask. – rickster Nov 14 '16 at 22:49
  • @dfd - Thanks for your answer! Do you know if possible to have the fiter apply to a UIView... where the view can have this blur/gradient effect that is on top of an UIImageView? So that I can swap images below it and also have this blur/gradient filter slide in and out via animation? – Ranknoodle Nov 14 '16 at 23:03
  • 1
    Short answer - yes. Longer answer? A CIImage (the outputImage of a CI filter) is best thought of as a "recipe" for an image. Put it into a CG context, write to a CALayer or subclass, and you can make it part of any UIView or subclass. In my next upgrade I'm moving my controls from the "main" view into a sliding "control board" (think keyboard with sliders and other controls) that do just that - apply a blur background over the part of the image it's projected over. It animates and orients to the bottom or right, depending on the longer dimension. –  Nov 15 '16 at 00:07
1

To achieve a blur view with gradient blur radius, you can do the following (per Apple document):

let ciContext = CIContext(options: nil)

if let inputImage = CIImage(image: yourUIImage) {
        let extent = inputImage.extent
        let h = extent.size.height
        guard let gradient = CIFilter(name: "CILinearGradient") else { return }
        gradient.setValue(CIVector(x: 0, y: 0.85 * h), forKey: "inputPoint0")
        gradient.setValue(CIColor.green, forKey: "inputColor0")
        gradient.setValue(CIVector(x: 0, y: 0.50 * h), forKey: "inputPoint1")
        gradient.setValue(CIColor(red: 0, green: 1, blue: 0, alpha: 0), forKey: "inputColor1")

        guard let mask = CIFilter(name: "CIMaskedVariableBlur") else { return }
        mask.setValue(inputImage.clampedToExtent(), forKey: kCIInputImageKey)
        // Set your blur radius here, default is 5
        mask.setValue(10, forKey: kCIInputRadiusKey) 
        mask.setValue(gradient.outputImage, forKey: "inputMask")

        guard let output = mask.outputImage,
            let cgImage = ciContext.createCGImage(output, from: extent) else { return }
        outUIImage = UIImage(cgImage: cgImage)
}

Here, inputPoint0 is the starting point of gradient ramp, inputPoint1 is the ending point. Note that for CIVector y increases from bottom to top. The input color you set doesn't matter, only its alpha is used to determine blur radius.

For your second question of dynamically applying CIFilter to an image underneath it, no, it's not possible. There is an Apple document on backgroundFilters that might make you think it is doable, until you see at the bottom ...

This property is not supported on layers in iOS.

What you should do is that, whenever you reset the image, apply the above operation to that image and set it to the image view.

Jack Guo
  • 3,959
  • 8
  • 39
  • 60