web-dev-qa-db-fra.com

Ajuster le cadre de navigationItem.titleView?

J'ai remarqué que la position de titleView dépend du titre de rightbarbutton. Comment puis-je définir le cadre de titleView pour qu'il soit au centre?

J'ai essayé de définir titleView.frame, mais en vain.

Voici le code:

UIButton *titleLabel = [UIButton buttonWithType:UIButtonTypeCustom];
[titleLabel setTitle:@"Right Eye" forState:UIControlStateNormal];
[titleLabel setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
[titleLabel setTitleColor:[UIColor blackColor] forState:UIControlStateHighlighted];
titleLabel.frame = CGRectMake(100, 0, 180, 44);
titleLabel.titleLabel.backgroundColor = [UIColor clearColor];
titleLabel.titleLabel.font = [UIFont boldSystemFontOfSize:20.0];
titleLabel.titleLabel.shadowColor = [UIColor colorWithWhite:0.0 alpha:0.3];
//  titleLabel.titleLabel.textAlignment = UITextAlignmentCenter; (this doesn't work either)
[titleLabel addTarget:self action:@selector(titleTap:) forControlEvents:UIControlEventTouchUpInside];
self.navigationItem.titleView = titleLabel;
self.navigationItem.titleView.frame = CGRectMake(100, 0, 180, 44); //(this doesn't work either)

alt text

27
Sidwyn Koh

À un moment donné entre viewWillAppear: & viewDidAppear: de votre contrôleur de vue, la barre de navigation repositionne votre titleView et le redimensionne si nécessaire. Cela peut finir par être déconcentré car la barre de navigation préfère ne pas redimensionner votre titleView pour s’adapter entre les boutons. Il semble essayer de centrer la vue des titres si elle est suffisamment petite, mais si ce n’est pas le cas, votre titre sera décentré, puis je pense qu’en dernier recours, il sera redimensionné. L'effet est que titleView occupe tout l'espace entre les deux boutons. Si son alignement textAlignment est centré, il sera centré dans that space mais pas au centre par rapport à tout l'écran. Vous pouvez le voir sur votre capture d'écran.

Une solution consiste à rendre votre titleView plus étroit afin que la barre de navigation puisse le centrer; essayez donc environ 160 au lieu de 180 lorsque vous définissez votre titleView.frame. Il est dommage que, lors de la création de la vue par programme comme vous le fassiez, il faut mettre une estimation de cette largeur dans le code. Ce qui serait bien, c’est un moyen de maximiser cet espace tout en restant centré.

Il est possible de commencer avec titleView comme étant la largeur de l'écran, puis après que la barre de navigation ait changé titleView.frame, mettez-le à nouveau à jour vous-même dans viewDidAppear: du contrôleur de vue. En utilisant les propriétés titleView.frame.Origin.x et size.width adaptées, ainsi que la largeur de l’écran, vous pouvez calculer la plus grande des marges gauche et droite, puis définir le paramètre Origin.x sur celui-ci, ainsi que size.width sur la largeur moins 2. Cela ne prend cependant effet que lorsque la vue poussée est animée et que le décalage est visible par la suite. Vous pouvez masquer le titre dans viewWillAppear: puis le masquer dans viewDidAppear: après l'avoir centré, de sorte qu'au lieu de glisser avec un titleView décentré qui est ensuite décalé, il se glissera sans titre qui par la suite.

Une meilleure possibilité consiste à créer votre titre. Affichez votre propre sous-classe de UIView (ou UILabel ou autre) et remplacez setFrame afin de se redimensionner pour rester centré dans son aperçu. Si jamais je finis par le faire moi-même, je le posterai ici. Ou peut-être que quelqu'un d'autre connaît un autre endroit pour modifier le cadre de titleView après sa mise à jour mais avant que la vue ne glisse sans une sous-classe de vue.

46
Pierre Houston

Après avoir configuré votre titleView ou titleLabel, appelez sizeToFit dessus, assurez-vous également que titleLabel.textAlignment = UITextAlignmentCenter. Il sera centré sur la largeur de la barre de navigation plutôt que sur l'espace entre le bouton Edge et le bord éloigné de la barre de navigation. 

17
Alfie Hanssen

Aucune des suggestions ici ne fonctionnait vraiment pour moi. Mon problème était que je réglais titleView alors que la barre de navigation était au milieu de sa transition - j'obtiendrais ce scintillement étrange où le titleView clignoterait vers la gauche, puis se retrouverait au centre.

J'ai fini par suivre l'idée de smallduck et par-dessus tout définir setFrame, c'était aussi simple que:

- (void)setFrame:(CGRect)frame {
    [super setFrame:CGRectMake(0, 0, self.superview.bounds.size.width, self.superview.bounds.size.height)];
}
12
Andrew Vilcsak

Vous pouvez essayer de définir initialement le cadre sur CGRectZero et d’appeler sizeToFit. Vous trouverez ci-dessous le code que j'ai utilisé pour changer le titre et m'assurer qu'il est toujours centré. J'ai eu un bouton de retour sur la gauche. 

UILabel *label = [[UILabel alloc] initWithFrame:CGRectZero];    
label.text = @"Title";
[label sizeToFit];
self.navigationItem.titleView = label;
6
Yunming Zhang

Ma solution consistait à redimensionner manuellement la barre de navigation dans viewDidAppear:

- (void)viewDidAppear
{
     [super viewDidAppear];
     [self.navigationController.navigationBar setFrame:CGRectMake(x, y, width, height)];
}

... vous pouvez ensuite repositionner la vue du titre. Je viens de remarquer que le problème principal était le fait que la barre de navigation avait également été redimensionnée. Je l'ai donc ramenée à sa taille normale. J'ai eu la taille normale en déboguant et en vérifiant sa taille dans viewDidLoad avant qu'il ne soit modifié ...

1
nburk
-(void) awakeFromNib
{
    UIButton *titleLabel = [UIButton buttonWithType:UIButtonTypeCustom];
    [titleLabel setTitle:@"Right Eye" forState:UIControlStateNormal];
    [titleLabel setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
    [titleLabel setTitleColor:[UIColor blackColor] forState:UIControlStateHighlighted];
    titleLabel.titleLabel.backgroundColor = [UIColor clearColor];
    titleLabel.titleLabel.font = [UIFont boldSystemFontOfSize:20.0];
    titleLabel.titleLabel.shadowColor = [UIColor colorWithWhite:0.0 alpha:0.3];
    [titleLabel addTarget:self action:@selector(titleTap:) forControlEvents:UIControlEventTouchUpInside];
    titleLabel.frame = CGRectMake(0, 0, 400, 20);
    self.navigationItem.titleView = titleLabel;

}
1
Muhammad Arshad

Remplacer -intrinsicContentSize dans votre vue personnalisée

- (CGSize )intrinsicContentSize { return CGSizeMake(180, 44); }

0
Dennis

Je mettais en place un titre à deux lignes semblable à quelque chose que WhatsApp fait (titre et sous-titre). 

J'ai résolu ce problème en sous-classant UINavigationController et en remplaçant viewDidLayoutSubviews()

Mon titleView ressemble à ce qui suit (dans une viewController):

let frame = self.navigationController?.navigationBar.bounds ?? CGRect.zero

// This is a container view for the two labels, laying them out vertically
let titleView = UIStackView(arrangedSubviews: [self._titleLabel, self._promptLabel])
titleView.axis = .vertical
titleView.alignment = .center
titleView.distribution = .fill
titleView.frame = frame // set the frame to the navbarFrame initially
titleView.spacing = 0

// The wrapper is necessary for laying out the stackView as the titleView
let wrapperView = UIView(frame: frame)
wrapperView.addSubview(titleView)

self.navigationItem.titleView = wrapperView

Mon viewDidLayoutSubviews():

guard let navigationItem = self.topViewController?.navigationItem,
      let titleView = navigationItem.titleView,
      let wrapperView = titleView.subviews[0] as? UIStackView else { return }

var width : CGFloat = 0

for view in wrapperView.arrangedSubviews {
    view.sizeToFit()
    width = max(width, view.frame.size.width)
}

titleView.frame.size.width = width
titleView.frame.size.height = self.navigationBar.frame.height
wrapperView.frame = titleView.bounds
wrapperView.center.x = self.navigationBar.convert(self.navigationBar.center, to: titleView).x
wrapperView.center.y = titleView.frame.height / 2

self.navigationBar.layoutIfNeeded()

Remarque: L'astuce consiste à disposer d'une vue d'ensemble autour de votre titleView, qui peut être mise en forme par la barre de navigation. Puis ajustez le cadre de votre titleView à ce que vous désirez.

0
mangerlahn

La seule façon que j'ai trouvée pour résoudre ce problème est de ne pas utiliser UINavigationItems . Insted utilise deux vues différentes sur le côté bouton eace du UINavigationBar comme suit:

-(UIView *)rightButtonViewSection {
    if (!_rightButtonViewSection) {

        _rightButtonViewSection = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 80, 45)];

    }
    return _rightButtonViewSection;

}

-(UIView *)leftButtonViewSection {
    if (!_leftButtonViewSection) {

        _leftButtonViewSection = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 80, 45)];

    }
    return _leftButtonViewSection;

}

et setButtons on it like so so:

 ButtonBase *searchButton = [[ButtonBase alloc] initWithFrame:CGRectMake(0, 0, 32, 30)];
    [searchButton setImage:[UIImage imageNamed:SEARCH_IMAGE] forState:UIControlStateNormal];
    SEL searchSelector = @selector(tradersSearchButtonPress);
    [searchButton addTarget:self action:searchSelector forControlEvents:UIControlEventTouchUpInside];

    self.notificationButton.view.frame = [self rightButtonFrame];
    searchButton.frame = [self rightSecoundButtonFrame];

    [self.rightButtonViewSection removeAllSubviews];
    [self.rightButtonViewSection addSubview:self.notificationButton.view];
    [self.rightButtonViewSection addSubview:searchButton];

    UIBarButtonItem *buttonItem  = [[UIBarButtonItem alloc] initWithCustomView:self.rightButtonViewSection];
    self.topViewController.navigationItem.rightBarButtonItem = buttonItem;

BoutonsFrames:

-(CGRect) rightButtonFrame {
    return CGRectMake(self.rightButtonViewSection.width - 32, 7, 32, 30);
}
-(CGRect) rightSecoundButtonFrame {
    return CGRectMake(self.rightButtonViewSection.width - 80, 7, 32, 30);
}

Alors le titre ne bougera pas! parce que les UIButtonsViews ont la même largeur.

0
MCMatan

Même problème ici. Implémentation du libellé de défilement automatique en tant que titleView. Pour que le long texte dans le titre de navigation puisse défiler automatiquement pour que l'utilisateur puisse tout voir . Tous ces problèmes avec des dimensions réelles inconnues de titleView empêchent le centrage du texte dans les cas les plus simples: quand le texte de l'étiquette est ajusté et qu'il n'est pas nécessaire de faire défiler à la propriété title de navigationItem).

0
HARDWARRIOR

Récemment, je suis confronté au même problème. J'ai une vue de titre complexe dont le contenu peut changer. Je souhaite donc pouvoir afficher le plus de contenu possible. Les éléments de mon bouton de barre gauche et de droite ont une largeur différente; grande largeur, la barre de navigation ne le centrera pas. Comme vous le savez peut-être lorsque l'élément du bouton de barre de gauche ou de droite est très long, il est impossible de centrer la vue du titre. Je souhaite donc le centrer autant que possible.

Une fois que nous avons défini une vue sur self.navigationItem.titleView, la barre de navigation gérera le cadre de la vue de titre. Il est difficile de la centrer directement. Je sous-classe donc une vue qui fonctionne comme la vue conteneur de la vue de titre réelle. Je règle sa largeur avec la largeur de l'écran, UINavigationBar avec définissez son cadre sur une valeur de propriété en respectant son élément de bouton de barre gauche/droite.

La vue du conteneur est MDLNavigationTitleContainer et la vue du titre réel est MDLMessagesNavigationTitleView, MDLMessagesNavigationTitleView est gérée par mise en page automatique, car sa super vue est MDLNavigationTitleContainer, chaque fois que sa largeur change, on appelle layoutSubviews. Le code dans layoutSubviews semble un peu bizarre, il est utilisé pour gérer l'animation Push/pop.

@interface MDLNavigationTitleContainer ()

@property (nonatomic) CGRect oldFrame;

@end

@implementation MDLNavigationTitleContainer

- (instancetype)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        UINib *nib = [UINib nibWithNibName:@"MDLMessagesNavigationTitleView" bundle:[NSBundle bundleForClass:[MDLMessagesNavigationTitleView class]]];
        self.titleView = [[nib instantiateWithOwner:nil options:nil] firstObject];
        self.titleView.translatesAutoresizingMaskIntoConstraints = NO;
        [self addSubview:self.titleView];
        NSLayoutConstraint *centerX = [NSLayoutConstraint constraintWithItem:self.titleView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeCenterX multiplier:1 constant:0];
        NSLayoutConstraint *centerY = [NSLayoutConstraint constraintWithItem:self.titleView attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeCenterY multiplier:1 constant:0];
        NSLayoutConstraint *width = [NSLayoutConstraint constraintWithItem:self.titleView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationLessThanOrEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:0];
        width.priority = 200;
        NSLayoutConstraint *width1 = [NSLayoutConstraint constraintWithItem:self.titleView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationLessThanOrEqual toItem:self attribute:NSLayoutAttributeWidth multiplier:1 constant:0];
        [self addConstraint:centerX];
        [self addConstraint:centerY];
        [self addConstraint:width];
        [self addConstraint:width1];
    }
    return self;
}

- (void)layoutSubviews {
    [super layoutSubviews];

    if (!self.superview) {
        return;
    }

    if (self.center.x > [UIScreen mainScreen].bounds.size.width) {
        self.titleView.frame = self.oldFrame;
        return;
    }

    CGFloat centerX = self.center.x;

    CGFloat superWidth = [UIScreen mainScreen].bounds.size.width;
    CGFloat superCenterX = superWidth/2;
    CGFloat offsetX = superCenterX - centerX;
    CGPoint center = self.titleView.center;

    if (CGRectGetMaxX(self.titleView.frame) + offsetX < self.bounds.size.width &&
        CGRectGetMinX(self.titleView.frame) + offsetX > 0) {
        center = CGPointMake(self.bounds.size.width/2 + offsetX, self.bounds.size.height/2);
    }
    CGSize size = self.titleView.bounds.size;
    if (size.width > self.bounds.size.width) {
        size.width = self.bounds.size.width;
    }

    self.titleView.frame = CGRectMake(center.x-size.width/2, center.y-size.height/2, size.width, size.height);
    self.oldFrame = self.titleView.frame;
}

Définir la vue du titre:

MDLNavigationTitleContainer *titleView = [[MDLNavigationTitleContainer alloc] initWithFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, 44)];
self.navigationItem.titleView = titleView;
0
KudoCC

J'ai un autre cas d'utilisation légèrement (décalage vertical de UIButton comme titleView sur iOS 7). Après un long moment, j’ai imaginé utiliser la propriété contentEdgeInsets de UIButton pour déplacer le contenu dans son cadre.

0
phikes