【发布时间】:2014-01-17 14:12:20
【问题描述】:
UIBarButtonItem 有多个图标可用。是否可以使用将其标识符设置为“垃圾”后出现的图标:
UIButton?没有像设置 identifier 或 style 这样的简单方法。
【问题讨论】:
标签: ios cocoa-touch uibutton uibarbuttonitemstyle
UIBarButtonItem 有多个图标可用。是否可以使用将其标识符设置为“垃圾”后出现的图标:
UIButton?没有像设置 identifier 或 style 这样的简单方法。
【问题讨论】:
标签: ios cocoa-touch uibutton uibarbuttonitemstyle
新 iOS 13 支持 SF Symbols 现在UIImage(systemName: "trash")
对于 swift 4.2 (在主线程上调用)
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()
对于 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;
}
【讨论】:
这是一个适用于ANY系统栏按钮项目的解决方案+它支持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;
}
附言如果您知道更好的方法,请随时改进,谢谢。
【讨论】:
UIView *itemView = (UIView *)[self.navigationItem.rightBarButtonItem performSelector:@selector(view)]; 翻译成 Swift?
从网络上的某处下载图像,将其添加到您的项目中并将UIButton 的图像设置为您刚刚下载的图像。
我没有找到与 Apple 使用的相同,但我找到了 this one。只需在 Pixelmator 或 Photoshop 中更改它的颜色。
【讨论】:
tintColor 做到这一点。让我检查一下。
tintColor 一起使用。应该不是问题,因为您可以在 Photoshop 或 Pixelmator 中简单地更改它的颜色。
根据@yycking 的回答,我写了一个合适的 Swift 4 扩展:
// 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
}
}
动作按钮示例(默认颜色):
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)
如果您希望您的图像始终被视为模板,无论如何 上下文,将renderingMode设置为.alwaysTemplate
let actionImage = UIImage(systemItem: .action, renderingMode: .alwaysTemplate)
【讨论】:
tempItem.value(forKey: "view") 为 nil 并且强制展开会出错。
正如@Islam Q 的答案中已经提到的那样,如果 UINavigationItem 当前未在屏幕上呈现,则所提出的解决方案可能会失败。例如,如果当前未加载视图控制器,它会失败。事实上,在这些情况下,问题似乎是 UINavigationBar 缺少布局。
更“防弹”的版本是使用专门创建的 UINavigationBar 对象来获取系统项目图像。这也将使保存和恢复任何现有的 UIBarButtonItems 过时。
我已经把它打包到一个小的帮助类中:
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
作为 add on/fallback ,如果无法检索到系统项目图像,该方法将返回自定义图像。当然,这些自定义图像必须存在于应用程序包中。
最后一个方法“barButtonImages”是为了好奇而实现的……在 UIBarButtonItem 标头中,成员 systemItem 被声明为使用 7 位 (0..127)。目前,从 UIBarButtonSystemItemDone 到 UIBarButtonItemSystemItemPageCurl 仅记录了 22 个值......事实上;我发现了一些索引超过 100 的未记录图像(在 6S+ 模拟器中的 iOS 9.3 上测试):-)
【讨论】:
所有 iOS 系统图标都可以使用一个方便的小应用程序(非常合适)iOS Artwork Extractor 来提取。当我想模仿 iOS 系统行为时,我一直使用它。
在以下位置下载 Xcode 项目:
【讨论】:
在 Swift 4.2 中使用 Quartz2D
基于扩展从UIBarButtonSystemItem 中提取图像的解决方案在 iOS 11/12 中不起作用,因此我决定添加一个自定义类来绘制图标而不添加任何 .png
这是为 .trash 和 .action 实现的,它们是我项目中需要的图标。随意添加其余部分。
如下使用(需符合SystemIConDelegate协议,设置Delegate属性并添加所需方法):
let trashIcon = SystemIcon(withType: .trash)
trashIcon.delegate = self
shareButton = UIButton()
shareButton.addSubview(trashIcon)
SystemIcon 课程在这里。它针对 30 X 30 点进行了优化:
import UIKit
protocol SystemIconDelegate {
func systemIconButtonClicked()
}
class SystemIcon: UIView {
var type: UIBarButtonItem.SystemItem!
let color = UIColor.blue
var delegate: SystemIconDelegate?
convenience init(withType: UIBarButtonItem.SystemItem) {
self.init(frame: CGRect(x: 0, y: 0, width: 30, height: 30))
self.backgroundColor = .clear
self.type = withType
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
self.delegate?.systemIconButtonClicked()
}
override func draw(_ rect: CGRect) {
let w = self.frame.width
let h = self.frame.height
let context = UIGraphicsGetCurrentContext()
context?.setStrokeColor(color.cgColor)
context?.setLineWidth(1.0)
switch type! {
case .action:
//Box
context?.stroke(CGRect(x: w * 0.16, y: h * 0.3, width: w * 0.69, height: h * 0.69))
context?.setFillColor(UIColor.white.cgColor)
context?.fill(CGRect(x: w * 0.4, y: 0, width: w * 0.2, height: h * 0.5))
//Arrow
context?.move(to: CGPoint(x: w * 0.5, y: h * 0.02))
context?.addLine(to: CGPoint(x: w * 0.5, y: h * 0.64))
context?.move(to: CGPoint(x: w * 0.33, y: h * 0.19))
context?.addLine(to: CGPoint(x: w * 0.5, y: h * 0.02))
context?.addLine(to: CGPoint(x: w * 0.67, y: h * 0.19))
context?.strokePath()
case .trash:
context?.move(to: CGPoint(x: w * 0.1, y: h * 0.15))
context?.addLine(to: CGPoint(x: w * 0.9, y: h * 0.15))
//Can
context?.move(to: CGPoint(x: w * 0.2, y: h * 0.15))
context?.addArc(tangent1End: CGPoint(x: w * 0.25, y: h * 0.95), tangent2End: CGPoint(x: w * 0.5, y:h * 0.95), radius: CGFloat.x(2.0))
context?.addArc(tangent1End: CGPoint(x: w * 0.75, y: h * 0.95), tangent2End: CGPoint(x: w * 0.8, y: h * 0.15), radius: CGFloat.x(2.0))
context?.addLine(to: CGPoint(x: w * 0.8, y: h * 0.15))
// Handle
context?.move(to: CGPoint(x: w * 0.34, y: h * 0.15))
context?.addArc(tangent1End: CGPoint(x: w * 0.34, y: h * 0.02), tangent2End: CGPoint(x: w * 0.5, y: h * 0.02), radius: CGFloat.x(2.0))
context?.addArc(tangent1End: CGPoint(x: w * 0.66, y : h * 0.02), tangent2End: CGPoint(x: w * 0.66, y: h * 0.15), radius: CGFloat.x(2.0))
context?.addLine(to: CGPoint(x: w * 0.66, y: h * 0.15))
//Lines
context?.move(to: CGPoint(x: w * 0.35, y: h * 0.25))
context?.addLine(to: CGPoint(x: w * 0.38, y: h * 0.8))
context?.move(to: CGPoint(x: w * 0.5, y: h * 0.25))
context?.addLine(to: CGPoint(x: w * 0.5, y: h * 0.8))
context?.move(to: CGPoint(x: w * 0.65, y: h * 0.25))
context?.addLine(to: CGPoint(x: w * 0.62, y: h*0.8))
default:
break
}
context?.strokePath()
}
}
【讨论】:
我将它用于表格单元格中的 imageView,但不知何故,无论我在哪里设置,色调颜色都不起作用......所以我最终得到了这段代码,你可以在其中为图像设置颜色。
+ (UIImage *)imageFromSystemBarButton:(UIBarButtonSystemItem)systemItem :(UIColor *) color {
UIToolbar *bar = UIToolbar.new;
UIBarButtonItem *buttonItem = [UIBarButtonItem createWithItem:systemItem];
[bar setItems:@[buttonItem] animated:NO];
[bar snapshotViewAfterScreenUpdates:YES];
for (UIView *view in [(id) buttonItem view].subviews)
if ([view isKindOfClass:UIButton.class]) {
UIImage *image = [((UIButton *) view).imageView.image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
UIGraphicsBeginImageContextWithOptions(image.size, NO, image.scale);
[color set];
[image drawInRect:CGRectMake(0, 0, image.size.width, image.size.height)];
image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
return nil;
}
【讨论】:
基于@yycking answer,这里是 Xamarin.iOS C# 端口:
[DllImport (Constants.ObjectiveCLibrary, EntryPoint = "objc_msgSend")]
static extern IntPtr IntPtr_objc_msgSend (IntPtr receiver, IntPtr selector);
public static UIImage GetImage (UIBarButtonSystemItem systemItem)
{
var tempItem = new UIBarButtonItem (systemItem);
// Add to toolbar and render it
var bar = new UIToolbar ();
bar.SetItems (new [] { tempItem }, false);
bar.SnapshotView (true);
// Get image from real UIButton
var selHandle = Selector.GetHandle ("view");
var itemView = Runtime.GetNSObject<UIView> (IntPtr_objc_msgSend (tempItem.Handle, selHandle));
foreach (var view in itemView?.Subviews) {
if (view is UIButton button)
return button.ImageForState (UIControlState.Normal);
}
return null;
}
【讨论】:
斯威夫特 5
let delete = UIBarButtonItem(barButtonSystemItem: .trash, target: self, action: #selector(deleteSelected))
【讨论】: