如何从 Core Graphics 生成动态明暗模式 UIImage?

Ort*_*ntz 13 uikit uiimage ios ios-darkmode uiuserinterfacestyle

iOS 13 引入UIImage了自动采用当前UIUserInterfaceStyle模式(又名亮或暗模式)的实例。但是,似乎只有从命名或系统映像(imageNamed:inBundle:withConfiguration:systemImageNamed:withConfiguration:)构建此类映像的方法。

有没有办法UIImage从 Core Graphics动态生成通用明暗模式(例如使用两个CGImages 或使用 UIGraphicsImageRenderer)?

我没有看到任何 API,但也许我错了。

bee*_*con 9

这是我在 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()
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这个实现:

  • 在评估每个图像时将当前特征与风格相结合(以便包括displayScaleuserInterfaceLevel
  • 在正确的特征集合中执行自动关闭(以确保正确生成以编程方式生成的图像)
  • 但注册深色图像时不包含当前特征,仅指定深色界面风格(因此,即使修改另一个特征属性,如userInterfaceLevelhorizontalSizeClass,深色图像的使用也不会受到影响,当且仅当界面风格为深色时仍然使用)

实施例1

假设我们已经加载了两个变体:

let lightImage = ...
let darkImage = ...
let result = UIImage.dynamicImage(withLight: lightImage, dark: darkImage)
Run Code Online (Sandbox Code Playgroud)

实施例2

假设我们想要一个红色图像,动态的亮/暗,只需调用:

let result = UIImage.dynamicImage(withLight: UIImage.generate(withColor: UIColor.red),
                                       dark: UIImage.generate(withColor: UIColor.red))
Run Code Online (Sandbox Code Playgroud)

其中generate函数如下:

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()
    }
}
Run Code Online (Sandbox Code Playgroud)

结果: 在此输入图像描述


LuR*_*Rui 7

您不会创建新的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)
Run Code Online (Sandbox Code Playgroud)

或者使用这个UIImage扩展


extension UIImage {
    func registerDarkImage(_ image: UIImage) {
        if #available(iOS 12.0, *) {
            imageAsset?.register(image, with: .init(userInterfaceStyle: .dark))
        }
    }
}

Run Code Online (Sandbox Code Playgroud)

  • 这个 `.init(userInterfaceStyle: .dark)` 给了我大小问题,我认为最好像这样初始化特征集合 `UITraitCollection(traitsFrom: [.current, .init(userInterfaceStyle: .dark)])` 如果你是在 iOS13+ 上 (4认同)