UIBarButtonItem: целевое действие не работает?

80

У меня есть собственный вид внутри UIBarButtonItem, установленный путем вызова -initWithCustomView. Элемент моей кнопки на панели отображается нормально, но когда я нажимаю на него, он не вызывает действие с моим целевым объектом.

Вот мой код:

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)];
Яков Релкин
источник

Ответы:

126

Я не думаю, что цель и действие UIBarButtonItem применимы к настраиваемым представлениям. Попробуйте использовать UIButton вместо UIImageView и примените цель и действие к кнопке.

Пример кода в 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
втянутый
источник
31
Благодарю. UIBarButtonItemнаследуется от UIBarItemи NSObjectпоэтому ничего не знает о прикосновениях. Было бы неплохо, если бы в документации упоминалось, что свойства actionи targetприменяются только в том случае, если настраиваемое представление является UIButton.
Джейсон Мур
18
@JasonMoore: еще лучше, было бы неплохо, если бы кнопки действовали как ... кнопки. Если он вел себя так, как ожидает программист, в дополнительной документации нет необходимости.
General Mike
Спасибо. Кроме того, мы можем использовать ссылки IBOutlet на setTarget и setAction в коде. Но если мы создадим UIBarButtonItem в коде, мы не сможем setTarget и setAction, это не сработает.
MOBYTELAB 03
1
Хотя это старый пост, я все равно хотел им поделиться. Поскольку мне надоело обращать внимание на объекты UIBarButtonItem, имеющие или не имеющие настраиваемые представления, я написал расширяющийся класс, который на самом деле ведет себя так (я думаю), что и должен. Вы можете найти источник здесь: gist.github.com/tvervest/8708465
Thomas Vervest
хех, я использовал targetForAction вместо target.
Mathijs Segers
24

У меня была такая же проблема, но я не хотел использовать UIButtonдля моего пользовательского представления UIBarButtonItem(согласно ответу drawonward).

В качестве альтернативы вы можете добавить UIGestureRecognizerк настраиваемому представлению, прежде чем использовать его для инициализации UIBarButtonItem; похоже, это работает в моем проекте.

Вот как я бы изменил ваш исходный код:

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];
Тим Арнольд
источник
У меня ничего не работало, только это решение. Пользовательский вид не позволяет установить действие. Обычный UIButton в то же время не может отобразить фоновое изображение. С этим распознавателем жестов наконец-то все получилось.
Денис
Добавление жеста в мой пользовательский вид не сработало, но оно сработало, когда я добавил его в свой элемент кнопки панели.
Роналдо1,
@ Ronaldoh1 Как вы добавили UITapgesture в UIBarButtonItem?
tsuz
@ user013948, как и любой другой вид. UIBarButtonItem является подклассом UIView (если не ошибаюсь).
Ronaldoh1
@ Ronaldoh1 Нет, это не так. developer.apple.com/library/ios/documentation/UIKit/Reference/…
tsuz
24

Вот как я заставляю это работать:

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

UIBarButtonItem* itemAboutUs =[[UIBarButtonItem alloc]initWithCustomView:infoButton];
…
Николя Локен
источник
9

Вот как я реализовал UIButton внутри 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];
Зак Витте
источник
Это правильное решение. Мы также можем использовать UIButton, чтобы добавить еще одно представление.
Brave,
7

У меня была аналогичная проблема. И я сначала пошел по пути, предложенному @drawnonward, но затем столкнулся с проблемой, когда попытался представить своим действием всплывающий контроллер на iPad: использование встроенного UIButton в качестве настраиваемого представления означает, что UIButton является отправителем события, и Метод presentPopoverFromBarButtonItem: контроллера всплывающего окна дает сбой при попытке отправить ему сообщения, которые подходят только для фактических элементов UIBarButtonItem.

Решение, которое я в конце концов нашел, заключалось в том, чтобы украсть изображение, которое я хотел использовать (значок «информация») из одноразового UIButton, и сконструировать мой UIBarButtonItem следующим образом:

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

Использование этого инициализатора дает кнопку-полосу, цель и селектор которой действительно работают. Это также проще, чем оборачивать изображение в пользовательском представлении, но это просто глазурь.

Джеймс Эллиотт
источник
1
Это решило проблему для меня! Для использования с всплывающим окном используйте это: [myPopover presentPopoverFromBarButtonItem: sender allowedArrowDirections: UIPopoverArrowDirectionAny animated: YES];
mpemburn
2

Если вы не хотите соглашаться с UIImage и иметь настраиваемое представление в своем barbuttonitem, установите распознаватель касаний для свойства customview вашего barbuttonitem

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

Лео
источник
1

Джейкоб, все выглядит хорошо, но, возможно, вы не предоставили правильный селектор.

Можете ли вы убедиться, что ваше действие действительно объявлено

- (void) deselectAll;

и нет

- (void) deselectAll:(id)sender;

Если это последнее, вам нужно будет установить действие @selector(deselectAll:). (обратите внимание на точку с запятой, чтобы соответствовать объявлению метода)

Кроме того, может быть void IBAction, но это не имеет отношения к вашей проблеме.

охороб
источник
@ohhorob, я уверен в сигнатуре метода.
Джейкоб Релкин
@ohhorob, я тоже не пользуюсь IB;)
Яков Релкин
также проверьте, что экземпляр, который вы устанавливаете как цель, не освобождается и не освобождается. Это подкласс контроллера навигации или другой контроллер представления в иерархии навигации?
охороб
@ohhorob, этот код находится внутри контроллера представления, который не выпускается.
Яков Релкин,
как вы подтвердили, что вам deselectAllне звонят? Установив точку останова отладчика, или вы просто не видите ожидаемых результатов?
охороб
1

Swift 3. У меня была аналогичная проблема, когда у меня был настраиваемый вид внутри элемента кнопки панели. Чтобы кран работал, я назначил представлению жест и установил действие. Представление должно было быть отдушиной, чтобы его присвоить. При этом он работал с пользовательским представлением.

Установка жеста как переменной класса

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

В viewDidLoad

incidentTap.addTarget(self, action: #selector(self.changeIncidentBarItem))
incidentView.addGestureRecognizer(incidentTap)
Мика Монтойя
источник
1

Swift 3 : Ниже представлена ​​моя полная реализация настройки кнопок и обработки событий.

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
}

Надеюсь это поможет

Джо Андолина
источник
0

Ваше пользовательское представление использует события касания или передает их родительскому представлению?

Алекс Рейнольдс
источник
как видите, мой пользовательский вид - это просто старый UIImageView . Итак, это не съедает никаких событий, нет.
Джейкоб Релкин
0

Чтобы упростить работу с этим, я создал категорию на UIBarButtonItem, которая выглядит так:

@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

В вашем клиентском коде просто используйте:

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

Сделав это таким образом, вы по-прежнему можете подключать свои цели и действия в IB (добавление в IB как можно большего количества конфигурации пользовательского интерфейса - это хорошо).

Tylerc230
источник