Espacement négatif pour UIBarButtonItem dans la barre de navigation sur iOS 11

Dans iOS 10 et ci-dessous, il y avait un moyen d'append un spacer négatif au tableau de buttons dans la barre de navigation, comme ceci:

UIBarButtonItem *negativeSpacer = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil]; negativeSpacer.width = -8; self.navigationItem.leftBarButtonItems = @[negativeSpacer, [self backButtonItem]]; 

Cela ne fonctionne plus sur iOS 11 (l'espaceur devient positif, au lieu de négatif). J'ai inspecté la hiérarchie de vue de l'élément du button de la barre, et il est maintenant embedded dans _UIButtonBarStackView . Comment ajuster la position du button de la barre sur iOS 11?

J'ai trouvé une solution un peu hacky sur les forums de développeurs Apple: https://forums.developer.apple.com/thread/80075

Il semble que le problème vient de la façon dont iOS 11 gère les buttons UIBarButtonItem .fixedSpace et comment un UINavigationBar est mis en page dans iOS 11. Les barres de navigation utilisent maintenant autolayout et les marges de disposition pour mettre en forme les buttons. La solution présentée dans ce post (en bas) consistait à définir toutes les marges de layout à la valeur que vous voulez.

 class InsetButtonsNavigationBar: UINavigationBar { override func layoutSubviews() { super.layoutSubviews() for view in subviews { // Setting the layout margins to 0 lines the bar buttons items up at // the edges of the screen. You can set this to any number to change // the spacing. view.layoutMargins = .zero } } } 

Pour utiliser cette nouvelle barre de navigation avec un espacement de buttons personnalisé, vous devez mettre à jour l'location où vous créez les controllers de navigation avec le code suivant:

 let navController = UINavigationController(navigationBarClass: InsetButtonsNavigationBar.self, toolbarClass: UIToolbar.self) navController.viewControllers = [yourRootViewController] 

Basé sur la réponse de keithbhunter, j'ai créé un UINavigationBar personnalisé:

NavigationBarCustomMargins.h:

 #import <UIKit/UIKit.h> @interface NavigationBarCustomMargins : UINavigationBar @property (nonatomic) IBInspectable CGFloat leftMargin; @property (nonatomic) IBInspectable CGFloat rightMargin; @end 

NavigationBarCustomMargins.m:

 #import "NavigationBarCustomMargins.h" #define DefaultMargin 16 #define NegativeSpacerTag 87236223 @interface NavigationBarCustomMargins () @property (nonatomic) BOOL leftMarginIsSet; @property (nonatomic) BOOL rightMarginIsSet; @end @implementation NavigationBarCustomMargins @synthesize leftMargin = _leftMargin; @synthesize rightMargin = _rightMargin; - (void)layoutSubviews { [super layoutSubviews]; if (([[[UIDevice currentDevice] systemVersion] compare:@"11.0" options:NSNumericSearch] != NSOrderedAscending)) { BOOL isRTL = [UIApplication sharedApplication].userInterfaceLayoutDirection == UIUserInterfaceLayoutDirectionRightToLeft; for (UIView *view in self.subviews) { view.layoutMargins = UIEdgeInsetsMake(0, isRTL ? self.rightMargin : self.leftMargin, 0, isRTL ? self.leftMargin : self.rightMargin); } } else { //left NSMutableArray *leftItems = [self.topItem.leftBarButtonItems mutableCopy]; if (((UIBarButtonItem *)leftItems.firstObject).tag != NegativeSpacerTag) { UIBarButtonItem *negativeSpacer = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil]; negativeSpacer.tag = NegativeSpacerTag; negativeSpacer.width = self.leftMargin - DefaultMargin; [leftItems insertObject:negativeSpacer atIndex:0]; [self.topItem setLeftBarButtonItems:[leftItems copy] animated:NO]; } //right NSMutableArray *rightItems = [self.topItem.rightBarButtonItems mutableCopy]; if (((UIBarButtonItem *)rightItems.firstObject).tag != NegativeSpacerTag) { UIBarButtonItem *negativeSpacer = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil]; negativeSpacer.tag = NegativeSpacerTag; negativeSpacer.width = self.rightMargin - DefaultMargin; [rightItems insertObject:negativeSpacer atIndex:0]; [self.topItem setRightBarButtonItems:[rightItems copy] animated:NO]; } } } - (CGFloat)leftMargin { if (_leftMarginIsSet) { return _leftMargin; } return DefaultMargin; } - (CGFloat)rightMargin { if (_rightMarginIsSet) { return _rightMargin; } return DefaultMargin; } - (void)setLeftMargin:(CGFloat)leftMargin { _leftMargin = leftMargin; _leftMarginIsSet = YES; } - (void)setRightMargin:(CGFloat)rightMargin { _rightMargin = rightMargin; _rightMarginIsSet = YES; } @end 

Après cela, j'ai défini la class personnalisée à mon UINavigationController dans Interface Builder et juste définir les marges nécessaires: Screenshot 1

Fonctionne bien. Prend en charge RTL et iOS avant 11: Capture d'écran 2

Juste une solution de contournement pour mon cas, il pourrait être utile à certaines personnes. Je voudrais réaliser ceci:

entrez la description de l'image ici et auparavant, j'utilisais aussi le negativeSpacer. Maintenant j'ai compris cette solution:

  let logoImage = UIImage(named: "your_image") let logoImageView = UIImageView(image: logoImage) logoImageView.frame = CGRect(x: -16, y: 0, width: 150, height: 44) logoImageView.contentMode = .scaleAspectFit let logoView = UIView(frame: CGRect(x: 0, y: 0, width: 10, height: 44)) **logoView.clipsToBounds = false** logoView.addSubview(logoImageView) let logoItem = UIBarButtonItem(customView: logoView) navigationItem.leftBarButtonItem = logoItem 

Pour moi, cette réponse aide https://stackoverflow.com/a/44896832
En particulier j'ai mis à la fois imageEdgeInsets et titleEdgeInsets parce que mon button a l'image et le titre set