Modify Uiimage Renderingmode from a Storyboard/Xib File

Modify UIImage renderingMode from a storyboard/xib file

Here's how you can do it in .xib or storyboard files:

(Obj-C) Create a category on UIImageView:

@interface UIImageView (Utils)

- (void)setImageRenderingMode:(UIImageRenderingMode)renderMode;

@end

@implementation UIImageView (Utils)

- (void)setImageRenderingMode:(UIImageRenderingMode)renderMode
{
NSAssert(self.image, @"Image must be set before setting rendering mode");
self.image = [self.image imageWithRenderingMode:renderMode];
}

@end

(Swift 4) Create an extension for UIImageView:

extension UIImageView {
func setImageRenderingMode(_ renderMode: UIImage.RenderingMode) {
assert(image != nil, "Image must be set before setting rendering mode")
// AlwaysOriginal as an example
image = image?.withRenderingMode(.alwaysOriginal)
}
}

Then in the Identity Inspector in the xib file, add a runtime attribute:

Sample Image

How to use template rendering mode in Xcode 6 Interface Builder?

I've hit the same problem. I think this is a bug.

You could dup this radar http://openradar.appspot.com/radar?id=5334033567318016, which refers to this minimal example app https://github.com/algal/TemplateImagesBrokenDemo.

I know of two workarounds for this problem

wrap in UIButton

Since tintColor works for UIButtons, one workaround is instead of UIImageView just to use a custom-type UIButton with userInteractionEnabled=false. If you disable the button's interactivity with UIView.userInteractionEnabled (as opposed to with UIControl.enabled), then you won't change the appearance of the image.

manually re-set the image in code

Another workaround is to re-set the .image property in code, after the UIImageView has been loaded from the nib. This works because setting an image in code seems to be what triggers the templating logic. For this to work, you need to re-set the image to its existing value in a way that won't be optimized away in the compiler. A snippet like this in awakeFromNib has worked for me:

override func awakeFromNib() {
super.awakeFromNib()

if shouldSetImagesManually {
// the following three-lines should in theory have no effect.
// but in fact, they ensure that the UIImageView
// correctly applies its tintColor to the vector template image

let image = self.templateImageView.image
self.templateImageView.image = nil
self.templateImageView.image = image
}

How to replace storyboard with an '.xib' file in coding

Use initWithNibName: Method for using xibs.

Example

ViewController *v1=[[ViewController alloc]initWithNibName:@"ViewController" bundle:nil];
[self addChildViewController: v1]];

How does UIImage get constructed when created from an XIB?

I know the objects constructed from a Nib are being unarchived according to the NSCoding protocol; you need to override initWithCoder: in this case.

You could use swizzling to replace UIImageView's initWithCoder: method, then snoop around to see if an image name or path is available in any of the coder's keys. This might be more effective than hacking UIImage itself, since for all we know UIImageView could be using a custom subclass that you don't have access to.

UIImageView doesn't always tint template image

Easy fix solution:

Sample Image

Just add a new runtime attribute which will set the tintColor of the UIImageView to the specified color and ensure the image is tinted.

You will still need to set your image to be rendered as a template image in your Images.xcassets file.

This way you dont need any additional outlets, extensions or lines of code.

Also take note:
It will not apply the tintColor in the user defined attribute if the tintColor on the view is the same color, they must be different.

UIImage renderingMode coloured icon - grey scale on UIAlertView

For rendering black and white :

https://stackoverflow.com/a/10033772/3319153

to handle ActionSheet or UIAlertView event, you can work with their delegate : – willPresentAlertView: or – willPresentActionSheet:

How can I keep the image color of UIButton from resetting to default?

Set Template Image

The short answer is that the image you expect to apply the color to needs to be designated as a template image. If this designation is made in the image asset's attribute inspector, all the code in the original question will work. Any calls to .withRenderingMode(.alwaysTemplate) would become unnecessary.

In Attributes Inspector

Unless the image is set as a Template Image, the color won't be applied.

The way that currently works (Xcode 9.4, Swift 4.1) is by setting the image to a template image in the GUI Attributes Inspector.

set template image attribute in GUI

Once this is done, all versions of the code in the original and edited question should work as expected.

In Code

Setting the image to a template image in code seems like it should work, but, at least with Xcode 9.4, Swift 4.1, on a simulator, it does not have permanence; the first user touch resets it.

In the code below, the .withRenderingMode(.alwaysTemplate) does cause the icon to become a template, but as soon as the user touches the icon the first time, it becomes default rendering again. This is true in the simulated iPhone (didn't test on a physical device).

override func viewDidLoad() {
super.viewDidLoad()
oneImageView = oneButton.imageView
// NOT PERMANENT
oneImageView?.image = UIImage(named: "image")?.withRenderingMode(.alwaysTemplate)
}

Can't modify a UIView that was created in storyboard

What you're missing is that Cocoa is a framework. You have to understand what it is doing and work with it, not against it.

The problem here is that this is not a legitimate way to work with a view controller and its view:

UIStoryboard *sb = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
self.storyboardVC = [sb instantiateInitialViewController];
self.viewA = [self.storyboardVC.view viewWithTag:77];

You are arbitrarily pulling out (instantiating) a view controller (storyboardVC) and working with its view (viewA), but that view controller is not part of your app's view controller hierarchy and the view is not part of its view hierarchy - none of this is happening in your app's interface. So naturally you don't see anything. You do all this on an invisible copy that is just thrown away. (You cannot use code to change the storyboard itself, you know; the storyboard is indeed read-only.)

To make changes in your view that you can actually see, wait until the framework tells you that the view is about to appear in the interface (e.g. your view controller's viewDidLoad or viewWillAppear). That is what these kinds of lifetime event messages are for.



Related Topics



Leave a reply



Submit