UIBarButtonItem: ¿la acción de destino no funciona?

80

Tengo una vista personalizada dentro de UIBarButtonItem, configurada llamando -initWithCustomView. El elemento del botón de la barra se visualiza bien, pero cuando lo toco, no invoca la acción en mi objeto de destino.

Aquí está mi código:

UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"someImage.png"]];
UIBarButtonItem *bbItem = [[UIBarButtonItem alloc] initWithCustomView:imageView];
self.navigationItem.leftBarButtonItem = bbItem;
[imageView release];
[bbItem setTarget:self];
[bbItem setAction:@selector(deselectAll)];
Jacob Relkin
fuente

Respuestas:

126

No creo que el objetivo y la acción de UIBarButtonItem se apliquen a las vistas personalizadas. Intente usar un UIButton en lugar de UIImageView y aplique el objetivo y la acción al botón.

Código de muestra en Swift:

let button  = UIButton(type: .Custom)
if let image = UIImage(named:"icon-menu.png") {
    button.setImage(image, forState: .Normal)
}
button.frame = CGRectMake(0.0, 0.0, 30.0, 30.0)
button.addTarget(self, action: #selector(MyClass.myMethod), forControlEvents: .TouchUpInside)
let barButton = UIBarButtonItem(customView: button)
navigationItem.leftBarButtonItem = barButton
hacia adelante
fuente
31
Gracias. UIBarButtonItemhereda UIBarItemy, NSObjectpor lo tanto, no sabe nada sobre toques. Sería bueno si los documentos mencionaran que las propiedades actiony targetsolo se aplican si la vista personalizada es un UIButton.
Jason Moore
18
@JasonMoore: mejor aún, sería bueno si los botones actuaran como ... botones. Si se comportó de la forma que espera un programador, no hay necesidad de documentación adicional.
GeneralMike
Muchas gracias. Además, podemos usar referencias de IBOutlet para setTarget y setAction en el código. Pero si creamos UIBarButtonItem en código, no podemos setTarget y setAction, no funcionará.
MOBYTELAB
1
Aunque esta es una publicación antigua, quería compartirla de todos modos. A medida que me cansaba de tener que prestar atención a los objetos UIBarButtonItem que tenían vistas personalizadas o no, escribí una clase de extensión que en realidad se comporta de la manera (creo) que debería. Puede encontrar la fuente aquí: gist.github.com/tvervest/8708465
Thomas Vervest
je, usé targetForAction en lugar de target.
Mathijs Segers
24

Tuve el mismo problema, pero era reacio a usar una vista en UIButtonlugar de una personalizada para mi UIBarButtonItem(según la respuesta de Drawonward).

Alternativamente, puede agregar un UIGestureRecognizera la vista personalizada antes de usarlo para inicializar UIBarButtonItem; esto parece funcionar en mi proyecto.

Así es como modificaría su código original:

UIImageView *SOCImageView = [[UIImageView alloc] initWithImage:
                             [UIImage imageNamed:@"cancel_wide.png"]];

UITapGestureRecognizer *tapGesture = 
       [[UITapGestureRecognizer alloc] initWithTarget:self 
                                               action:@selector(deselectAll:)];
[SOCImageView addGestureRecognizer:tapGesture];

SOItem.leftBarButtonItem = 
       [[[UIBarButtonItem alloc] initWithCustomView:SOCImageView] autorelease];
[tapGesture release];
[SOCImageView release];
Tim Arnold
fuente
Nada funcionó para mí, solo esta solución. La vista personalizada no permite configurar la acción. El botón UIB simple al mismo tiempo tiene problemas para mostrar la imagen de fondo. Con este reconocimiento de gestos finalmente funcionó.
Denis
agregar el tapgesture a mi vista personalizada no funcionó, pero funcionó cuando lo agregué al elemento del botón de la barra.
Ronaldoh1
@ Ronaldoh1 ¿Cómo agregaste UITapgesture a UIBarButtonItem?
tsuz
@ user013948 como lo haría con cualquier vista. UIBarButtonItem subclases UIView (si no me equivoco).
Ronaldoh1
24

Así es como lo hago funcionar:

UIButton* infoButton = [UIButton buttonWithType: UIButtonTypeInfoLight];
[infoButton addTarget:self action:@selector(displayAboutUs) forControlEvents:UIControlEventTouchDown];

UIBarButtonItem* itemAboutUs =[[UIBarButtonItem alloc]initWithCustomView:infoButton];
…
Nicolás Lauquin
fuente
9

Así es como implementé el UIButton dentro del UIBarButtonItem:

UIButton *logoButton = [UIButton buttonWithType:UIButtonTypeCustom];
[logoButton setImage: [UIImage imageNamed:@"icon.png"] forState:UIControlStateNormal];
logoButton.frame = CGRectMake(0, 0, 30, 30);
[logoButton addTarget:self action:@selector(showAbout:) forControlEvents:UIControlEventTouchUpInside];
UIBarButtonItem *barItem = [[UIBarButtonItem alloc] initWithCustomView:logoButton];
self.navigationItem.rightBarButtonItem = barItem;
[barItem release];
Zac Witte
fuente
Esta solución es correcta. También podemos usar UIButton para agregar otra vista.
Brave
7

Tuve un problema similar. E inicialmente seguí la ruta sugerida por @drawnonward, pero luego tuve problemas cuando intenté que mi acción presentara un controlador popover en un iPad: usar un UIButton integrado como una vista personalizada significa que UIButton es el remitente del evento, y El método presentPopoverFromBarButtonItem: del controlador popover se bloquea cuando intenta enviarle mensajes que solo son apropiados para UIBarButtonItems reales.

La solución que encontré finalmente fue robar la imagen que quería usar (el icono de "información") de un UIButton desechable y construir mi UIBarButtonItem de la siguiente manera:

// Make the info button use the standard icon and hook it up to work
UIButton *infoButton = [UIButton buttonWithType:UIButtonTypeInfoLight];
UIBarButtonItem *barButton = [[[UIBarButtonItem alloc]        
      initWithImage:infoButton.currentImage
              style:UIBarButtonItemStyleBordered
             target:self
             action:@selector(showInfo:)] autorelease];

El uso de este inicializador produce un botón de barra cuyo objetivo y selector realmente funcionan. También es más fácil que envolver la imagen en una vista personalizada, pero eso es solo glaseado.

James Elliott
fuente
1
¡Esto me resolvió! Para usar con un popover, use esto: [myPopover presentPopoverFromBarButtonItem: remitente permitidoArrowDirections: UIPopoverArrowDirectionAny animado: YES];
mpemburn
2

Si no desea conformarse con un UIImage y tiene una vista personalizada en su elemento barbutton, configure un reconocedor de gestos de toque en la propiedad de vista personalizada de su elemento barbutton

let tap = UITapGestureRecognizer(target: self, action: #selector(goProButtonPressed)) deviceStatusBarButtonItem.customView?.addGestureRecognizer(tap)

León
fuente
1

Jacob, todo se ve bien, sin embargo, es posible que no hayas proporcionado el selector correcto.

¿Puedes verificar que tu acción esté realmente declarada?

- (void) deselectAll;

y no

- (void) deselectAll:(id)sender;

Si es el último, deberá configurar la acción en @selector(deselectAll:) . (observe el punto y coma para que coincida con la declaración del método)

Además, puede ser nulo IBAction, pero eso no es relevante para este problema que está teniendo.

ohhorob
fuente
@ohhorob, estoy seguro de la firma del método.
Jacob Relkin
@ohhorob, tampoco estoy usando IB;)
Jacob Relkin
también verifique que la instancia que está configurando como el objetivo no se libera ni se desasigna. ¿Es una subclase de controlador de navegación u otro controlador de vista en la jerarquía de navegación?
ohhorob
@ohhorob, este código está dentro de un controlador de vista que no se publica.
Jacob Relkin
¿Cómo confirmó deselectAllque no está siendo llamado? Al establecer un punto de interrupción del depurador, ¿o simplemente no ve los resultados esperados?
ohhorob
1

Swift 3. Tuve un problema similar en el que tenía una vista personalizada dentro del elemento del botón de la barra. Para que el grifo funcione, asigné un gesto a la vista y configuré la acción. La vista tenía que ser una salida para asignarle. Al hacer esto, funcionó con la vista personalizada.

Establecer el gesto como variable de clase

@IBOutlet weak var incidentView: UIView!
let incidentTap = UITapGestureRecognizer()

En viewDidLoad

incidentTap.addTarget(self, action: #selector(self.changeIncidentBarItem))
incidentView.addGestureRecognizer(incidentTap)
Micah Montoya
fuente
1

Swift 3 : A continuación se muestra mi implementación completa para la personalización de botones y el manejo de eventos.

override func viewDidLoad() {
    super.viewDidLoad()

    let button = UIButton.init(type: .custom)
    button.setTitle("Tester", for: .normal)
    button.setTitleColor(.darkGray, for: .normal)
    button.layer.borderWidth = 1
    button.layer.cornerRadius = 5
    button.layer.borderColor = UIColor.darkGray.cgColor
    button.addTarget(self, action: #selector(self.handleButton), for: .touchUpInside)

    self.navigationItem.rightBarButtonItem = UIBarButtonItem(customView: button)
}

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)

    if let button = self.navigationItem.rightBarButtonItem?.customView {
        button.frame = CGRect(x:0, y:0, width:80, height:34)
    }
}

func handleButton( sender : UIButton ) {
    // It would be nice is isEnabled worked...
    sender.alpha = sender.alpha == 1.0 ? 0.5 : 1.0
}

Espero que esto ayude

Joe Andolina
fuente
0

¿Su vista personalizada se alimenta de eventos táctiles o los transmite a la vista de padres?

Alex Reynolds
fuente
como puede ver, mi vista personalizada es simplemente un viejo UIImageView. Entonces, no está comiendo ningún evento, no.
Jacob Relkin
0

Para facilitar el trabajo, creé una categoría en UIBarButtonItem que se ve así:

@implementation UIBarButtonItem (CustomButtonView)

- (void)setButtonImage:(UIImage *)image
{
    UIButton * button = [UIButton buttonWithType:UIButtonTypeCustom];
    [button setBackgroundImage:image forState:UIControlStateNormal];
    [button sizeToFit];
    [button addTarget:self.target action:self.action forControlEvents:UIControlEventTouchUpInside];
    self.customView = button;
}

- (UIImage *)buttonImage
{
    return [(UIButton *)self.customView imageForState:UIControlStateNormal];
}

@end

En su código de cliente, simplemente use:

myBarButtonItem.buttonImage = [UIImage imagedNamed:@"image_name"];

Hecho de esta manera, aún puede conectar sus objetivos y acciones en IB (introducir tanta configuración de UI en IB como pueda es algo bueno).

Tylerc230
fuente