I have an image in my iPad app and I basically want to place a color filter on top of it. For this I have a colored UIView that is masked to a certain shape that I put over the
Take a look at the docs for CALayer's compositingFilter: https://developer.apple.com/documentation/quartzcore/calayer/1410748-compositingfilter
On your color overlay view you can set view.layer.compositingFilter
to a CICategoryCompositeOperation to achieve blending with the content behind it. Here is some sample playground code with a multiply blend mode applied to test it out (replace the UIImage(named: "")
with your own image for testing)
import UIKit
import PlaygroundSupport
class MyViewController : UIViewController {
override func loadView() {
let mainView = UIView()
self.view = mainView
let image = UIImageView()
image.translatesAutoresizingMaskIntoConstraints = false;
image.image = UIImage(named: "maxresdefault.jpg")
mainView.addSubview(image)
let overlay = UIView()
overlay.translatesAutoresizingMaskIntoConstraints = false;
overlay.backgroundColor = .red
overlay.layer.compositingFilter = "multiplyBlendMode"
mainView.addSubview(overlay)
mainView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-0-[subview]-0-|", options: .directionLeadingToTrailing, metrics: nil, views: ["subview": view]))
mainView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-0-[subview]-0-|", options: .directionLeadingToTrailing, metrics: nil, views: ["subview": view]))
mainView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-0-[subview]-0-|", options: .directionLeadingToTrailing, metrics: nil, views: ["subview": overlay]))
mainView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-0-[subview]-0-|", options: .directionLeadingToTrailing, metrics: nil, views: ["subview": overlay]))
}
}
PlaygroundPage.current.liveView = MyViewController()
iOS 13, Swift 5
The key is setting the compositingFilter
property on the appropriate CALayer (the one on top - it blends with whatever is behind it). Color blending works between UIViews or CALayers. You can find blending modes here CICategoryCompositeOperation. To use a filter, just drop the CI
& lowercase the first letter of the name (e.g. CIDivideBlendMode
becomes divideBlendMode
). Here is some playground code to illustrate:
import UIKit
import PlaygroundSupport
let view = UIView(frame: CGRect(x: 0, y: 0, width: 500, height: 900))
PlaygroundPage.current.liveView = view
let heightIncrement = view.frame.height / 13
let widthIncrement = view.frame.width / 7
// TOP EXAMPLE (UIViews)
let backgroundView = UIView(frame: CGRect(x: widthIncrement * 2,
y: heightIncrement,
width: widthIncrement * 3,
height: heightIncrement * 5))
backgroundView.backgroundColor = .black
view.addSubview(backgroundView)
let overlayView1 = UIView(frame: CGRect(x: widthIncrement,
y: heightIncrement * 2,
width: widthIncrement * 5,
height: heightIncrement))
overlayView1.backgroundColor = UIColor.yellow.withAlphaComponent(0.3)
overlayView1.layer.compositingFilter = "darkenBlendMode"
view.addSubview(overlayView1)
let overlayView2 = UIView(frame: CGRect(x: widthIncrement,
y: heightIncrement * 4,
width: widthIncrement * 5,
height: heightIncrement))
overlayView2.backgroundColor = UIColor.yellow.withAlphaComponent(0.3)
overlayView2.layer.compositingFilter = "divideBlendMode"
view.addSubview(overlayView2)
// BOTTOM EXAMPLE (CALayers)
let backgroundLayer = CALayer()
backgroundLayer.frame = CGRect(x: widthIncrement * 2,
y: heightIncrement * 7,
width: widthIncrement * 3,
height: heightIncrement * 5)
backgroundLayer.backgroundColor = UIColor.black.cgColor
view.layer.addSublayer(backgroundLayer)
let overlayLayer1 = CALayer()
overlayLayer1.frame = CGRect(x: widthIncrement,
y: heightIncrement * 8,
width: widthIncrement * 5,
height: heightIncrement)
overlayLayer1.backgroundColor = UIColor.yellow.withAlphaComponent(0.3).cgColor
overlayLayer1.compositingFilter = "darkenBlendMode"
view.layer.addSublayer(overlayLayer1)
let overlayLayer2 = CALayer()
overlayLayer2.frame = CGRect(x: widthIncrement,
y: heightIncrement * 10,
width: widthIncrement * 5,
height: heightIncrement)
overlayLayer2.backgroundColor = UIColor.yellow.withAlphaComponent(0.3).cgColor
overlayLayer2.compositingFilter = "divideBlendMode"
view.layer.addSublayer(overlayLayer2)
The result looks like this:
I confirmed that the same code works in iOS. Here's the view controller code:
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let heightIncrement = view.frame.height / 13
let widthIncrement = view.frame.width / 7
// TOP EXAMPLE (UIViews)
let backgroundView = UIView(frame: CGRect(x: widthIncrement * 2,
y: heightIncrement,
width: widthIncrement * 3,
height: heightIncrement * 5))
backgroundView.backgroundColor = .black
view.addSubview(backgroundView)
let overlayView1 = UIView(frame: CGRect(x: widthIncrement,
y: heightIncrement * 2,
width: widthIncrement * 5,
height: heightIncrement))
overlayView1.backgroundColor = UIColor.yellow.withAlphaComponent(0.3)
overlayView1.layer.compositingFilter = "darkenBlendMode"
view.addSubview(overlayView1)
let overlayView2 = UIView(frame: CGRect(x: widthIncrement,
y: heightIncrement * 4,
width: widthIncrement * 5,
height: heightIncrement))
overlayView2.backgroundColor = UIColor.yellow.withAlphaComponent(0.3)
overlayView2.layer.compositingFilter = "divideBlendMode"
view.addSubview(overlayView2)
// BOTTOM EXAMPLE (CALayers)
let backgroundLayer = CALayer()
backgroundLayer.frame = CGRect(x: widthIncrement * 2,
y: heightIncrement * 7,
width: widthIncrement * 3,
height: heightIncrement * 5)
backgroundLayer.backgroundColor = UIColor.black.cgColor
view.layer.addSublayer(backgroundLayer)
let overlayLayer1 = CALayer()
overlayLayer1.frame = CGRect(x: widthIncrement,
y: heightIncrement * 8,
width: widthIncrement * 5,
height: heightIncrement)
overlayLayer1.backgroundColor = UIColor.yellow.withAlphaComponent(0.3).cgColor
overlayLayer1.compositingFilter = "darkenBlendMode"
view.layer.addSublayer(overlayLayer1)
let overlayLayer2 = CALayer()
overlayLayer2.frame = CGRect(x: widthIncrement,
y: heightIncrement * 10,
width: widthIncrement * 5,
height: heightIncrement)
overlayLayer2.backgroundColor = UIColor.yellow.withAlphaComponent(0.3).cgColor
overlayLayer2.compositingFilter = "divideBlendMode"
view.layer.addSublayer(overlayLayer2)
}
}
And here's the result in the simulator: