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:
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:
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.
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
Detect If the Application in Background or Foreground in Swift
How to Connect Viewcontroller.Swift to Viewcontroller in Storyboard
How to Have a Dynamic List of Views Using Swiftui
Change Push Notification Sound
Radial Gradient Background in Swift
Do We Need to Use _Weak Self Inside Uianimationblocks in Arc
Pushing a Navigation Controller Is Not Supported
iOS Navigation Bar Item Image Size
Deploying an iOS Application Using Apple Enterprise Developer Program
iOS 8 Keyboard Hides My Textview
Uiwebview Webpage Caching for Offline Viewing
Converting Nsdictionary Object to Nsdata Object and Vice-Versa
Using Cabasicanimation to Rotate a Uiimageview More Than Once
How to Properly Order Data from Firebase Chronologically
Cordova Run with iOS Error .. Error Code 65 for Command: Xcodebuild with Args:
iOS Builds/Ipa Creation No Longer Works from the Command Line