barra de navegação rightbaritem imagem-botão bug iOS 11

101

Este código funciona bem no ios10. recebo minha etiqueta e um botão de imagem que é o perfil da foto do usuário, redondo .. ok. mas ao executar o simulador ios11 do xcode 9, consigo esticá-lo. o quadro do botão deve ser 32x32, ao verificar o sim e obter a visualização e dizer ao xcode para descrever a visualização, recebo a saída como 170x32 ou algo assim.

aqui está meu código.

let labelbutton = UIButton( type: .system)
    labelbutton.addTarget(self, action:#selector(self.toLogin(_:)), for: .touchUpInside)
    labelbutton.setTitleColor(UIColor.white, for: .normal)
    labelbutton.contentHorizontalAlignment = .right
    labelbutton.titleLabel?.font = UIFont.systemFont(ofSize: 18.00)



    let button = UIButton(type: .custom)
     button.addTarget(self, action:#selector(self.toLogin(_:)), for: .touchUpInside)
     button.frame = CGRect(x: 0, y: 0, width: 32, height: 32)
     button.setTitleColor(UIColor.white, for: .normal)
     button.setTitleColor(UIColor.white, for: .highlighted)


    var buttomItem : UIBarButtonItem = UIBarButtonItem()
    buttomItem.customView = button
    buttomItem.target = self
    buttomItem.action = "ToLogin"

    var labelItem : UIBarButtonItem = UIBarButtonItem()
    labelItem.customView = labelbutton
    labelItem.target = self
    labelItem.action = "ToLogin"


    if let user = PFUser.current() {
        print("LOGIN : checkiando si existe usuario ")
            labelbutton.setTitle(USERNAME, for: UIControlState.normal)
            labelbutton.sizeToFit()

        if(user["profile_photo_url"] != nil) {
            print(" ENCONTRO PROFILE PHOTO URL NOT NIL Y ES \(user["profile_photo_url"])")
            let photoURL = user["profile_photo_url"] as! String
            let a = LoginService.sharedInstance
            a.downloadImage(url: photoURL, complete: { (complete) in

                if (complete) {

                    button.setImage(LoginService.sharedInstance.profile_photo! , for: UIControlState.normal)

                    button.layer.cornerRadius = 0.5 * button.bounds.size.width
                   // button.imageView!.contentMode = .scaleAspectFit
                   // button.imageView!.frame = CGRect(x: 0, y: 0, width: 40, height: 40)
                    //button.imageView!.contentMode = .scaleAspectFit
                    //button.imageView!.clipsToBounds = true
                    //button.imageView!.layer.cornerRadius = 60
                    button.clipsToBounds = true
                    self.NavigationItem.rightBarButtonItems = [buttomItem,labelItem]
                }


            })
        } else {
                self.NavigationItem.rightBarButtonItem = labelItem

        }
            print(" EL FRAME DEL BUTTON ES \(button.frame)")

    } else {

        labelbutton.setTitle("Login", for: UIControlState.normal)
        labelbutton.sizeToFit()
        self.NavigationItem.rightBarButtonItem = labelItem

    }

insira a descrição da imagem aqui

Lorenzo Gonzalez
fonte
Você usou o modo de exibição de pilha na barra de navegação?
Vlad Khambir
@ V.Khambir Nop ...: /
lorenzo gonzalez
este relatório de bug está em algum lugar?
Edu
iOS 11 usa AutoLayout para itens de navegação de layout. Caso você precise mudar o UIButtoninterior UIBarButtonItem, usebutton.imageEdgeInsets = UIEdgeInsets(top: 0, left: 20, bottom: 0, right: -20)
onmyway133

Respostas:

188

Razão

O problema aparece porque o ios 11 UIBarButtonItemusa autolayout em vez de lidar com frames.

Solução

Você deve adicionar uma restrição de largura para este botão de imagem se usar o Xcode 9.

 button.widthAnchor.constraint(equalToConstant: 32.0).isActive = true
 button.heightAnchor.constraint(equalToConstant: 32.0).isActive = true

PS

buttonnão está UIBarButtonItem, está UIButtondentro UIBarButtonItem. Você deve definir a restrição não para UIBarButtonItem, mas para os elementos dentro dela.

Vlad Khambir
fonte
3
@ V.Khambir agora estou entendendo esse problema. Mas com esta mesma solução com o xcode 9, e se eu executar no dispositivo ios 1, tudo bem ... mas se eu executar no dispositivo da versão 10 do ios ... as imagens do botão da minha barra não estão aparecendo. Como posso corrigir para todas as versões para isso. Eu verifiquei no dispositivo de ambos ios 11, ios 10. Apenas mostrando boa na versão ios 1. em ios 10, imagem não é exibida
david,
@spikesa, ele deve funcionar nas duas versões do iOS, provavelmente você configurou restrições incorretas.
Vlad Khambir,
@ V.Khambir O valor do tipo 'UIBarButtonItem' não tem nenhum membro 'widthAnchor' no Xcode 9 dentro de um condicional # disponível (iOS 11.0, *)?
Ryan Brodie
1
@jped você encontrou uma solução para isso? Não funciona tão bem para mim no iOS 10 com Xcode 9
swalkner
1
Ótimo. Tenho que amar essas mudanças significativas. A Apple documentou uma lista desses tipos de mudanças em uma página ou notas de versão em algum lugar?
GONeale
53

Obrigado a todos por contribuir! vocês estão certos !. para xcode9 ios11 você precisa colocar uma restrição.

 let widthConstraint = button.widthAnchor.constraint(equalToConstant: 32)
 let heightConstraint = button.heightAnchor.constraint(equalToConstant: 32)
 heightConstraint.isActive = true
 widthConstraint.isActive = true
Lorenzo Gonzalez
fonte
1
Isso funcionou para mim também, mas alguém pode explicar por que isso é necessário para o iOS 11 quando nunca foi antes?
stonedauwg
9
UINavigationBar agora faz o layout de suas subvisualizações usando Layout automático
mangerlahn
2
Não cometa o mesmo erro que eu: desliguei translatesAutoresizingMaskIntoConstraintse depois descobri que estava totalmente quebrado no iOS 9 (mas parecia bem no iOS 10 e 11)
xaphod
2
Value of type 'UIBarButtonItem' has no member 'widthAnchor'no Xcode 9 dentro de um if #available(iOS 11.0, *)condicional?
Ryan Brodie
@ lorenzo-gonzalez Como você fez isso? Eu estou preso nisso.
Alessandro Lucarini
18

O código Objective C está obsoleto agora. Mas para o usuário que precisa construir / manter projetos Objective C no iOS 11, a tradução a seguir de Swift (resposta de Karoly Nyisztor) para Objective C é útil.

//  UIView+Navbar.h

#import <UIKit/UIKit.h>

@interface UIView (Navbar)

- (void)applyNavBarConstraints:(CGFloat)width height:(CGFloat)height;

@end

//----------

//  UIView+Navbar.m

#import "UIView+Navbar.h"

@implementation UIView (Navbar)

- (void)applyNavBarConstraints:(CGFloat)width height:(CGFloat)height
{
    if (width == 0 || height == 0) {
        return;
    }

    NSLayoutConstraint *heightConstraint = [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:height];
    NSLayoutConstraint *widthConstraint = [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:width];
    [heightConstraint setActive:TRUE];
    [widthConstraint setActive:TRUE];
}

//----------

// Usage :-
[button applyNavBarConstraints:33 height:33];
SHS
fonte
Legal. Na verdade, eu precisava da versão Objective-C também. Aliás, o Xcode 9 continua redimensionando aleatoriamente os itens da barra de navegação no storyboard. Tenho que verificar duas vezes antes de fazer minhas alterações. Espero que isso seja corrigido em breve.
Karoly Nyisztor
você é meu herói, porque preciso refatorar o projeto antigo com o objetivo-c.
Marosdee Uma
18

Bem, o novo barButtonItemusa autolayout em vez de lidar com frames.

A imagem que você estava adicionando ao botão é maior do que o tamanho do botão em si. É por isso que o próprio botão foi esticado para o tamanho da imagem. Você deve redimensionar a imagem para corresponder ao tamanho do botão necessário, antes de adicioná-la ao botão.

Ahmad Farrag
fonte
14

Eu escrevi uma pequena extensão para definir as restrições nos itens da barra de navegação:

import UIKit

extension UIView {
    func applyNavBarConstraints(size: (width: CGFloat, height: CGFloat)) {
    let widthConstraint = self.widthAnchor.constraint(equalToConstant: size.width)
    let heightConstraint = self.heightAnchor.constraint(equalToConstant: size.height)
    heightConstraint.isActive = true
    widthConstraint.isActive = true
  }
}

// Usage
button.applyNavBarConstraints(size: (width: 33, height: 33))
Karoly Nyisztor
fonte
Boa ideia! Aplicado este UIButtonfunciona muito bem.
Alessandro Ornano
12

Eu fiz isso objetivamente usando as seguintes linhas:

NSLayoutConstraint * widthConstraint = [customButton.widthAnchor constraintEqualToConstant:40];
NSLayoutConstraint * HeightConstraint =[customButton.heightAnchor constraintEqualToConstant:40];
[widthConstraint setActive:YES];
[HeightConstraint setActive:YES];

UIBarButtonItem* customBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:customButton];
self.navigationItem.leftBarButtonItem = customBarButtonItem;

Obrigado, feliz codificação !!

Aleem
fonte
7

O que eu fiz?

No meu aplicativo, adicionei a imagem do perfil na NavigationBar no item rightBarButton. antes do iOS 11, ele funcionava bem e era exibido corretamente, mas quando atualizado para o iOS 11, mudava o comportamento como um golpe

insira a descrição da imagem aqui

Então adicionei UIViewo item do botão direito e defini UIButtoncomo subvisualização de UIView? Como abaixo,

insira a descrição da imagem aqui

E eu defini as restrições de altura e largura de UIButton.

insira a descrição da imagem aqui insira a descrição da imagem aqui

E meu problema está resolvido. Não se esqueça de definir UIViewa cor de fundo de como uma cor clara .

NOTA: Se o botão não funcionar, verifique se a UIView'saltura pode ser 0, aqui você deve alterar a altura 0 para 44 ou o que quiser. E também clipToBound = true, agora você pode definir a posição do botão e funcionará bem.

iPatel
fonte
Isso funciona, no entanto, o botão para de funcionar quando você o adiciona a uma visualização. Alguma dica?
eonist
@GitSync Verifique se a altura da sua visualização será 0, mude para 44 ou o que quiser e faça clipToBound = true e defina o botão.
iPatel
1
Uau. trabalho! Dica profissional: use recursos .pdf em vez de .png
eonist
a visualização do contêiner é necessária? Não posso simplesmente usar o UIButton diretamente?
igrek
5

Alterando o widthAnchor/heightAnchor funcionará apenas em dispositivos iOS 11+. Para dispositivos iOS 10, você precisa seguir o caminho clássico de alterar manualmente os quadros. O fato é que nenhuma das duas abordagens funciona para as duas versões, então você absolutamente precisa alternar programaticamente dependendo da versão do tempo de execução, como abaixo:

if #available(iOS 11.0, *)
{
   button.widthAnchor.constraint(equalToConstant: 32.0).isActive = true
   button.heightAnchor.constraint(equalToConstant: 32.0).isActive = true
}else
{
   var frame = button.frame
   frame.size.width = 32.0
   frame.size.height = 32.0
   button.frame = frame
}
Malloc
fonte
1
Obrigado pela dica do iOS 10! Eu tinha a correção do iOS 11 funcionando, mas não consegui descobrir para o iOS 10.
Clifton Labrum
3

Mesmo que o iOS 11 use o Autolayout para a barra de navegação, é possível fazê-lo funcionar tradicionalmente configurando frames. Aqui está meu código funcionando para ios11 e ios10 ou mais antigo:

func barItemWithView(view: UIView, rect: CGRect) -> UIBarButtonItem {
    let container = UIView(frame: rect)
    container.addSubview(view)
    view.frame = rect
    return UIBarButtonItem(customView: container)
}

e aqui está como o item da barra é composto:

    let btn = UIButton()
    btn.setImage(image.withRenderingMode(.alwaysTemplate), for: .normal)
    btn.tintColor = tint
    btn.imageView?.contentMode = .scaleAspectFit
    let barItem = barItemWithView(view: btn, rect: CGRect(x: 0, y: 0, width: 22, height: 22))
    return barItem
Leszek Zarna
fonte
3

Colocar restrições de maneira programática funcionou para mim para usuários que executam iOS 11.X. No entanto, o botão da barra ainda estava esticado para usuários que executam iOS 10.X.Acho que os revisores da AppStore estavam executando o iOS 11.X, portanto, não puderam identificar meu problema, então meu aplicativo ficou pronto para venda e foi carregado.

Minha solução foi simplesmente mudar as dimensões da minha imagem para 30x30 em outro software (a dimensão da imagem anterior era 120x120).

Erik Nguyen
fonte
1
Como a barra de navegação no ios10 está com redimensionamento automático e o ios11 é autolayout, você precisa controlar se # disponível (iOS 11, *) {}
Paulo Sigales
Eu estava passando pelas novas mudanças e encontrei esta pergunta, mas acho que o desenvolvedor deve usar esta resposta
Shobhakar Tiwari
Ótima resposta simples. Obrigado. Nada mais estava funcionando e isso estava me deixando louco.
Bretanha
0

Também tive sucesso implementando intrinsicContentSizepara retornar um tamanho apropriado para qualquer subclasse UIView personalizada que pretendo usar como um customView.

Chris
fonte
0

Eu criei um item de botão da barra e o adicionei à barra de navegação.

    private var addItem: UIBarButtonItem = {
        let addImage = UIImage(named: "add")
        let addButton = UIButton(type: UIButton.ButtonType.custom)
        addButton.setBackgroundImage(addImage, for: UIControl.State())
        addButton.frame = CGRect(x: 0, y: 0, width: (addImage?.size.width)!, height: (addImage?.size.height)!)
        let addItem = UIBarButtonItem(customView: addButton)
        return addItem
    }()

 private var contactsItem: UIBarButtonItem = {
        let contactsImage = UIImage(named: "contacts")
        let contactsButton = UIButton(type: UIButton.ButtonType.custom)
        contactsButton.setBackgroundImage(contactsImage, for: UIControl.State())
        contactsButton.frame = CGRect(x: 0, y: 0, width: (contactsImage?.size.width)!, height: (contactsImage?.size.height)!)
        let contactsItem = UIBarButtonItem(customView: contactsButton)
        return contactsItem
    }()

Em viewDidLoad ()

let spacerBarButtonItem = UIBarButtonItem(barButtonSystemItem: UIBarButtonItem.SystemItem.fixedSpace, target: nil, action: nil)
        spacerBarButtonItem.width = 11
        navigationItem.rightBarButtonItems = [addItem, spacerBarButtonItem, contactsItem]

Aqui estou tendo a imagem de 28x28.

vinny
fonte