web-dev-qa-db-fra.com

Utiliser l'icône UIBarButtonItem dans UIButton

UIBarButtonItem a plusieurs icônes disponibles. Est-il possible d'utiliser l'icône qui apparaît après avoir défini son identifiant sur 'corbeille':

trash icon

avec un UIButton? Il n’existe pas de méthode simple pour le faire, comme définir l’identificateur identifiant ou style

30
ZviBar

Téléchargez l'image quelque part sur le Web, ajoutez-la à votre projet et définissez l'image UIButton sur celle que vous venez de télécharger.

Je n'ai pas trouvé le même produit qu'Apple mais j'ai trouvé celui-ci . Changez simplement sa couleur dans Pixelmator ou Photoshop.

8
yoeriboven

Voici une solution qui fonctionne avecANYélément de bouton de la barre système + il supporte 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. N'hésitez pas à vous améliorer si vous connaissez une meilleure solution, merci.

14
Islam Q.

Sur la base de la réponse @yycking, j’ai écrit une extension Swift 4 appropriée:

//  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
    }
}

Exemple avec le bouton d'action (coloration par défaut):

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)

Si vous voulez que votre image soit toujours traitée comme un modèle, peu importe du contexte, définissez renderingMode sur .alwaysTemplate

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

Comme déjà mentionné dans les commentaires sur la réponse de @Islam Q, la solution présentée peut échouer si le UINavigationItem n'est pas affiché à l'écran. Il échoue, par exemple, si le contrôleur de vue n'est pas actuellement chargé. En fait, le problème semble être la structure manquante de UINavigationBar dans ces cas.

Une version plus «résistante aux balles» consisterait à utiliser un objet UINavigationBar spécialement créé pour obtenir les images d'élément système. Cela rendrait également l'enregistrement et la restauration de tout UIBarButtonItems existant obsolète.

J'ai mis cela dans une petite classe d'aide:

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

En tant qu'add-on/fallback, la méthode renvoie une image personnalisée, si aucune image d'élément système n'a pu être récupérée. Bien entendu, ces images personnalisées doivent être présentes dans l'ensemble d'applications.

La dernière méthode 'barButtonImages' a été implémentée juste par curiosité ... dans l'en-tête UIBarButtonItem, le membre systemItem est déclaré comme utilisant 7 bits (0..127). Actuellement, seules 22 valeurs sont documentées de UIBarButtonSystemItemDone à UIBarButtonItemSystemItemPageCurl ... et en fait; J'ai trouvé des images non documentées commençant par des index supérieurs à 100 (testées sur iOS 9.3 dans le simulateur 6S +) :-)

4
LaborEtArs

Toutes les icônes du système iOS peuvent être extraites à l'aide d'une petite application très pratique appelée iOS Artwork Extractor. Je l'utilise tout le temps lorsque je veux imiter les comportements du système iOS.

Téléchargez le projet Xcode sur: 

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

3
DKLA

Utilisation de Quartz2D avec Swift 4.2

Les solutions basées sur une extension permettant d'extraire l'image de la UIBarButtonSystemItem ne fonctionnent pas dans iOS 11/12, alors j'ai décidé d'ajouter une classe personnalisée pour dessiner les icônes sans ajouter de fichier .png.

 enter image description here

Ceci est implémenté pour .trash et .action, qui sont les icônes dont j'ai besoin dans mon projet. N'hésitez pas à ajouter le reste.

Utilisez-le comme suit (vous devez vous conformer au protocole SystemIConDelegate, définir la propriété Delegate et ajouter la méthode requise):

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

La classe SystemIcon est ici. Il est optimisé pour 30 X 30 points:

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

Je l'utilisais pour imageView dans une cellule de tableau et la teinte couleur ne fonctionnait pas où que je sois configurée ... Je me suis donc retrouvé avec ce code dans lequel vous définissez la couleur de l'image.

+ (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;
}
0
Renetik

Basé sur @yycking answer , voici le port 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;
}
0
dalexsoto