[英]How to generate a dynamic light/dark mode UIImage from Core Graphics?
iOS 13 引入了UIImage
實例,這些實例自動采用當前的UIUserInterfaceStyle
(又名淺色或深色模式)。 但是,似乎只有從命名或系統圖像( imageNamed:inBundle:withConfiguration:
或systemImageNamed:withConfiguration:
)構造此類圖像的方法。
有沒有辦法從 Core Graphics 動態生成通用明/暗模式UIImage
(例如使用兩個CGImage
或使用 UIGraphicsImageRenderer)?
我沒有看到任何 API 但也許我錯了。
您不創建新的UIImageAsset
,而是從現有UIImage
的imageAsset
屬性中引用一個,您可以使用UIImageAsset.register(_:with:)
方法向其中添加暗圖像變體。
// Prepare a UIImage for light mode.
let lightImage: UIImage!
// Prepare a UIImage for dark mode.
let darkImage: UIImage!
// Register your dark mode image to the light mode image's image asset.
lightImage?.imageAsset?.register(darkImage, with: .init(userInterfaceStyle: .dark))
// Now your light mode image actually becomes a dynamic image. Use it.
someImageView.image = lightImage
someButton.setImage(lightImage, for: .normal)
或者使用這個UIImage
擴展
extension UIImage {
func registerDarkImage(_ image: UIImage) {
if #available(iOS 12.0, *) {
imageAsset?.register(image, with: .init(userInterfaceStyle: .dark))
}
}
}
幾天前對此進行了一些研究(也需要此功能,但到目前為止還沒有實現):
UIImageAsset
register(_:with:)
注冊兩個UIImageAsset
(提供 userInterfaceStyle.dark /.light)作為特征集合參數https://developer.apple.com/documentation/uikit/uiimageasset/1624974-register+ (UIImage*)dynamicImageWithNormalImage:(UIImage*)normalImage darkImage:(UIImage*)darkImage{
if (normalImage == nil || darkImage == nil) {
return normalImage ? : darkImage;
}
if (@available(iOS 13.0, *)) {
UIImageAsset* imageAseset = [[UIImageAsset alloc]init];
// 注冊 lightImage
UITraitCollection* lightImageTrateCollection = [UITraitCollection traitCollectionWithTraitsFromCollections:
@[[UITraitCollection traitCollectionWithUserInterfaceStyle:UIUserInterfaceStyleLight],
[UITraitCollection traitCollectionWithDisplayScale:normalImage.scale]]];
[imageAseset registerImage:normalImage withTraitCollection:lightImageTrateCollection];
// 注冊 darkImage
UITraitCollection* darkImageTrateCollection = [UITraitCollection traitCollectionWithTraitsFromCollections:
@[[UITraitCollection traitCollectionWithUserInterfaceStyle:UIUserInterfaceStyleDark],
[UITraitCollection traitCollectionWithDisplayScale:darkImage.scale]]];
[imageAseset registerImage:darkImage withTraitCollection:darkImageTrateCollection];
return [imageAseset imageWithTraitCollection:[UITraitCollection currentTraitCollection]];
}
else {
return normalImage;
}
}
也許,這就是你想要的。
這是我在 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: darkTC)
return lightImage
}
else {
return light()
}
}
}
這個實現:
displayScale
和userInterfaceLevel
)假設我們已經加載了兩個變體:
let lightImage = ...
let darkImage = ...
let result = UIImage.dynamicImage(withLight: lightImage, dark: darkImage)
假設我們想要一個紅色圖像,動態的亮/暗,只需調用:
let result = UIImage.dynamicImage(withLight: UIImage.generate(withColor: UIColor.red),
dark: UIImage.generate(withColor: UIColor.red))
其中generate
function如下:
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()
}
}
我想避免使用上面建議的底層 UIImage imageAsset
屬性,因為文檔指出它可以為零。
我發現通過創建資產並針對它注冊圖像,您可以獲得相同的結果。
注意:將
UITraitCollection(traitsFrom: [.current
初始化器與當前集一起使用很重要,因為它包含有關顯示比例等的更多信息。
private func createDynamicImage(light: UIImage, dark: UIImage) -> UIImage {
let imageAsset = UIImageAsset()
let lightMode = UITraitCollection(traitsFrom: [.current, .init(userInterfaceStyle: .light)])
imageAsset.register(light, with: lightMode)
let darkMode = UITraitCollection(traitsFrom: [.current, .init(userInterfaceStyle: .dark)])
imageAsset.register(dark, with: darkMode)
return imageAsset.image(with: .current)
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.