ios 在 UIButton 中使用 UIBarButtonItem 图标

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/21187885/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-30 23:09:44  来源:igfitidea点击:

Use UIBarButtonItem icon in UIButton

ioscocoa-touchuibuttonuibarbuttonitemstyle

提问by ZviBar

UIBarButtonItemhas multiple icons available. Is it possible to use the icon which appears after setting its identifier to 'trash':

UIBarButtonItem有多个图标可用。是否可以使用将其标识符设置为“垃圾”后出现的图标:

trash icon

垃圾桶图标

with an UIButton? There is no straighforward method to do that like setting the identifieror style.

UIButton? 没有像设置identifierstyle那样直接的方法来做到这一点。

采纳答案by yoeriboven

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.

从网络上的某个位置下载图像,将其添加到您的项目中,并将UIButton的图像设置为您刚刚下载的图像。

I did not find the same as Apple is using but I found this one. Simply change it's color in Pixelmator or Photoshop.

我没有找到与 Apple 使用的相同,但我找到了这个。只需在 Pixelmator 或 Photoshop 中更改它的颜色即可。

回答by yycking

NewiOS 13 support SF Symbolsnow
UIImage(systemName: "trash")

新的iOS 13现在支持SF Symbols
UIImage(systemName: "trash")



for swift 4.2 (call it on main thread)

对于 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()

UIBarButtonSystemItem.play.image()

For Objective-C:

对于 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;
}

回答by Islam Q.

Here's a solution that works with ANYSystem bar button item + it supports tintColor:

这是一个适用于任何系统栏按钮项+它支持的解决方案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.


PS 如果您知道更好的方法,请随时改进,谢谢。

回答by Peter Kreinz

Based on @yycking answer, i wrote an suitable Swift 4extension:

基于@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
    }
}

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

如果您希望无论上下文如何始终将图像视为模板,请将renderMode设置为.alwaysTemplate

 let actionImage = UIImage(systemItem: .action, renderingMode: .alwaysTemplate) 

回答by DKLA

All the iOS system icons can be extracted using a handy little app called (fittingly enough) iOS Artwork Extractor. I use it all the time when I want to mimic iOS system behaviors.

所有 iOS 系统图标都可以使用一个名为(非常合适)iOS Artwork Extractor 的方便的小应用程序提取。当我想模仿 iOS 系统行为时,我一直使用它。

Download the Xcode project at:

在以下位置下载 Xcode 项目:

https://github.com/0xced/iOS-Artwork-Extractor

https://github.com/0xced/iOS-Artwork-Extractor

回答by LaborEtArs

As already mentioned in the comments to @Islam Q's answer, the there presented solution might fail, if the UINavigationItemisn'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 UINavigationBarin these cases.

正如在对@Islam Q 的回答的评论中已经提到的那样,如果UINavigationItem当前未在屏幕上呈现,则所提出的解决方案可能会失败。例如,如果当前未加载视图控制器,则会失败。事实上,在这些情况下,问题似乎是UINavigationBar缺少布局。

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.

更“防弹”的版本是使用专门创建的 UINavigationBar 对象来获取系统项目图像。这也将使保存和恢复任何现有 UIBarButtonItems 过时。

I've packed this into a small helper class:

我已经把它打包成一个小的帮助类:

LEABarButtonSystemItemImage.h:

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

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) :-)

最后一个方法“barButtonImages”的实现只是为了好奇……在 UIBarButtonItem 标头中,成员 systemItem 被声明为使用 7 位(0..127)。目前,从 UIBarButtonSystemItemDone 到 UIBarButtonItemSystemItemPageCurl 只记录了 22 个值……事实上;我发现了一些索引高于 100 的未记录图像(在 6S+ 模拟器中在 iOS 9.3 上测试):-)

回答by eharo2

Using Quartz2D with Swift 4.2

在 Swift 4.2 中使用 Quartz2D

The solutions based in an extension to extract the image from the UIBarButtonSystemItemdon't work in iOS 11/12, so I decided to add a custom class to draw the icons without adding any .png

基于从扩展中提取图像的解决方案UIBarButtonSystemItem在 iOS 11/12 中不起作用,所以我决定添加一个自定义类来绘制图标而不添加任何 .png

enter image description here

在此处输入图片说明

This is implemented for .trash and .action that are the icons I need in my project. Feel free to add the rest.

这是为 .trash 和 .action 实现的,它们是我在项目中需要的图标。随意添加其余的。

Use it as follows (You need to conform to the SystemIConDelegateprotocol, set the Delegate property and add the required method):

使用方法如下(需要符合SystemIConDelegate协议,设置Delegate属性并添加需要的方法):

let trashIcon = SystemIcon(withType: .trash)
trashIcon.delegate = self
shareButton = UIButton()
shareButton.addSubview(trashIcon)

The SystemIconclass is here. It is optimized for 30 X 30 points:

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()
    }
}

回答by dalexsoto

Based on @yycking answer, here is the Xamarin.iOS C# port:

基于@yycking 的回答,这里是 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;
}

回答by Renetik

I was using it for imageView in table cell and somehow tint color did not work wherever I set it... So I ended up with this code where you set color for image.

我在表格单元格中将它用于 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;
}