簡體   English   中英

如何從 Core Graphics 生成動態明暗模式 UIImage?

[英]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而是從現有UIImageimageAsset屬性中引用一個,您可以使用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))
        }
    }
}

幾天前對此進行了一些研究(也需要此功能,但到目前為止還沒有實現):

  1. 在代碼中創建UIImageAsset
  2. 使用 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()
        }
    }
}

這個實現:

  • 將當前特征與樣式相結合(以包括displayScaleuserInterfaceLevel
  • 在正確的特征集合中執行自動關閉(以確保正確生成以編程方式生成的圖像)

示例 1

假設我們已經加載了兩個變體:

let lightImage = ...
let darkImage = ...
let result = UIImage.dynamicImage(withLight: lightImage, dark: darkImage)

示例 2

假設我們想要一個紅色圖像,動態的亮/暗,只需調用:

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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM