UIBarButtonItem avec une vue personnalisée mal alignée sur iOS 7 lorsqu’elle est utilisée comme éléments de barre de navigation gauche ou droite

Le code suivant fonctionne via iOS 6:

UIButton *myButton = nil; myButton = [UIButton buttonWithType:UIButtonTypeCustom]; myButton.bounds = CGRectMake(0,0,44,30); // setup myButton's images, etc. UIBarButtonItem *item = nil; item = [[UIBarButtonItem alloc] initWithCustomView:customButton]; 

Voici comment le bouton est censé être aligné:

Positionnement normal

Cependant, sur iOS 7, le bouton semble être décalé de la droite ou de la gauche par trop de pixels:

Positionnement incorrect sur iOS 7

Comment puis-je aligner correctement les boutons de barre personnalisés?

    Fonctionne jusqu’à iOS11!

    Vous pouvez utiliser des espaces flexibles négatifs et la propriété rightBarButtonItems au lieu de rightBarButtonItem:

     UIBarButtonItem *spacer = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil]; spacer.width = -10; // for example shift right bar button to the right self.navigationItem.rightBarButtonItems = @[spacer, yourBarButton]; 

    Pour corriger ce bogue, vous devez sous-classer UIButton afin de pouvoir substituer alignmentRectInsets . D’après mes tests, vous devrez retourner un UIEdgeInsets avec un décalage droit positif ou un décalage gauche positif, en fonction de la position du bouton. Ces chiffres n’ont aucun sens pour moi (au moins l’un d’entre eux devrait être négatif, selon le bon sens), mais c’est ce qui fonctionne réellement:

     - (UIEdgeInsets)alignmentRectInsets { UIEdgeInsets insets; if (IF_ITS_A_LEFT_BUTTON) { insets = UIEdgeInsetsMake(0, 9.0f, 0, 0); } else { // IF_ITS_A_RIGHT_BUTTON insets = UIEdgeInsetsMake(0, 0, 0, 9.0f); } return insets; } 

    Un merci spécial à @zev pour avoir suggéré que j’essaie d’ajuster alignmentRectInsets.

    J’ai essayé toutes les réponses ci-dessus et rien n’a fonctionné pour moi. Et voici ce qui fonctionne, si quelqu’un a besoin de ceci:

    (Pas de sous-classement nécessaire)

     // Add your barButtonItem with custom image as the following UIBarButtonItem *barButton = [[UIBarButtonItem alloc] initWithTitle:@"" style:UIBarButtonItemStyleBordered target:self action:@selector(categoryButtonPressed)]; // set your custom image [barButton setImage:categoryImage]; // finally do the magic barButton.imageInsets = UIEdgeInsetsMake(0.0, -20, 0, 0); 

    -Prendre un coup d’oeil au résultat.

    Notez l’espace sur le bouton gauche et sur le bouton droit (le bouton droit a le comportement par défaut)

    entrer la description de l'image ici

    Ok, je suis allé l’autre “direction”. J’ai fait en sorte que tout soit correctement aligné via Storyboard avec iOS 7 (en supposant que cela continuera à fonctionner). Et puis en utilisant l’approche de la sous-classe décrite, j’ai sous-classe UIButton avec l’implémentation suivante.

     - (UIEdgeInsets)alignmentRectInsets { UIEdgeInsets insets; if (SYSTEM_VERSION_LESS_THAN(@"7.0")) { if ([self isLeftButton]) { insets = UIEdgeInsetsMake(0, -10, 0, 0); } else { insets = UIEdgeInsetsMake(0, 0, 0, -10); } } else { insets = UIEdgeInsetsZero; } return insets; } - (BOOL)isLeftButton { return self.frame.origin.x < (self.superview.frame.size.width / 2); } 

    Donc, ce code ne fonctionne que si l'appareil est pré-iOS 7.

    Merci pour la perspicacité @jaredsinclair!

    @jaredsinclair

    Voici un coup d’oeil à mon code.

     // Create the tweetly button that will show settings self.tweetlyDisplay = [NavButton buttonWithType:UIButtonTypeCustom]; [self.tweetlyDisplay setFrame:CGRectMake(0, 0, 90, 44)]; [self.tweetlyDisplay setBackgroundColor:[UIColor clearColor]]; [self.tweetlyDisplay setBackgroundImage:[UIImage imageNamed:@"settings.png"] forState:UIControlStateNormal]; [self.tweetlyDisplay setAdjustsImageWhenHighlighted:NO]; [self.tweetlyDisplay addTarget:self action:@selector(tweetlyPressed:) forControlEvents:UIControlEventTouchUpInside]; // Add the Tweetly button as the left bar button item // This had a glitch that moves the image to the right somewhat UIBarButtonItem *leftBarButton = [[UIBarButtonItem alloc] initWithCustomView:self.tweetlyDisplay]; self.navigationItem.leftBarButtonItem = leftBarButton; 

    Voir tout ce qui ne va pas?

    Voici le résultat. La deuxième image n’est pas si visible car j’ai dû prendre la capture d’écran et elle est toujours en transition, mais vous pouvez voir clairement comment elle est incorrectement décalée.

    Bonne image normale:

    C'est la bonne image avant de naviguer

    Bad Offset Image:

    Vous pouvez voir le décalage maintenant à nouveau avant de se mettre en place

    Après environ une demi-seconde, l’image retourne à l’emplacement de l’image d’origine.

    Voici mon code pour le NavButton.h et .m:

     /********************************************** NavButton.h **********************************************/ #import  @interface NavButton : UIButton @end /********************************************** NavButton.m **********************************************/ #import "NavButton.h" @implementation NavButton { int imageHeight; } - (id)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { // Initialization code imageHeight = 44; } return self; } /* // Only override drawRect: if you perform custom drawing. // An empty implementation adversely affects performance during animation. - (void)drawRect:(CGRect)rect { // Drawing code } */ - (UIEdgeInsets)alignmentRectInsets { UIEdgeInsets insets; if ([self isLeftButton]) { insets = UIEdgeInsetsMake(0, 9.0f, 0, 0); } else { // IF ITS A RIGHT BUTTON insets = UIEdgeInsetsMake(0, 0, 0, 9.0f); } return insets; } - (BOOL)isLeftButton { return self.frame.origin.x < (self.superview.frame.size.width / 2); } // THIS IS THE TRICK. We make the height of the background rect match the image. -(CGRect)backgroundRectForBounds:(CGRect)bounds { CGRect bgRect = bounds; bgRect.origin.y = (bounds.size.height - imageHeight)/2.0f; bgRect.size.height = imageHeight; return bgRect; } @end 

    Une meilleure solution sans sauter de boutons est disponible ici:

    Alignement de UIBarButtonItem personnalisé avec iOS7

    Pas un grand fan de la sous-classification de UIButton ou de la méthode de la réponse de Marius: https://stackoverflow.com/a/19317105/287403

    Je viens d’utiliser un simple wrapper et j’ai déplacé le x du cadre du bouton dans une direction négative jusqu’à ce que je trouve le bon positionnement. Le tapotement des boutons semble aussi bien fonctionner (bien que vous puissiez étendre la largeur du bouton pour correspondre au décalage négatif si vous en aviez besoin).

    Voici le code que j’utilise pour générer un nouveau bouton retour:

     - (UIBarButtonItem *) newBackButton { // a macro for the weak strong dance @weakify(self); UIButton *backButton = [[UIButton alloc] init]; [backButton setTitle:@"Back" forState:UIControlStateNormal]; backButton.titleLabel.font = [UIFont systemFontOfSize:17]; CGFloat width = [@"Back" boundingRectWithSize:CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX) options:NSSsortingngDrawingUsesFontLeading|NSSsortingngDrawingUsesLineFragmentOrigin atsortingbutes:@{NSFontAtsortingbuteName: [UIFont systemFontOfSize:17]} context:nil].size.width + 27; backButton.frame = CGRectMake(-17.5, 0, width + 17.5, 44); [backButton setImage:[UIImage imageNamed:@"UINavigationBarBackIndicatorDefault"] forState:UIControlStateNormal]; [backButton setTitleEdgeInsets:UIEdgeInsetsMake(0, 14, 0, 0)]; [backButton setTitleColor:mTCOrangeColor forState:UIControlStateNormal]; backButton.contentEdgeInsets = UIEdgeInsetsZero; backButton.imageEdgeInsets = UIEdgeInsetsZero; [backButton addEventHandler:^(id sender) { // a macro for the weak strong dance @strongify(self); // do stuff here } forControlEvents:UIControlEventTouchUpInside]; UIView *buttonWrapper = [[UIView alloc] initWithFrame:CGRectMake(0, 0, width, 44)]; [buttonWrapper addSubview:backButton]; return [[UIBarButtonItem alloc] initWithCustomView:buttonWrapper]; } 

    Je voudrais append à @jaredsinclair la réponse confirmée aux méthodes de remplacement ci-dessous pour ceux qui ont du texte et / ou une image dans leur bouton de navigation, sinon le texte et l’image ne seront pas alignés (seulement l’image d’arrière-plan):

     - (UIEdgeInsets) titleEdgeInsets { if(SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"7.0")) { if (IF_ITS_A_LEFT_BUTTON) { { return UIEdgeInsetsMake(0, 0, 0, 9.0f); } else { // IF_ITS_A_RIGHT_BUTTON return UIEdgeInsetsMake(0, 9.0f, 0, 0); } } return [super titleEdgeInsets]; } - (UIEdgeInsets)imageEdgeInsets { if(SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"7.0")) { if (IF_ITS_A_LEFT_BUTTON) { return UIEdgeInsetsMake(0, 0, 0, 9.0f); } else { // IF_ITS_A_RIGHT_BUTTON return UIEdgeInsetsMake(0, 9.0f, 0, 0); } } return [super imageEdgeInsets]; } 

    ps la macro est:

     #define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedAscending) 

    * Solution trouvée, lisez à la fin de la réponse. *

    @jaredsinclair

    J’ai un cas similaire à Kyle Begeman

    C’est le bouton

      - (id)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { self.contentHorizontalAlignment = UIControlContentHorizontalAlignmentCenter; } return self; } - (UIEdgeInsets)alignmentRectInsets { UIEdgeInsets insets; if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"7.0")){ if ([self isLeftButton]) { insets = UIEdgeInsetsMake(0, 9.0f, 0, 0); } else { insets = UIEdgeInsetsMake(0, 0, 0, 9.0f); } }else{ insets = UIEdgeInsetsZero; } return insets; } - (BOOL)isLeftButton { return self.frame.origin.x < (self.superview.frame.size.width / 2); } 

    et je l'utilise dans ce cas

     PBNavigationBarButton *dashboardButton = [PBNavigationBarButton buttonWithType:UIButtonTypeCustom]; UIImage *dashboardImage = [UIImage imageNamed:@"btn_dashboard.png"]; UIImage *dashboardImageHighlighted = [UIImage imageNamed:@"btn_dashboard_pressed.png"]; NSInteger halfImageWidth = ceilf([dashboardImage size].width/2.0f); [dashboardButton setBackgroundImage:[dashboardImage stretchableImageWithLeftCapWidth:halfImageWidth topCapHeight:0] forState:UIControlStateNormal]; [dashboardButton setBackgroundImage:[dashboardImageHighlighted stretchableImageWithLeftCapWidth:halfImageWidth topCapHeight:0] forState:UIControlStateHighlighted]; [dashboardButton addTarget:target action:action forControlEvents:UIControlEventTouchUpInside]; [dashboardButton sizeToFit]; UIBarButtonItem *barButtonItem = [[UIBarButtonItem alloc] initWithCustomView:dashboardButton]; 

    Tout est beau, sauf que lorsque je reviens au VC précédent, le bouton se repositionne. Comme un petit saut. Pour moi, ça saute tout de suite pas après une seconde. Et cela arrive après que je vais faire le popVC de NavigationViewController.

    Edit: La réponse ci-dessous, la méthode de swizzling de Marius m'a aussi aidé.

    Je suis venu avec une version raccourcie de l’approche de Jaredsinclair:

     - (UIEdgeInsets)alignmentRectInsets { return [self isLeftButton] ? UIEdgeInsetsMake(0, 9.0f, 0, 0) : UIEdgeInsetsMake(0, 0, 0, 9.0f); } - (BOOL)isLeftButton { return self.frame.origin.x < (self.superview.frame.size.width / 2); } 

    Fonctionne comme un charme.

    dans ios7, vous pouvez simplement append un élément de bouton factice pour fixer l’espace de gauche.

    Par exemple, vous devez append ceci après avoir défini vos éléments d’origine ou dans viewdidload si vous définissez des boutons à l’aide du storyboard.

     NSMutableArray *buttons = [[NSMutableArray alloc] init]; UIBarButtonItem *spacerItem = [[UIBarButtonItem alloc] init]; [buttons addObject:spacerItem]; for(UIBarButtonItem *item in self.leftBarButtonItems){ [buttons addObject:item]; } [self setLeftBarButtonItems:[NSArray arrayWithArray:buttons] animated:NO]; 

    Modifiez les éléments suivants pour votre UIButton

    Contrôle -> Alignement -> Horizontal pour aligner à droite

    Il suffit simplement d’ajuster imageEdgeInsets pour corriger ce bogue.

    essaye ça.

      UIButton *actionBtn = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 40, 40)]; [actionBtn setImage:[UIImage imageNamed:@"arrow.png"] forState:UIControlStateNormal]; [actionBtn addTarget:self action:@selector(goToSetting) forControlEvents:UIControlEventTouchUpInside]; actionBtn.imageEdgeInsets = UIEdgeInsetsMake(0, 9, 0, -9); UIBarButtonItem *profileBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:actionBtn]; self.topViewController.navigationItem.rightBarButtonItems = @[profileBarButtonItem]; 

    Testé sous iOS 11

      public lazy var navigationBackBarButton : UIBarButtonItem = { let backBarButtonIcon =  let backBarButton = UIBarButtonItem(image: backBarButtonIcon, style: .plain, target: self, action: #selector(self.backBarButtonTouched(_:))) backBarButton.imageInsets = UIEdgeInsets(top: 0.0, left: -9.0, bottom: 0.0, right: 0.0) return backBarButton }() 

    2018, iOS 11+, Swift 4.x, cela a fonctionné pour moi.

    Combiner les meilleures réponses:

    Propriété

     internal lazy var button_Favorite: UIButton = { let button = UIButton(type: .custom) button.setImage(.favNormal, for: .normal) button.contentHorizontalAlignment = .right button.imageEdgeInsets = UIEdgeInsetsMake(0, 0, 0, -9.0) return button }() 

    Dans viewDidLoad :

      let barButton = UIBarButtonItem(customView: self.button_Favorite) self.button_Favorite.frame = CGRect(x: 0, y: 0, width: 40.0, height: 44.0) let negativeSpacer = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.fixedSpace, target: nil, action: nil) negativeSpacer.width = -10.0 self.navigationItem.rightBarButtonItems = [negativeSpacer, barButton]