Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Programmatically create iOS 13 dynamic images
- (UIImage *)dynamicImage
{
UITraitCollection *const baseTraitCollection = /* an existing trait collection */;
UITraitCollection *const lightTraitCollection = [UITraitCollection traitCollectionWithTraitsFromCollections:@[baseTraitCollection, [UITraitCollection traitCollectionWithUserInterfaceStyle:UIUserInterfaceStyleLight]]];
UITraitCollection *const purelyDarkTraitCollection = [UITraitCollection traitCollectionWithUserInterfaceStyle:UIUserInterfaceStyleDark];
UITraitCollection *const darkTraitCollection = [UITraitCollection traitCollectionWithTraitsFromCollections:@[baseTraitCollection, purelyDarkTraitCollection]];
__block UIImage *lightImage;
[lightTraitCollection performAsCurrentTraitCollection:^{
lightImage = /* draw image */;
}];
__block UIImage *darkImage;
[darkTraitCollection performAsCurrentTraitCollection:^{
darkImage = /* draw image */;
}];
[lightImage.imageAsset registerImage:darkImage withTraitCollection:purelyDarkTraitCollection];
return lightImage;
}
@timonus

This comment has been minimized.

Copy link
Owner Author

commented Jun 8, 2019

lightImage will automatically switch to darkImage when in a dark environment.

@timonus

This comment has been minimized.

Copy link
Owner Author

commented Jun 8, 2019

Note: you don't have to do all the -performAsCurrentTraitCollection: hooplah if you're just using hardcoded, non-dynamic colors/images when drawing. The important line is -registerImage:withTraitCollection:.

@smileyborg

This comment has been minimized.

Copy link

commented Jun 8, 2019

⚠️ The trait collection you set as current (e.g. using performAsCurrentTraitCollection as in this example) should be based upon a real trait collection that you get from a view or view controller or other trait environment in your app.

You do not want to create a trait collection with only a light/dark user interface style trait and set that as the current one, because if you do that then you're setting a trait collection with every other trait unspecified, and so anything that uses other traits will get undefined behavior and may not behave the way it should.

Instead, use traitCollectionWithTraitsFromCollections: to merge/combine the base set of traits you get from some trait environment, and the single-trait collection with the specific userInterfaceStyle you want. For example:

UITraitCollection *baseTraitCollection = someView.traitCollection;

UITraitCollection *lightTraitCollection = [UITraitCollection traitCollectionWithTraitsFromCollections:@[baseTraitCollection, [UITraitCollection traitCollectionWithUserInterfaceStyle:UIUserInterfaceStyleLight]]];

[lightTraitCollection performAsCurrentTraitCollection:^{
    // do stuff
}];

On the other hand, when you register images with the image asset, you do only want to register for the least-specific trait collection you need to. In other words, if you just have light & dark variants, then you should register the images with a single-trait collection of just the userInterfaceStyle. So this is correct:

[lightImage.imageAsset registerImage:darkImage withTraitCollection:[UITraitCollection traitCollectionWithUserInterfaceStyle:UIUserInterfaceStyleDark]];
@timonus

This comment has been minimized.

Copy link
Owner Author

commented Jun 8, 2019

@smileyborg thanks for the feedback, updated the gist!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.