web-dev-qa-db-fra.com

Comment ajuster l'espace entre deux UIBarButtonItem dans rightBarButtonItems

J'utilise les codes suivants pour ajouter deux boutons à self.navigationItem.rightBarButtonItems, et je pense que dans iOS7, l'espace entre deux boutons est trop large, existe-t-il un moyen de réduire l'espace entre ces deux boutons?

UIBarButtonItem *saveStyleButton = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"save.png"] style:UIBarButtonItemStyleBordered target:self action:@selector(saveStyle)];

UIBarButtonItem *shareStyleButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAction target:self action:@selector(shareStyle)];

NSArray *arr= [[NSArray alloc] initWithObjects:shareStyleButton,saveStyleButton,nil];

self.navigationItem.rightBarButtonItems=arr;

Appréciez n'importe quel indice ou idée. 

30
Bob

Mis à jour en juillet 2015

Une meilleure façon de faire est d’utiliser le storyboard (testé dans Xcode 6.4). Tout d'abord, ajoutez un UINavigationItem; deuxièmement, ajoutez un élément de bouton de barre; troisièmement, ajoutez une vue à l'élément de bouton de barre que vous venez de créer à l'étape 2; quatrièmement, ajoutez autant de boutons que vous le souhaitez dans la vue que vous venez de faire glisser; Enfin, ajustez l’espace avec votre souris et vos contraintes. 

Questions connexes

Impossible d'affecter plusieurs boutons à UINavigationItem lors de l'utilisation de Storyboard avec iOS 5

Comment ajouter des boutons au contrôleur de navigation visibles après le classement?


Old Answer (Acceptable uniquement pour les petits encarts) 

Utilisez la propriété imageInsets:

leftButton.imageInsets = UIEdgeInsetsMake(0.0, 0.0, 0, -15);
rightButton.imageInsets = UIEdgeInsetsMake(0.0, -15, 0, 0);

pour trois boutons ou plus, le ou les boutons du milieu reçoivent les deux incrustations:

leftButton.imageInsets = UIEdgeInsetsMake(0.0, 0.0, 0, -15);
middleButton.imageInsets = UIEdgeInsetsMake(0.0, -15, 0, -15);
rightButton.imageInsets = UIEdgeInsetsMake(0.0, -15, 0, 0);

Faites attention aux boutons de droite: le bouton PREMIER du tableau d’éléments est le bouton DROIT:

rightButton.imageInsets = UIEdgeInsetsMake(0.0, -15, 0, 0);
middleButton.imageInsets = UIEdgeInsetsMake(0.0, -15, 0, -15);
leftButton.imageInsets = UIEdgeInsetsMake(0.0, 0.0, 0, -15);

IMPORTANT: Séparez l’encart entre les deux voisins. si vous appliquez l'intégralité de l'encadré à un bord, il deviendra évident que les boutons se chevauchent dans l'espace "vide" - un bouton obtient toutes les touches "d'espace". Même en "divisant" le réglage comme celui-ci, à -40 sur les deux bords, le tap tapera parfois sur le mauvais bouton. -15 ou -20 est le plus à envisager avec cette technique.

En appliquant cette méthode, le bouton pourrait même être déplacé dans quatre directions.

47
Bob

Si vous souhaitez avoir 2 boutons en haut à droite, sans espace entre eux ou à droite, cela a fonctionné pour moi.

let imgLeft = UIImage(named: "buttonLeft")?.imageWithRenderingMode(.AlwaysOriginal)
let bLeft = UIBarButtonItem(image: imgLeft, style: UIBarButtonItemStyle.Done, target: self, action: "action1:")
let space = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.FixedSpace, target: nil, action: nil)
space.width = -16.0

bLeft.imageInsets = UIEdgeInsetsMake(0, 0, 0, -25.0)


let imgRight = UIImage(named: "buttonRight")?.imageWithRenderingMode(.AlwaysOriginal)
let bRight = UIBarButtonItem(image: imgRight, style: UIBarButtonItemStyle.Done, target: self, action: "action2:")

bRight.imageInsets = UIEdgeInsetsMake(0, -25, 0, 0)


self.navigationItem.rightBarButtonItems = [space,bLeft,bRight ]
4
abinop

Ma situation était de donner un espace horizontal pour logOut Button sur le bord droit. 

 func addLogOutButtonToNavigationBar(triggerToMethodName: String)
    {
        let button: UIButton = UIButton()
        button.setImage(UIImage(named: "logOff.png"), forState: .Normal)
        button.frame = CGRectMake(20, 0, 30, 25)
        button.contentEdgeInsets = UIEdgeInsets.init(top: 0, left: 10, bottom: 0, right: -10)

        button .addTarget(self, action:Selector(triggerToMethodName), forControlEvents: UIControlEvents.TouchUpInside)
        let rightItem:UIBarButtonItem = UIBarButtonItem()
        rightItem.customView = button
        self.navigationItem.rightBarButtonItem = rightItem
    }
4
A.G

Ma solution utilise un affichage personnalisé pour les boutons de la barre de droite. Créez un stackview horizontal avec un espacement égal et ajoutez autant de boutons que vous le souhaitez en tant que sous-vue. 

Exemple de code:

func addRightBarButtonItems()
{
    let btnSearch = UIButton.init(type: .custom)
    btnSearch.setImage(UIImage(named: "icon-search"), for: .normal)
    btnSearch.addTarget(self, action: #selector(MyPageContainerViewController.searchButtonPressed), for: .touchUpInside)

    let btnEdit = UIButton.init(type: .custom)
    btnEdit.setImage(UIImage(named: "icon-edit"), for: .normal)
    btnEdit.addTarget(self, action: #selector(MyPageContainerViewController.editButtonPressed), for: .touchUpInside)

    let stackview = UIStackView.init(arrangedSubviews: [btnEdit, btnSearch])
    stackview.distribution = .equalSpacing
    stackview.axis = .horizontal
    stackview.alignment = .center
    stackview.spacing = 8

    let rightBarButton = UIBarButtonItem(customView: stackview)
    self.navigationItem.rightBarButtonItem = rightBarButton
}
3
Ali Seymen

Créez une UIBarButtonItem avec le type flexible ou fixe. Définissez la largeur et ajoutez-la au tableau des éléments barbutton. Essayez d’utiliser une largeur négative pour voir si cela fonctionne.

Ou, vous pourriez peut-être ajuster votre image. Je pense que les boutons du système ont une taille fixe et peuvent inclure une partie transparente. Ainsi, même quand ils sont emballés, ils semblent toujours espacés.

2
Joride

Sans code. Je viens de mettre un autre UIBarButtonItem entre les boutons qui ont besoin d'espacement dans le storyboard. Le bouton est simplement un espace réservé pour l'espacement et UIBarButton doit avoir UIView comme sous-vue de UIBarButtonItem. ajustez la largeur de la vue pour votre espacement. Voir Captures d'écran.

 enter image description here

 enter image description here

 enter image description here

2
iOSCodeJunkie

Pour ce faire dans le code sans ajouter de vue de conteneur supplémentaire, utilisez une UIBarButtonItem avec le type d'élément système défini sur FixedSpace. Définissez ensuite la largeur de l’espace fixe sur -10 et placez-le entre les deux boutons.

1
Alex311

Premier:

Pour UIBarButtonItem, vous devez utiliser le constructeur init(customView: UIView)

Seconde:

Utilisez fixedSpace pour définir l'espace entre les boutons

exemple:

let firstButton = UIButton()
let firstButtonItem = UIBarButtonItem(customView: firstButton)

let secondButton = UIButton()
let secondButtonItem = UIBarButtonItem(customView: secondButton)

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

self.navigationItem.rightBarButtonItems = [firstButtonItem, space, secondButtonItem]
1
ober

autre réponse: Cela fonctionne sous ios 9 - 12 . Vous devriez appeler fixNavigationItemsMargin (margin :) dans la fonction viewDidAppear (_ animated: Bool) et viewDidLayoutSubviews () .fixNavigationItemsMargin (margin :) modifierait la pile UINavigationController.

vous pouvez appeler fixNavigationItemsMargin (margin :) dans BaseNavigationController, effectuez le travail commun. Et appelez fixNavigationItemsMargin (margin :) dans UIViewController pour une mise en page précise.

// do common initilizer
class BaseNavigationController: UINavigationController {
    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        fixNavigationItemsMargin()
    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        fixNavigationItemsMargin()
    }

}

extension UINavigationController {
func fixNavigationItemsMargin(_ margin: CGFloat = 8) {
    let systemMajorVersion = ProcessInfo.processInfo.operatingSystemVersion.majorVersion
    if systemMajorVersion >= 11 {
        // iOS >= 11
        guard let contentView = navigationBar.subviews
            .first(
                where: { sub in
                    String(describing: sub).contains("ContentView")
            }) else { return }

        // refer to: https://www.matrixprojects.net/p/uibarbuttonitem-ios11/
        // if rightBarButtonItems has not any custom views, then margin would be 8(320|375)/12(414)
        // should use customView
        let needAdjustRightItems: Bool
        if let currentVC = viewControllers.last,
            let rightItems = currentVC.navigationItem.rightBarButtonItems,
            rightItems.count > 0,
            rightItems.filter({ $0.customView != nil }).count > 0 {
            needAdjustRightItems = true
        } else {
            print("Use 8(320|375)/12(414), if need precious margin ,use UIBarButtonItem(customView:)!!!")
            needAdjustRightItems = false
        }

        let needAdjustLeftItems: Bool
        if let currentVC = viewControllers.last,
            let leftItems = currentVC.navigationItem.leftBarButtonItems,
            leftItems.count > 0,
            leftItems.filter({ $0.customView != nil }).count > 0 {
            needAdjustLeftItems = true
        } else {
            print("Use 8(320|375)/12(414), if need precious margin ,use UIBarButtonItem(customView:)!!!")
            needAdjustLeftItems = false
        }

        let layoutMargins: UIEdgeInsets
        if #available(iOS 11.0, *) {
            let directionInsets = contentView.directionalLayoutMargins
            layoutMargins = UIEdgeInsets(
                top: directionInsets.top,
                left: directionInsets.leading,
                bottom: directionInsets.bottom,
                right: directionInsets.trailing)
        } else {
            layoutMargins = contentView.layoutMargins
        }

        contentView.constraints.forEach(
            { cst in

                // iOS 11 the distance between rightest item and NavigationBar should be  margin
                // rightStackView trailing space is -margin / 2
                // rightestItem trailing to rightStackView trailing is -margin / 2
                let rightConstant = -margin / 2

                switch (cst.firstAttribute, cst.secondAttribute) {
                case (.leading, .leading), (.trailing, .trailing):

                    if let stackView = cst.firstItem as? UIStackView,
                        stackView.frame.minX < navigationBar.frame.midX {
                        // is leftItems
                        if needAdjustLeftItems {
                            cst.constant = margin - layoutMargins.left
                        }
                    } else if let layoutGuide = cst.firstItem as? UILayoutGuide,
                        layoutGuide.layoutFrame.minX < navigationBar.frame.midX {
                        // is leftItems
                        if needAdjustLeftItems {
                            cst.constant = margin - layoutMargins.left
                        }
                    }

                    if let stackView = cst.firstItem as? UIStackView,
                        stackView.frame.maxX > navigationBar.frame.midX {
                        // is rightItems
                        if needAdjustRightItems {
                            cst.constant = rightConstant
                        }
                    } else if let layoutGuide = cst.firstItem as? UILayoutGuide,
                        layoutGuide.layoutFrame.maxX > navigationBar.frame.midX {
                        // is rightItems
                        if needAdjustRightItems {
                            cst.constant = rightConstant
                        }
                    }

                default: break
                }

        })

        // ensure items space == 8, minispcae
        contentView.subviews.forEach(
            { subsub in
                guard subsub is UIStackView else { return }
                subsub.constraints.forEach(
                    { cst in
                        guard cst.firstAttribute == .width
                            || cst.secondAttribute == .width
                            else { return }
                        cst.constant = 0
                })
        })

    } else {
        // iOS < 11

        let versionItemsCount: Int
        if systemMajorVersion == 10 {
            // iOS 10 navigationItem.rightBarButtonItems == 0
            // space = 16(320|375) / 20(414)
            // should adjust margin
            versionItemsCount = 0
        } else {
            // iOS 9 navigationItem.rightBarButtonItems == 0
            // space = 8(320|375) / 12(414)
            // should not adjust margin
            versionItemsCount = 1
        }

        let spaceProducer = { () -> UIBarButtonItem in
            let spaceItem = UIBarButtonItem(
                barButtonSystemItem: .fixedSpace,
                target: nil,
                action: nil)
            spaceItem.width = margin - 16
            return spaceItem
        }
        if let currentVC = viewControllers.last,
            var rightItems = currentVC.navigationItem.rightBarButtonItems,
            rightItems.count > versionItemsCount,
            let first = rightItems.first {

            // ensure the first BarButtonItem is NOT fixedSpace
            if first.title == nil && first.image == nil && first.customView == nil {
                print("rightBarButtonItems SPACE SETTED!!!  SPACE: ", abs(first.width))

            } else {
                rightItems.insert(spaceProducer(), at: 0)

                // arranged right -> left
                currentVC.navigationItem.rightBarButtonItems = rightItems
            }
        }

        if let currentVC = viewControllers.last,
            var leftItems = currentVC.navigationItem.leftBarButtonItems,
            leftItems.count > versionItemsCount,
            let first = leftItems.first {
            if first.title == nil && first.image == nil && first.customView == nil {
                print("leftBarButtonItems  SPACE SETTED!!!  SPACE: ", abs(first.width))
            } else {
                leftItems.insert(spaceProducer(), at: 0)

                // arranged left -> right
                currentVC.navigationItem.leftBarButtonItems = leftItems
            }
        }
    }
}
}

// do precise layout
class ViewController: UIViewController {
  override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    navigationController?.fixNavigationItemsMargin(40)
}

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    navigationController?.fixNavigationItemsMargin(40)
}
1
Jules

Trouvé une idée folle qui fonctionne.

func createCustomToolbar(items: [UIBarButtonItem]) -> UIToolbar
{
    // no spacing between bar buttons
    let customToolbar = UIToolbar(frame: CGRect(x: 0, y: 0, width: items.count*45, height: 44))
    customToolbar.items = items
    customToolbar.barStyle = UIBarStyle(rawValue: -1)!
    customToolbar.clearsContextBeforeDrawing = false
    customToolbar.backgroundColor = UIColor.clearColor()
    customToolbar.tintColor = UIColor.clearColor()
    customToolbar.translucent = true
    return customToolbar
}


let customToolbar = createCustomToolbar([item0,item1,item2,item3])
navigationItem.rightBarButtonItems = [UIBarButtonItem(customView: customToolbar)]

Testé sur iOS7 et supérieur. Même si cela est écrit en Swift, le concept est clair.

0
Prcela

J'ai abandonné la lutte contre ce bogue et j'ai proposé l'extension suivante:

import UIKit

extension UIBarButtonItem {

    convenience init(buttonImage: UIImage?, target: Any?, action: Selector?) {
        let button = UIButton(type: .system)
        button.frame = CGRect(Origin: CGPoint.zero, size: buttonImage != nil ? buttonImage!.size : CGSize.zero)
        button.setImage(buttonImage, for: .normal)

        if let action = action {
            button.addTarget(target, action: action, for: .touchUpInside)
        }

        self.init(customView: button)
    }

    public func updateButton(image: UIImage?) {
        if let button = self.customView as? UIButton {
            button.setImage(image, for: .normal)

            let size = image != nil ? image!.size : CGSize.zero
            let frame = button.frame
            button.frame = frame.insetBy(dx: ceil((frame.size.width - size.width) / 2), dy: ceil((frame.size.height - size.height) / 2))
        }
    }
}
0
bteapot