Use UIBarButtonItem icon in UIButton
Solution 1
Download the image from somewhere on the web, add it to your project and set the UIButton
's image to the image you just downloaded.
I did not find the same as Apple is using but I found this one. Simply change it's color in Pixelmator or Photoshop.
Solution 2
New iOS 13 support SF Symbols now
UIImage(systemName: "trash")
for swift 4.2 (call it on main thread)
extension UIBarButtonItem.SystemItem {
func image() -> UIImage? {
let tempItem = UIBarButtonItem(barButtonSystemItem: self,
target: nil,
action: nil)
// add to toolbar and render it
let bar = UIToolbar()
bar.setItems([tempItem],
animated: false)
bar.snapshotView(afterScreenUpdates: true)
// got image from real uibutton
let itemView = tempItem.value(forKey: "view") as! UIView
for view in itemView.subviews {
if let button = view as? UIButton,
let image = button.imageView?.image {
return image.withRenderingMode(.alwaysTemplate)
}
}
return nil
}
}
UIBarButtonSystemItem.play.image()
For Objective-C:
+ (UIImage *)imageFromSystemBarButton:(UIBarButtonSystemItem)systemItem {
UIBarButtonItem* tempItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:systemItem target:nil action:nil];
// Add to toolbar and render it
UIToolbar *bar = [[UIToolbar alloc] init];
[bar setItems:@[tempItem] animated:NO];
[bar snapshotViewAfterScreenUpdates:YES];
// Get image from real UIButton
UIView *itemView = [(id)tempItem view];
for (UIView* view in itemView.subviews) {
if ([view isKindOfClass:[UIButton class]]) {
return [(UIButton*)view imageForState:UIControlStateNormal];
}
}
return nil;
}
Solution 3
Here's a solution that works with ANY System bar button item + it supports tintColor
:
- (void)viewDidLoad {
[super viewDidLoad];
[self.button setImage:[self imageFromSystemBarButton:UIBarButtonSystemItemTrash]
forState:UIControlStateNormal];
self.button.tintColor = [UIColor redColor];
}
- (UIImage *)imageFromSystemBarButton:(UIBarButtonSystemItem)systemItem {
// Holding onto the oldItem (if any) to set it back later
// could use left or right, doesn't matter
UIBarButtonItem *oldItem = self.navigationItem.rightBarButtonItem;
UIBarButtonItem *tempItem = [[UIBarButtonItem alloc]
initWithBarButtonSystemItem:systemItem
target:nil
action:nil];
// Setting as our right bar button item so we can traverse its subviews
self.navigationItem.rightBarButtonItem = tempItem;
// Don't know whether this is considered as PRIVATE API or not
UIView *itemView = (UIView *)[self.navigationItem.rightBarButtonItem performSelector:@selector(view)];
UIImage *image = nil;
// Traversing the subviews to find the ImageView and getting its image
for (UIView *subView in itemView.subviews) {
if ([subView isKindOfClass:[UIImageView class]]) {
image = ((UIImageView *)subView).image;
break;
}
}
// Setting our oldItem back since we have the image now
self.navigationItem.rightBarButtonItem = oldItem;
return image;
}
P.S. Feel free to improve if you know of a better way, thanks.
Solution 4
Based on @yycking answer, i wrote an suitable Swift 4 extension:
// UIImage+FromSystemItem.swift
import UIKit
extension UIImage {
public convenience init?(systemItem sysItem: UIBarButtonItem.SystemItem, renderingMode:UIImage.RenderingMode = .automatic) {
guard let sysImage = UIImage.imageFromSystemItem(sysItem, renderingMode: renderingMode)?.cgImage else {
return nil
}
self.init(cgImage: sysImage)
}
private class func imageFromSystemItem(_ systemItem: UIBarButtonItem.SystemItem, renderingMode:UIImage.RenderingMode = .automatic) -> UIImage? {
let tempItem = UIBarButtonItem(barButtonSystemItem: systemItem, target: nil, action: nil)
// add to toolbar and render it
let bar = UIToolbar()
bar.setItems([tempItem], animated: false)
bar.snapshotView(afterScreenUpdates: true)
// got image from real uibutton
let itemView = tempItem.value(forKey: "view") as! UIView
for view in itemView.subviews {
if view is UIButton {
let button = view as! UIButton
let image = button.imageView!.image!
image.withRenderingMode(renderingMode)
return image
}
}
return nil
}
}
Example with action button (default coloring):
let actionImage = UIImage(systemItem: .action)
let myButton = UIButton(frame: CGRect(x: 0, y: 0, width: 30, height: 30))
myButton.setImage(actionImage, for: .normal)
view.addSubview(myButton)
If you want your image to always be treated as a template regardless of context, set renderingMode to .alwaysTemplate
let actionImage = UIImage(systemItem: .action, renderingMode: .alwaysTemplate)
Solution 5
As already mentioned in the comments to @Islam Q's answer, the there presented solution might fail, if the UINavigationItem isn't currently rendered onscreen. It fails, for example, if the view controller isn't currently loaded. In fact the problem seems to be the missing layout of the UINavigationBar in these cases.
A more 'bulletproof' version would be to use a specially created UINavigationBar object just for getting the system item images. This would also make save saving and restoring of any existing UIBarButtonItems obsolete.
I've packed this into a small helper class:
LEABarButtonSystemItemImage.h:
#import <UIKit/UIKit.h>
/**
LEABarButtonSystemItemImage interface
*/
@interface LEABarButtonSystemItemImage : NSObject
+ (UIImage *)imageFromBarButtonSystemItem:(UIBarButtonSystemItem)pBarButtonSystemItem;
+ (UIImage *)customImageForBarButtonSystemItem:(UIBarButtonSystemItem)pBarButtonSystemItem;
+ (NSDictionary<__kindof NSNumber*, __kindof UIImage*> *)barButtonItemImages;
@end
LEABarButtonSystemItemImage.m
#import "LEABarButtonSystemItemImage.h"
/**
LEABarButtonSystemItemImage implementation
*/
@implementation LEABarButtonSystemItemImage
/*
imageFromBarButtonSystemItem:
*/
+ (UIImage *)imageFromBarButtonSystemItem:(UIBarButtonSystemItem)pBarButtonSystemItem {
static const CGFloat defaultNBBtnHW = 44.0;
UINavigationBar * tempNavigationBar = [[UINavigationBar alloc] initWithFrame:CGRectMake(0, 0, defaultNBBtnHW, defaultNBBtnHW)];
UINavigationItem * tempNavigationItem = [[UINavigationItem alloc] init];
UIBarButtonItem * tempBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:pBarButtonSystemItem target:nil action:NULL];
tempNavigationBar.items = @[tempNavigationItem];
tempNavigationItem.rightBarButtonItems = @[tempBarButtonItem];
UIImage * barButtonSystemItemImage = nil;
@try {
UIView * barButtonItemView = [tempBarButtonItem valueForKey:@"view"];
for (UIView* subview in barButtonItemView.subviews) {
if ([subview isKindOfClass:UIImageView.class]) {
barButtonSystemItemImage = ((UIImageView *)subview).image;
break;
}
}
} @catch (...) { NSLog(@"%s: Exception while retrieving image from UIBarButtonItem!", __PRETTY_FUNCTION__); }
return (barButtonSystemItemImage ?: [LEABarButtonSystemItemImage customImageForBarButtonSystemItem:pBarButtonSystemItem]);
}
/*
customImageForBarButtonSystemItem:
*/
+ (UIImage *)customImageForBarButtonSystemItem:(UIBarButtonSystemItem)pBarButtonSystemItem {
NSString * customBarButtonSystemItemImageName = nil;
switch (pBarButtonSystemItem) {
case UIBarButtonSystemItemDone: customBarButtonSystemItemImageName = @"customBarButtonSystemItemDone"; break;
case UIBarButtonSystemItemCancel: customBarButtonSystemItemImageName = @"customBarButtonSystemItemCancel"; break;
case UIBarButtonSystemItemEdit: customBarButtonSystemItemImageName = @"customBarButtonSystemItemEdit"; break;
case UIBarButtonSystemItemSave: customBarButtonSystemItemImageName = @"customBarButtonSystemItemSave"; break;
case UIBarButtonSystemItemAdd: customBarButtonSystemItemImageName = @"customBarButtonSystemItemAdd"; break;
case UIBarButtonSystemItemCompose: customBarButtonSystemItemImageName = @"customBarButtonSystemItemCompose"; break;
case UIBarButtonSystemItemReply: customBarButtonSystemItemImageName = @"customBarButtonSystemItemReply"; break;
case UIBarButtonSystemItemAction: customBarButtonSystemItemImageName = @"customBarButtonSystemItemAction"; break;
case UIBarButtonSystemItemOrganize: customBarButtonSystemItemImageName = @"customBarButtonSystemItemOrganize"; break;
case UIBarButtonSystemItemBookmarks: customBarButtonSystemItemImageName = @"customBarButtonSystemItemBookmarks"; break;
case UIBarButtonSystemItemSearch: customBarButtonSystemItemImageName = @"customBarButtonSystemItemSearch"; break;
case UIBarButtonSystemItemRefresh: customBarButtonSystemItemImageName = @"customBarButtonSystemItemRefresh"; break;
case UIBarButtonSystemItemStop: customBarButtonSystemItemImageName = @"customBarButtonSystemItemStop"; break;
case UIBarButtonSystemItemCamera: customBarButtonSystemItemImageName = @"customBarButtonSystemItemCamera"; break;
case UIBarButtonSystemItemTrash: customBarButtonSystemItemImageName = @"customBarButtonSystemItemTrash"; break;
case UIBarButtonSystemItemPlay: customBarButtonSystemItemImageName = @"customBarButtonSystemItemPlay"; break;
case UIBarButtonSystemItemPause: customBarButtonSystemItemImageName = @"customBarButtonSystemItemPause"; break;
case UIBarButtonSystemItemRewind: customBarButtonSystemItemImageName = @"customBarButtonSystemItemRewind"; break;
case UIBarButtonSystemItemFastForward: customBarButtonSystemItemImageName = @"customBarButtonSystemItemFastForward"; break;
case UIBarButtonSystemItemUndo: customBarButtonSystemItemImageName = @"customBarButtonSystemItemUndo"; break;
case UIBarButtonSystemItemRedo: customBarButtonSystemItemImageName = @"customBarButtonSystemItemRedo"; break;
case UIBarButtonSystemItemPageCurl: customBarButtonSystemItemImageName = @"customBarButtonSystemItemPageCurl"; break;
default: break;
}
return (customBarButtonSystemItemImageName
? [UIImage imageNamed:customBarButtonSystemItemImageName]
: nil);
}
/*
barButtonItemImages
*/
+ (NSDictionary<__kindof NSNumber*, __kindof UIImage*> *)barButtonItemImages {
NSMutableDictionary<__kindof NSNumber*, __kindof UIImage*> * barButtonItemImages = [NSMutableDictionary dictionary];
// From: https://github.com/nst/iOS-Runtime-Headers/blob/master/Frameworks/UIKit.framework/UIBarButtonItem.h
// unsigned int systemItem : 7;
for (NSUInteger uIndex = 0; uIndex < (1<<7); ++uIndex) {
UIImage* systemImage = [LEABarButtonSystemItemImage imageFromBarButtonSystemItem:uIndex];
if (systemImage) {
[barButtonItemImages setObject:systemImage forKey:@(uIndex)];
}
}
NSLog(@"%s: %@", __PRETTY_FUNCTION__, barButtonItemImages);
return barButtonItemImages;
}
@end
As an add on/fallback , the method returns an custom image, if no system item image could be retrieved. Of course these custom images must be present in the apps bundle.
The last method 'barButtonImages' was implemented just for curiosity... in the UIBarButtonItem header the member systemItem is declared to use 7 bits (0..127). Currently only 22 values are documented from UIBarButtonSystemItemDone to UIBarButtonItemSystemItemPageCurl... and in fact; I found some undocumented images starting with indexes above 100 (tested on iOS 9.3 in the 6S+ simulator) :-)
Comments
-
ZviBar about 3 years
UIBarButtonItem
has multiple icons available. Is it possible to use the icon which appears after setting its identifier to 'trash':with an
UIButton
? There is no straighforward method to do that like setting the identifier or style. -
ZviBar over 10 yearsIt's not bad but I'd rather it be blue.
-
yoeriboven over 10 yearsYou can easily do that in Photoshop and I even think it is possible to do it with
tintColor
. Let me check that one. -
yoeriboven over 10 yearsCould not get it to work with
tintColor
. Shouldn't be a problem since you can simply change it's color in Photoshop or Pixelmator. -
Gram almost 9 yearsThank you, this helped me a lot. I found that I could use the tempItem directly into the bar. Although not exactly a UIButton I had a hard time googling the answer.
-
Islam almost 9 yearsYou're welcome. I tried to break it down as much as possible so it's easily understandable.
-
Hans Brende over 8 yearsHow would you translate
UIView *itemView = (UIView *)[self.navigationItem.rightBarButtonItem performSelector:@selector(view)];
into Swift? -
Islam over 8 yearsHaven't done it in Swift, but I hope answers to this question will help.
-
LaborEtArs over 8 yearsOne trap is hidden ;-) This code only works, if the UINavigationItem is currently rendered onscreen. It fails, for example, if the view controller isn't currently loaded (e.g. if it is one of the not selected view controllers in a UITabBarController).
-
aroth about 8 yearsWhat @LaborEtArs said. This is a good idea, but it's not a general-purpose solution to the problem.
-
LaborEtArs about 8 yearsI've added a more bulletproof solution to retrieve the system item images in another answer.
-
Frans over 7 yearsto have the image use the tint color of the button, add .imageWithRenderingMode(.AlwaysTemplate)
-
Lepidopteron over 7 yearsReally sad that the accepted answer is supposed to break copyright rules... "copy image somewhere and add it to your project"
-
quemeful about 7 years@Lepidopteron actually there are many "somewheres" that are made exactly for this purpose
-
Sanju about 7 yearscolor is changing! default color is blue but your code output color is black . Any solution?
-
Peter Kreinz about 7 years@sanju: i have updated the default rendering mode to .automatic (default blue color)
-
kelin about 7 years@Sanju, that's because UIBarButtonItem use template image. Images are black, but when they are drawn with tint color of the bar. More about template rendering mode.
-
David almost 7 yearsNo longer works in Xcode 9. Image must be rendered before it can be extracted.
-
Darkwonder almost 7 yearsThis solution doesn't work in Xcode 9/iOS 11. Please update the answer with guard as? View and don't force unwrap. My app crashed because of this.
-
Denis Balko almost 7 yearsThanks for the answer Peter, I just want to point out it no longer works for Swift 4,
tempItem.value(forKey: "view")
is nil and forceful unwrapping gives an error. -
RubberDucky4444 over 6 yearsI keep getting a nil return on the line.... let itemView = tempItem.value(forKey: "view") as! UIView
-
Efren over 6 yearsIt appears that it still does not work on Swift 4, when you say Solved, in which version did you test Peter?
-
oguzhan00 over 5 yearsThis solution is doesn't work on Swift 4, it getting nil image return error
-
Peter Kreinz over 5 yearsDeployment Target 11.4 (Swift 4.2): let itemView = tempItem.value(forKey: "view") as! UIView = Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value
-
Peter Kreinz over 5 yearsYou are all right. It does not work on Swift 4 anymore. I will try to find a solution ...
-
Peter Dietz about 5 yearsApple has several icons available at: developer.apple.com/design/human-interface-guidelines/ios/… action, add, bookmarks, camera, cancel, compose, done, edit, fastForward, organize, pause, play, redo, refresh, reply, rewind, save, search, stop, trash, undo
-
adijazz91 almost 5 yearsWhere ever you are adding add the function in the Main queue, then it will work perfectly. Enjoy \m/