web-dev-qa-db-fra.com

Changer la position de UIBarButtonItem dans UINavigationBar

Comment puis-je changer la position d'un UIBarButtonItem dans un UINavigationBar? Je voudrais que mon bouton soit environ 5px plus élevé que sa position normale.

61
Ben Williams

Il n'y a pas de moyen particulièrement efficace de le faire. Votre meilleur choix si vous devez vraiment est de sous-classer UINavigationBar et de remplacer layoutSubviews pour appeler [super layoutSubviews], puis de rechercher et de repositionner la vue du bouton.

33
Anomie

Ce code crée un bouton de retour pour UINavigationBar avec un fond d’image et une position personnalisée. L'astuce consiste à créer une vue intermédiaire et à modifier ses limites.

UIButton *backBtn = [UIButton buttonWithType:UIButtonTypeCustom];
UIImage *backBtnImage = [UIImage imageNamed:@"btn-back"];
UIImage *backBtnImagePressed = [UIImage imageNamed:@"btn-back-pressed"];
[backBtn setBackgroundImage:backBtnImage forState:UIControlStateNormal];
[backBtn setBackgroundImage:backBtnImagePressed forState:UIControlStateHighlighted];
[backBtn addTarget:self action:@selector(goBack) forControlEvents:UIControlEventTouchUpInside];
backBtn.frame = CGRectMake(0, 0, 63, 33);
UIView *backButtonView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 63, 33)];
backButtonView.bounds = CGRectOffset(backButtonView.bounds, -14, -7);
[backButtonView addSubview:backBtn];
UIBarButtonItem *backButton = [[UIBarButtonItem alloc] initWithCustomView:backButtonView];
self.navigationItem.leftBarButtonItem = backButton;
117
Evgeny Shadchnev

J'ai résolu l'utilisation de la transformation et de la vue personnalisée:

(Rapide)

  // create the button
  let suggestImage  = UIImage(named: "tab-item-popcorn-on")!.imageWithRenderingMode(.AlwaysOriginal)
  let suggestButton = UIButton(frame: CGRectMake(0, 0, 40, 40))
  suggestButton.setBackgroundImage(suggestImage, forState: .Normal)
  suggestButton.addTarget(self, action: Selector("suggesMovie:"), forControlEvents:.TouchUpInside)

  // here where the magic happens, you can shift it where you like
  suggestButton.transform = CGAffineTransformMakeTranslation(10, 0)

  // add the button to a container, otherwise the transform will be ignored  
  let suggestButtonContainer = UIView(frame: suggestButton.frame)
  suggestButtonContainer.addSubview(suggestButton)
  let suggestButtonItem = UIBarButtonItem(customView: suggestButtonContainer)

  // add button shift to the side
  navigationItem.rightBarButtonItem = suggestButtonItem
35
Adriano Spadoni

Pour ceux d'entre vous développant pour iOS 5 qui ont trébuché sur cela et ont été découragés ... Essayez quelque chose comme ça:

float my_offset_plus_or_minus = 3.0f;

UIBarButtonItem * item = [[UIBarButtonItem alloc] initWithTitle:@"title" 
                                                          style:UIBarButtonItemStyleDone
                                                          target:someObject action:@selector(someMessage)];

[item setBackgroundVerticalPositionAdjustment:my_offset_plus_or_minus forBarMetrics:UIBarMetricsDefault];
17
Joe Marx

Le meilleur moyen consiste à sous-classer votre UINavigationBar, comme décrit ici: https://stackoverflow.com/a/17434530/1351190

Voici mon exemple:

#define NAVIGATION_BTN_MARGIN 5

@implementation NewNavigationBar

- (void)layoutSubviews {

    [super layoutSubviews];

    UINavigationItem *navigationItem = [self topItem];

    UIView *subview = [[navigationItem rightBarButtonItem] customView];

    if (subview) {

        CGRect subviewFrame = subview.frame;
        subviewFrame.Origin.x = self.frame.size.width - subview.frame.size.width - NAVIGATION_BTN_MARGIN;
        subviewFrame.Origin.y = (self.frame.size.height - subview.frame.size.height) / 2;

        [subview setFrame:subviewFrame];
    }

    subview = [[navigationItem leftBarButtonItem] customView];

    if (subview) {

        CGRect subviewFrame = subview.frame;
        subviewFrame.Origin.x = NAVIGATION_BTN_MARGIN;
        subviewFrame.Origin.y = (self.frame.size.height - subview.frame.size.height) / 2;

        [subview setFrame:subviewFrame];
    }
}

@end

J'espère que ça aide.

12
Simone Lai

Je devais régler mon bouton pour être plus vers la droite. Voici comment je l'ai fait en utilisant UIAppearance dans Swift. Il existe également une propriété de position verticale, alors j'imagine que vous pouvez vous ajuster dans n'importe quelle direction. 

UIBarButtonItem.appearance().setTitlePositionAdjustment(UIOffset.init(horizontal: 15, vertical: 0), forBarMetrics: UIBarMetrics.Default)

Cela me semble beaucoup moins invasif que de jouer directement avec le cadre ou d'ajouter des sous-vues personnalisées.

6
Mark Bridges

Essayez le code ci-dessous,

UIBarButtonItem *button = [[UIBarButtonItem alloc] initWithTitle:@"Logout" style:UIBarButtonItemStyleDone target:self action:nil];
[button setBackgroundVerticalPositionAdjustment:-20.0f forBarMetrics:UIBarMetricsDefault];
[[self navigationItem] setRightBarButtonItem:button];

Il est utilisé pour changer la position 'y' dans ce code. Modifiez la valeur 'y' (ici -20.0f) en fonction de vos besoins. Si la valeur est positive, la position du bouton sera abaissée. Si la valeur est négative, cela augmentera la position de votre bouton.

6
Augustine P A

Si vous utilisez simplement une image et non le chrome par défaut, vous pouvez utiliser des encarts d’image négatifs (définis dans l’inspecteur de taille) pour déplacer votre image dans l’UIBarButtonItem (pratique, car par défaut le remplissage horizontal peut l'image étant plus à l'intérieur que vous le souhaitez). Vous pouvez également utiliser les encarts d'image pour positionner l'image en dehors des limites de UIBarButtonItem. De plus, tout le voisinage du bouton situé à gauche est encliquetable. Vous n'avez donc pas à vous soucier de le positionner près d'un appuyez sur la cible. (du moins dans les limites du raisonnable.)

4
Christopher Swasey
  • Swift 3
  • hauteur de barre de navigation personnalisée
  • pas de titre de saut

Étape 1: Définissez la position du titre à l'aide de l'API d'apparence. Par exemple, dans didFinishLaunchingWithOptions de AppDelegate

UINavigationBar.appearance().setTitleVerticalPositionAdjustment(-7, for: .default)

Étape 2: Sous-classe UINavigationBar

class YourNavigationBar: UINavigationBar {

    let YOUR_NAV_BAR_HEIGHT = 60

    override func sizeThatFits(_ size: CGSize) -> CGSize {
        return CGSize(width: UIScreen.main.bounds.width, 
                     height: YOUR_NAV_BAR_HEIGHT)
    }

    override func layoutSubviews() {
        super.layoutSubviews()

        let navigationItem = self.topItem

        for subview in subviews {
            if  subview == navigationItem?.leftBarButtonItem?.customView ||
                subview == navigationItem?.rightBarButtonItem?.customView {
                subview.center = CGPoint(x: subview.center.x, y: YOUR_NAV_BAR_HEIGHT / 2)
            }
        }
    }
}
3
andriy_fedin

La meilleure solution que je puisse trouver consiste à initialiser un UIBarButtonItem avec une sous-vue comprenant un espace supplémentaire à gauche/à droite. Ainsi, vous n'aurez pas à vous préoccuper des sous-classes et de la disposition des autres éléments de la barre de navigation, tels que le titre.

Par exemple, pour déplacer un bouton de 14 points à gauche:

UIView *containerView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, image.size.width + 14, image.size.height)];
UIButton* button = [UIButton buttonWithType:UIButtonTypeCustom];
button.frame = CGRectMake(-14, 0, image.size.width, image.size.height);
[button setImage:image forState:UIControlStateNormal];
[button addTarget:target action:action forControlEvents:UIControlEventTouchUpInside];
[containerView addSubview:button];

UIButton* button2 = [UIButton buttonWithType:UIButtonTypeCustom];
button2.frame = CGRectMake(0, 0, image.size.width + 14, image.size.height);
[button2 addTarget:target action:action forControlEvents:UIControlEventTouchUpInside];
[containerView addSubview:button2];

UIBarButtonItem* item = [[[self alloc] initWithCustomView:containerView] autorelease];
3
Jorge Aguirre

Swift 3.1

let cancelBarButtonItem = UIBarButtonItem()
cancelBarButtonItem.setBackgroundVerticalPositionAdjustment(4, for: .default)
vc.navigationItem.setLeftBarButton(cancelBarButtonItem, animated: true)
2
Adam Smaka

Voici la solution d'Adriano utilisant Swift 3. C'était la seule solution qui fonctionnait pour moi et j'ai essayé plusieurs solutions.

  let suggestImage  = UIImage(named: "menu.png")!
    let suggestButton = UIButton(frame: CGRect(x:0, y:0, width:34, height:20))
    suggestButton.setBackgroundImage(suggestImage, for: .normal)
    suggestButton.addTarget(self, action: #selector(self.showPopover(sender:)), for:.touchUpInside)
    suggestButton.transform = CGAffineTransform(translationX: 0, y: -8)
    // add the button to a container, otherwise the transform will be ignored
    let suggestButtonContainer = UIView(frame: suggestButton.frame)
    suggestButtonContainer.addSubview(suggestButton)
    let suggestButtonItem = UIBarButtonItem(customView: suggestButtonContainer)
    // add button shift to the side
    navigationItem.leftBarButtonItem = suggestButtonItem
2
JP Aquino

Comme @Anomie l'a dit, nous devons sous-classe UINavigationBar, et remplacer layoutSubviews().

Cela placera tous les éléments de bouton de la barre de droite fermement attachés au côté droit de la barre de navigation (au lieu d'être légèrement ajustés à gauche par défaut):

class AdjustedNavigationBar: UINavigationBar {

    override func layoutSubviews() {
        super.layoutSubviews()

        if let rightItems = topItem?.rightBarButtonItems where rightItems.count > 1 {
            for i in 0..<rightItems.count {
                let barButtonItem = rightItems[i]
                if let customView = barButtonItem.customView {
                    let frame = customView.frame
                    customView.frame = CGRect(x: UIApplication.sharedApplication().windows.last!.bounds.size.width-CGFloat(i+1)*44, y: frame.Origin.y, width: frame.size.width, height: frame.size.height)
                }

            }
        }
    }
}

Le seul endroit où définir la propriété UINavigationBar de UINavigationController se trouve dans init (), comme suit:

let controllerVC = UINavigationController(navigationBarClass: AdjustedNavigationBar.self, toolbarClass: nil)
controllerVC.viewControllers = [UIViewController()]

La deuxième ligne définit le contrôleur de vue racine de UINavigationController .(nous ne pouvons pas le définir via init(rootViewController:)

2
Zoltán Matók

Dans mon cas

  1. changer le cadre de barbuttonItem pour personnaliser les espaces

  2. Ajouter, supprimer barButtonItems dynamiquement. 

  3. changer les couleurs de teinte par contentOffset.y de tableview

comme ça  enter image description here  enter image description here

 enter image description here  enter image description here

Si votre cible minimale est iOS 11, vous pouvez modifier les images barButton dans le viewDidLayoutSubviews.

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()

    // Change the navigationBar item frames
    if let customView = wishButton.customView?.superview {
        customView.transform = CGAffineTransform(translationX: 7.0, y: 0)
    }

    if let customView = gourmetCountButton.customView?.superview {
        customView.transform = CGAffineTransform(translationX: 9.0, y: 0)
    }
}

Mais cela ne fonctionne que sur iOS 11.

J'ai aussi essayé d'utiliser le fixedSpace. Mais cela ne fonctionnait pas dans plusieurs éléments navigationBarButton.

 let space = UIBarButtonItem(barButtonSystemItem: .fixedSpace, target: nil, action: nil)
    space.width = -10

Alors, j'ai changé la largeur de customView pour ajuster l'espace horizontal.

C'est l'un des cours de ma classe barButtonItem

final class DetailShareBarButtonItem: UIBarButtonItem {

// MARK: - Value
// MARK: Public
***// Change the width to adjust space***
let button = UIButton(frame: CGRect(x: 0, y: 0, width: 32.0, height: 30.0))

override var tintColor: UIColor? {
    didSet {
        button.tintColor = tintColor
    }
}

required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
    setButton()
}

required override init() {
    super.init()
    setButton()
}


// MARK: - Function
// MARK: Private
private func setButton() {
    // Button
    button.setImage( #imageLiteral(resourceName: "navibarIcShare02White").withRenderingMode(.alwaysTemplate), for: .normal)
    button.tintColor              = .white
    button.imageEdgeInsets        = UIEdgeInsetsMake(0, 1.0, 1.0, 0)
    button.imageView?.contentMode = .scaleAspectFill

    let containerView = UIView(frame: button.bounds)
    containerView.backgroundColor = .clear
    containerView.addSubview(button)
    customView = containerView
}
}

C'est le résultat. 

J'ai testé sur iOS 9 ~ 11, (Swift 4)

 enter image description here

2
Den

Init UIBarButtonItem avec vue personnalisée et surcharge layoutSubviews en vue personnalisée, comme ceci

-(void) layoutSubviews {
  [super layoutSubviews];
  CGRect frame = self.frame;
  CGFloat offsetY = 5;
  frame.Origin.y = (44 - frame.size.height) / 2 - offsetY;
  self.frame = frame;
}
1
alex1704

Vous pouvez toujours faire des ajustements en utilisant Inserts sur le bouton. Par exemple,

UIButton *toggleBtn =  [UIButton buttonWithType:UIButtonTypeCustom];
[toggleBtn setFrame:CGRectMake(0, 0, 20, 20)];
[toggleBtn addTarget:self action:@selector(toggleView) forControlEvents:UIControlEventTouchUpInside];

[toggleBtn setImageEdgeInsets:((IS_IPAD)? UIEdgeInsetsMake(0,-18, 0, 6) : UIEdgeInsetsMake(0, -3, 0, -3))];

UIBarButtonItem *toggleBtnItem = [[UIBarButtonItem alloc] initWithCustomView: toggleBtn];
self.navigationItem.rightBarButtonItems = [NSArray arrayWithObjects:searchBtnItem, toggleBtnItem, nil];

Ça marche pour moi.

1
Joe M

Barre de navigation utilisant la modification de la position de la barre gauche et des incrustations d'image

Swift 4

let leftBarButtonItem = UIBarButtonItem.init(image: UIImage(named:"ic_nav-bar_back.png"), landscapeImagePhone: nil, style: .plain, target: viewController, action: #selector(viewController.buttonClick(_:)))
            leftBarButtonItem.imageInsets = UIEdgeInsets(top: 0, left: -15, bottom: 0, right: 0)
            leftBarButtonItem.tintColor = UIColor(hex: 0xED6E19)
            viewController.navigationItem.setLeftBarButton(leftBarButtonItem, animated: true)
0
CSE 1994

La transformation affine peut faire ce dont vous avez besoin. Dans mon cas, le concepteur m'a donné l'icône de fermeture 16x16 et je veux créer une zone de prise 44x44. 

closeButton.transform = CGAffineTransform(translationX: 14, y: 0)
closeButton.snp.makeConstraints { make in
   make.size.equalTo(CGSize(width: 44, height: 44))
}
self.navigationItem.rightBarButtonItem = UIBarButtonItem(customView: closeButton)
0
vdugnist

Voici une solution de contournement simple et suffisante pour mes besoins. J'ai ajouté un bouton d'information sur le côté droit de la barre UINavigationBar, mais par défaut, il se trouve trop près du bord. En étendant la largeur du cadre, j'ai pu créer l'espacement supplémentaire nécessaire à droite.

 UIButton *info = [UIButton buttonWithType:UIButtonTypeInfoLight];
 CGRect frame = info.frame;
 frame.size.width += 20;
 info.frame = frame;
 myNavigationItem.rightBarButtonItem = [[[UIBarButtonItem alloc]initWithCustomView:info]autorelease];
0
anna

J'ai trouvé la solution à ce problème en ajustant les incrustations d'image du bouton personnalisé. J'avais l'exigence dans l'application d'augmenter la hauteur de la barre de navigation et après l'augmentation de la hauteur, le problème sans positionnement des images rightBarButtonItem et leftBarButtonItem.

Trouvez le code ci-dessous: -

UIImage *image = [[UIImage imageNamed:@"searchbar.png"];
UIButton* searchbutton = [UIButton buttonWithType:UIButtonTypeCustom];
[searchbutton addTarget:self action:@selector(searchBar:) forControlEvents:UIControlEventTouchUpInside]; 
searchbutton.frame = CGRectMake(0,0,22, 22);
[searchbutton setImage:image forState:UIControlStateNormal];
[searchbutton setImageEdgeInsets:UIEdgeInsetsMake(-50, 0,50, 0)];
// Make BarButton Item
 UIBarButtonItem *navItem = [[UIBarButtonItem alloc] initWithCustomView:searchbutton];
self.navigationItem.rightBarButtonItem = navItem;

J'espère que cela aide quelqu'un.

0
iGW