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:
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.
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.