UIBarButtonItem: akcja docelowa nie działa?

80

Mam niestandardowy widok wewnątrz UIBarButtonItem, ustawiany przez telefon -initWithCustomView. Mój przycisk paska renderuje się dobrze, ale kiedy go stukam, nie wywołuje akcji na moim obiekcie docelowym.

Oto mój kod:

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
źródło

Odpowiedzi:

126

Nie sądzę, aby cel i działanie UIBarButtonItem miały zastosowanie do widoków niestandardowych. Spróbuj użyć UIButton zamiast UIImageView i zastosować cel i akcję do przycisku.

Przykładowy kod w 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
ciągnięty do przodu
źródło
31
Dzięki. UIBarButtonItemDziedziczy z UIBarItema NSObjectwięc nie wiem nic o akcentami. Byłoby miło, gdyby dokumentacja wspomniała, że właściwości actioni targetmają zastosowanie tylko wtedy, gdy widok niestandardowy to UIButton.
Jason Moore
18
@JasonMoore: jeszcze lepiej, byłoby miło, gdyby przyciski działały jak ... przyciski. Jeśli zachowywał się tak, jak oczekuje tego programista, nie ma potrzeby dodatkowej dokumentacji.
GeneralMike
Dzięki wielkie. Możemy również użyć odwołań IBOutlet do setTarget i setAction w kodzie. Ale jeśli utworzymy UIBarButtonItem w kodzie, nie możemy ustawićTarget i setAction, to nie zadziała.
MOBYTELAB
1
Chociaż jest to stary post, i tak chciałem się nim podzielić. Kiedy zmęczyło mnie zwracanie uwagi na obiekty UIBarButtonItem mające własne widoki lub nie, napisałem rozszerzającą klasę, która faktycznie zachowuje się tak, jak (myślę), że powinna. Możesz znaleźć źródło tutaj: gist.github.com/tvervest/8708465
Thomas Vervest
heh, zamiast celu użyłem targetForAction.
Mathijs Segers
24

Miałem ten sam problem, ale nie chciałem UIButtonużywać widoku niestandardowego zamiast mojego UIBarButtonItem(zgodnie z odpowiedzią drawonward).

Alternatywnie, możesz dodać UIGestureRecognizerdo widoku niestandardowego przed użyciem go do inicjalizacji UIBarButtonItem; wydaje się, że to działa w moim projekcie.

Oto jak zmodyfikowałbym twój oryginalny kod:

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
źródło
Nic nie działało, tylko to rozwiązanie. Widok niestandardowy nie pozwala na ustawienie akcji. Zwykły przycisk UIB w tym samym czasie ma problem z wyświetleniem obrazu tła. Dzięki temu rozpoznawaniu gestów w końcu się udało.
Denis
dodanie tapgesture do mojego widoku niestandardowego nie działało, ale działało, gdy dodałem go do przycisku paska.
Ronaldoh1
@ Ronaldoh1 Jak dodałeś UITapgesture do UIBarButtonItem?
tsuz
@ user013948, tak jak w przypadku każdego widoku. UIBarButtonItem podklasy UIView (jeśli się nie mylę).
Ronaldoh1
24

Oto jak to działa:

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

UIBarButtonItem* itemAboutUs =[[UIBarButtonItem alloc]initWithCustomView:infoButton];
…
Nicolas Lauquin
źródło
9

Oto jak zaimplementowałem UIButton wewnątrz 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
źródło
To rozwiązanie jest poprawne. Możemy również użyć UIButton, aby dodać inny widok.
Brave
7

Miałem podobny problem. Początkowo podążałem ścieżką sugerowaną przez @drawnonward, ale potem wpadłem w kłopoty, gdy próbowałem, aby moja akcja przedstawiała kontroler popover na iPadzie: użycie wbudowanego przycisku UIButton jako widoku niestandardowego oznacza, że ​​UIButton jest nadawcą zdarzenia, i PresentPopoverFromBarButtonItem: metoda kontrolera popover ulega awarii, gdy próbuje wysłać do niej komunikaty, które są odpowiednie tylko dla rzeczywistych elementów UIBarButtonItems.

Ostatecznie znalazłem rozwiązanie polegające na kradzieży obrazu, którego chciałem użyć (ikona „info”) z jednorazowego UIButton i skonstruowanie mojego UIBarButtonItem w następujący sposób:

// 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];

Użycie tego inicjatora daje przycisk paska, którego cel i selektor faktycznie działają. Jest to również łatwiejsze niż zawijanie obrazu w widoku niestandardowym, ale to tylko lukier.

James Elliott
źródło
1
To rozwiązało to dla mnie! Do użytku z popover, użyj tego: [myPopover presentPopoverFromBarButtonItem: sender allowedArrowDirections: UIPopoverArrowDirectionAny animated: YES];
mpemburn
2

Jeśli nie chcesz rozliczać się z UIImage i mieć niestandardowy widok w swoim barbuttonitem, ustaw rozpoznawanie gestów dotknięcia na właściwość customview swojego barbuttonitem

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

Lew
źródło
1

Jacob, wszystko wygląda dobrze, ale być może nie podałeś prawidłowego selektora.

Czy możesz sprawdzić, czy Twoje działanie zostało faktycznie zadeklarowane?

- (void) deselectAll;

i nie

- (void) deselectAll:(id)sender;

Jeśli jest to drugie, musisz ustawić akcję na @selector(deselectAll:). (zwróć uwagę na średnik, aby dopasować deklarację metody)

Może być też pustka IBAction, ale to nie dotyczy tego problemu.

ohhorob
źródło
@ohhorob, jestem pewien co do podpisu metody.
Jacob Relkin
@ohhorob, ja też nie używam IB;)
Jacob Relkin
sprawdź także, czy instancja, którą ustawiasz jako miejsce docelowe, nie jest zwalniana i zwalniana. Czy jest to podklasa kontrolera nawigacji czy inny kontroler widoku w hierarchii nawigacji?
ohhorob
@ohhorob, ten kod znajduje się w kontrolerze widoku, który nie jest publikowany.
Jacob Relkin
jak potwierdziłeś, deselectAllże nie dzwonisz? Ustawiając punkt przerwania debugera, czy po prostu nie widzisz oczekiwanych wyników?
ohhorob
1

Swift 3. Miałem podobny problem, gdy miałem niestandardowy widok wewnątrz elementu przycisku paska. Aby kran działał, przypisałem do widoku gest i ustawiłem akcję. Widok musiał być ujściem, aby go przypisać. W ten sposób działało z widokiem niestandardowym.

Ustawianie gestu jako zmiennej klasy

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

W widokuDidLoad

incidentTap.addTarget(self, action: #selector(self.changeIncidentBarItem))
incidentView.addGestureRecognizer(incidentTap)
Micah Montoya
źródło
1

Swift 3 : Poniżej znajduje się moja pełna implementacja dostosowywania przycisków i obsługi zdarzeń.

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
}

Mam nadzieję że to pomoże

Joe Andolina
źródło
0

Czy Twój niestandardowy widok zjada zdarzenia dotykowe, czy przekazuje je do widoku rodzica?

Alex Reynolds
źródło
jak widać, mój niestandardowy widok jest zwykłym starym UIImageView. Więc to nie zjada żadnych wydarzeń, nie.
Jacob Relkin
0

Aby ułatwić pracę, utworzyłem kategorię na UIBarButtonItem, która wygląda następująco:

@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

W swoim kodzie klienta po prostu użyj:

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

Zrobione w ten sposób, nadal możesz łączyć cele i akcje w IB (wpychanie jak największej ilości konfiguracji UI do IB jest dobrą rzeczą).

Tylerc230
źródło