13

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

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

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

4

5 回答 5

8

您不创建新的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))
        }
    }
}

于 2020-09-07T09:58:01.213 回答
4

几天前对此进行了一些研究(也需要此功能,但到目前为止还没有实现):

  1. UIImageAsset在代码中创建
  2. register(_:with:)使用(UIImageAsset提供 userInterfaceStyle .dark / .light)注册两个 UIImages作为特征集合参数https://developer.apple.com/documentation/uikit/uiimageasset/1624974-register
于 2019-10-10T16:42:31.537 回答
3

这是我在 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()
        }
    }
}

这个实现:

  • 将当前特征与样式结合起来(以便包括displayScaleand userInterfaceLevel
  • 在正确的特征集合中执行自动关闭(以确保正确生成以编程方式生成的图像)

示例 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函数如下:

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()
    }
}

结果: 在此处输入图像描述

于 2020-09-16T22:53:22.923 回答
2
+ (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;
   }
}

也许,这就是你想要的。

于 2020-07-27T10:06:42.710 回答
1

在 Xcode 13 iOS 14.0 及更高版本上测试

我想避免使用imageAsset上面建议的底层 UIImage 属性,因为文档指出它可以为零。

我发现通过手动创建资产并使用尽可能少的特征集合注册图像,您可以获得动态图像。

private func createDynamicImage(light: UIImage, dark: UIImage) -> UIImage {
    let imageAsset = UIImageAsset()
    
    let lightMode = UITraitCollection(traitsFrom: [.init(userInterfaceStyle: .light)])
    imageAsset.register(light, with: lightMode)
    
    let darkMode = UITraitCollection(traitsFrom: [.init(userInterfaceStyle: .dark)])
    imageAsset.register(dark, with: darkMode)
    
    return imageAsset.image(with: .current)
}

下面是使用两张图片的插图,一张取自资产目录,另一张手动绘制。两者都设置了明暗模式的两种变体:

于 2021-11-10T15:04:19.437 回答