Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

iOS 13 `withTintColor` not obeying the color I assign

I find the behavior of the new iOS 13 UIImage property withTintColor(_:renderingMode:) incomprehensible. What is it for, and how does it relate to the tint color of the context in which the image appears?

For example:

let im = UIImage(systemName:"circle.fill")?.withTintColor(.red)
let iv = UIImageView(image:im)
iv.frame.origin = CGPoint(x: 100, y: 400)
self.view.addSubview(iv)

I said .red. But it's blue:

enter image description here

Evidently it's blue because it takes its color from the tint color of the surrounding context. But then what's the point of giving the image itself a tint color?

But it gets even weirder. I'll draw my own image and give it a tint color:

let sz = CGSize(width: 20, height: 20)
let im = UIGraphicsImageRenderer(size:sz).image { ctx in
    ctx.cgContext.fillEllipse(in:CGRect(origin:.zero, size:sz))
}.withTintColor(.red)
let iv = UIImageView(image:im)
iv.frame.origin = CGPoint(x: 100, y: 400)
self.view.addSubview(iv)

I said .red and it's red:

enter image description here

What accounts for the inconsistency?

like image 278
matt Avatar asked Nov 14 '19 22:11

matt


People also ask

Can I change the color of an app on the iPhone?

Few apps on the iPhone give you ways to change their color scheme aside from Dark Mode in iOS 13 and later, but that doesn't mean you can't give an app a new color theme or filtered look. With the Shortcuts app in iOS 14 and later, it's totally possible, and it'll work in practically any app.

How will iOS 13’s dark mode affect apps?

Before iOS 13, apps had the opportunity to implement a dark mode themselves. Now, Apple has a system where apps are encouraged — if not expected — to change their themes based on the system theme. Down the line, we expect the relationship between iOS and third-party apps to be much more harmonious when it comes to Dark Mode.

Can I change an app's color theme without the Shortcuts app?

Without the Shortcuts app, you can still change an app's color theme fairly easily using the Accessibility Shortcut. And you'll have more options to do so.

How to change the color of multicolor symbols?

We can change the color of multicolor symbols by set both renderingMode and foregroundColor. <1> We enable multicolor mode by set .renderingMode (.original). <2> We override the color of the symbol's part by setting .foregroundColor. In this case, we set the stroke of the folder to pink.


1 Answers

withTintColor is probably intended primarily for use only with symbol images, which are always treated as templates and have no color of their own. However, it is also very nice to be able to treat an ordinary image as a template and give it a color.

Let's say you're drawing into a custom UIView's draw(_:) method, or drawing in a UIImageView graphics context. There has always been a problem with template views; you have no access here to their template behavior, so there is no way to tint them. withTintColor solves that problem.

There is no doubt, however, that the behavior of these methods is profoundly weird when you use them in any other way:

  • As soon as you say withTintColor, the resulting image is treated as a template image, even if you say .alwaysOriginal. So your rendering mode is now being ignored!

  • Moreover, in contexts where there is already a tintColor, there can now be two competing tint colors: that of the context (the surrounding view or the inherited tintColor) and that of the image when you say withTintColor. And if this is a template image — a symbol image, or an .alwaysTemplate image, or an image in a template context like a button — then the context's tintColor wins, and the color that you specifically applied by saying withTintColor is ignored!!

For example, this results in a blue symbol image (because the default image view tintColor is blue) even though you specifically set the symbol image's tint color to red:

let im = UIImage(systemName:"circle.fill")?.withTintColor(.red)
let iv = UIImageView(image:im)

To get a red symbol image, you have to say this:

let im = UIImage(systemName:"circle.fill")?.withTintColor(.red,
    renderingMode: .alwaysOriginal)

So in the first code, the tint color you assign is being ignored (we get blue, not red), but in the second code, the rendering mode you assign is being ignored (it's still a template image, but now it's red as requested). Weird, eh?

(I find it very hard to believe that this is the intended behavior, and I've filed a bug, but so far no response from Apple.)

like image 157
matt Avatar answered Oct 17 '22 18:10

matt