web-dev-qa-db-fra.com

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

Dans iOS 10 et les versions antérieures, il existait un moyen d'ajouter une entretoise négative au tableau de boutons 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 de bouton de barre et il est maintenant intégré dans _UIButtonBarStackView. Comment ajuster la position du bouton de barre sur iOS 11?

24
pckill

MODIFIER:

Cela peut ne plus fonctionner à partir d'iOS 13. Vous pouvez obtenir l'erreur:

Erreur du client lors de la tentative de modification des marges de mise en page d'une vue privée

ANCIENNE RÉPONSE:

J'ai trouvé une solution quelque peu hacky sur les Apple: https://forums.developer.Apple.com/thread/80075

Il semble que le problème vient de la façon dont iOS 11 gère le UIBarButtonItem.fixedSpace boutons et la disposition d'un UINavigationBar dans iOS 11. Les barres de navigation utilisent désormais la mise en page automatique et les marges de disposition pour disposer les boutons. La solution présentée dans cet article (en bas) était de définir toutes les marges de mise en page à la valeur souhaitée.

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 bouton personnalisé, vous devrez mettre à jour où vous créez les contrôleurs de navigation avec le code suivant:

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

Juste une solution de contournement pour mon cas, cela pourrait être utile à certaines personnes. Je voudrais y parvenir:

enter image description here 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
2
Ke MA

Sur la base de 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 une classe personnalisée pour mon UINavigationController dans Interface Builder et je viens de définir les marges nécessaires: Capture d'écran 1

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

1
Alexander Korotkov