iOS 13 introduced UIImage
instances that auto-adopt to the current UIUserInterfaceStyle
(aka light or dark mode). However, there seem to be only methods to construct such images from named or system images (imageNamed:inBundle:withConfiguration:
or systemImageNamed:withConfiguration:
).
Is there a way to dynamically generate a universal light/dark mode UIImage
from Core Graphics (e.g. using two CGImage
s or using UIGraphicsImageRenderer)?
I don't see any API for that but maybe I'm wrong.
Here's my implementation in Swift 5
extension UIImage {
static func dynamicImage(withLight light: @autoclosure () -> UIImage,
dark: @autoclosure () -> UIImage) -> UIImage {
if #available(iOS 13.0, *) {
let lightTC = UITraitCollection(traitsFrom: [.current, .init(userInterfaceStyle: .light)])
let darkTC = UITraitCollection(traitsFrom: [.current, .init(userInterfaceStyle: .dark)])
var lightImage = UIImage()
var darkImage = UIImage()
lightTC.performAsCurrent {
lightImage = light()
}
darkTC.performAsCurrent {
darkImage = dark()
}
lightImage.imageAsset?.register(darkImage, with: UITraitCollection(userInterfaceStyle: .dark))
return lightImage
}
else {
return light()
}
}
}
This implementation:
displayScale
and userInterfaceLevel
)userInterfaceLevel
or horizontalSizeClass
, usage of the dark image will be unaffected and still used if and only if the interface style is dark)Assume we have two variants already loaded:
let lightImage = ...
let darkImage = ...
let result = UIImage.dynamicImage(withLight: lightImage, dark: darkImage)
Assume we want a red image, dynamic for light/dark, simply call:
let result = UIImage.dynamicImage(withLight: UIImage.generate(withColor: UIColor.red),
dark: UIImage.generate(withColor: UIColor.red))
where generate
function is as follows:
extension UIImage {
static func generate(withColor color: UIColor, size: CGSize = CGSize(width: 1, height: 1)) -> UIImage {
let rect = CGRect(x: 0, y: 0, width: size.width, height: size.height)
UIGraphicsBeginImageContext(rect.size)
let context = UIGraphicsGetCurrentContext()
context?.setFillColor(color.cgColor)
context?.fill(rect)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image ?? UIImage()
}
}