Передача параметров в addTarget:action:forControlEvents

Я использую addTarget:action:forControlEvents следующим образом:

[newsButton addTarget:self
action:@selector(switchToNewsDetails)
forControlEvents:UIControlEventTouchUpInside];

и я хотел бы передать параметры своему селектору "switchToNewsDetails". Единственное, что мне удалось сделать, это передать (id) отправителя, написав:

action:@selector(switchToNewsDetails:)

Но я пытаюсь передать переменные как целочисленные значения. Запись таким образом не работает:

int i = 0;
[newsButton addTarget:self
action:@selector(switchToNewsDetails:i)
forControlEvents:UIControlEventTouchUpInside];

Запись таким образом тоже не работает:

int i = 0;
[newsButton addTarget:self
action:@selector(switchToNewsDetails:i:)
forControlEvents:UIControlEventTouchUpInside];

Любая помощь будет оценена по достоинству :)


person Pierre Espenan    schedule 21.10.2010    source источник
comment
какова подпись метода для switchToNewsDetails?   -  person Aaron Saunders    schedule 21.10.2010
comment
- (void)switchToNewsDetails:(id)sender; - (void)switchToNewsDetails:(int)i:(id)sender;   -  person Pierre Espenan    schedule 21.10.2010
comment
но от чего это зависит? это конкретно для каждой кнопки? Смотрите мой ответ - разве свойство тега не то, что вам нужно?   -  person Vladimir    schedule 21.10.2010
comment
stackoverflow.com/questions/26619872 /   -  person quantumpotato    schedule 31.10.2014
comment
@PierreEspenan Ваш текущий принятый ответ позволяет передавать целые числа только через тег, что очень ограничивает. Я призываю вас изменить его на мой ответ, который позволяет передавать объект ЛЮБОЙ. stackoverflow.com/a/40051706/2057171   -  person Albert Renshaw    schedule 14.10.2016
comment
Кроме того, с помощью нового метода вы можете передавать НЕСКОЛЬКО объектов любого типа, а не только одно целое число. :D   -  person Albert Renshaw    schedule 15.10.2016
comment
Вы можете использовать решение, представленное в этом ответе, относительно UIButton и передачи нескольких параметров его селектору: stackoverflow.com/a/53779104/5324541   -  person Lloyd Keijzer    schedule 14.12.2018
comment
@LloydKeijzer Действительно интересно   -  person Pierre Espenan    schedule 17.12.2018


Ответы (13)


action:@selector(switchToNewsDetails:)

Здесь вы не передаете параметры методу switchToNewsDetails:. Вы просто создаете селектор, чтобы кнопка могла вызывать его, когда происходит определенное действие (подправить в вашем случае). Элементы управления могут использовать 3 типа селекторов для ответа на действия, все они имеют предопределенное значение своих параметров:

  1. без параметров

    action:@selector(switchToNewsDetails)
    
  2. с 1 параметром, указывающим элемент управления, который отправляет сообщение

    action:@selector(switchToNewsDetails:)
    
  3. С 2 параметрами, указывающими элемент управления, который отправляет сообщение, и событие, вызвавшее сообщение:

    action:@selector(switchToNewsDetails:event:)
    

Непонятно, что именно вы пытаетесь сделать, но, учитывая, что вы хотите назначить конкретный индекс сведений для каждой кнопки, вы можете сделать следующее:

  1. установить свойство тега для каждой кнопки, равное требуемому индексу
  2. в методе switchToNewsDetails: вы можете получить этот индекс и открыть соответствующие детали:

    - (void)switchToNewsDetails:(UIButton*)sender{
        [self openDetails:sender.tag];
        // Or place opening logic right here
    }
    
person Vladimir    schedule 21.10.2010
comment
что, если мы пытаемся передать что-то отличное от целых чисел? что, если мне нужно передать объект как строку? - person user102008; 07.04.2011
comment
@user какой у тебя контекст? Кажется, вам нужно будет пройти его отдельно - person Vladimir; 07.04.2011
comment
большое спасибо. где вы нашли эти данные? Я всегда ценю ссылку, так что, возможно, я мог бы найти ее сам в следующий раз. - person bearMountain; 30.03.2012
comment
@bearMountain вы можете проверить эту ссылку: developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/, например - person Vladimir; 31.03.2012
comment
Я получаю сообщение об ошибке, пытаясь использовать ожидаемый @selector, разделитель - person quantumpotato; 29.10.2014
comment
@quantumpotato, это выглядит странно. каков ваш фактический код? - person Vladimir; 30.10.2014
comment
Мне нужно что-то подобное: мне нужно передать indexpath.row И indexpath.section для передачи. Как этого добиться? - person Joris416; 10.01.2015
comment
Вам не нужно проходить его отдельно. Если вы передаете какой-либо NSObject, вы можете просто сказать [button.layer setValue:yourObject forKey:@"anyKey"];, а затем в методе просто отметить (objectClass *)[button.layer valueForKey:@"anyKey"]; Это похоже на более бесплатную версию .tag - person Albert Renshaw; 14.10.2016

Чтобы передать пользовательские параметры вместе с нажатием кнопки, вам просто нужно SUBCLASS UIButton.

(ASR включен, поэтому в коде нет выпусков.)

Это myButton.h

#import <UIKit/UIKit.h>

@interface myButton : UIButton {
    id userData;
}

@property (nonatomic, readwrite, retain) id userData;

@end

Это myButton.m

#import "myButton.h"
@implementation myButton
@synthesize userData;
@end

Использование:

myButton *bt = [myButton buttonWithType:UIButtonTypeCustom];
[bt setFrame:CGRectMake(0,0, 100, 100)];
[bt setExclusiveTouch:NO];
[bt setUserData:**(insert user data here)**];

[bt addTarget:self action:@selector(touchUpHandler:) forControlEvents:UIControlEventTouchUpInside];

[view addSubview:bt];

Получение функции:

- (void) touchUpHandler:(myButton *)sender {
    id userData = sender.userData;
}

Если вам нужно, чтобы я конкретизировал какую-либо часть приведенного выше кода — не стесняйтесь спрашивать об этом в комментариях.

person Yuriy Polezhayev    schedule 25.01.2013
comment
я думаю это лучший способ - person Çağatay Gürtürk; 12.01.2014
comment
Самое элегантное решение. - person Victor C.; 07.02.2014
comment
Отличный ответ ... очень настраиваемый. Именно то, что я искал. МОЛОДЕЦ! :) - person denikov; 27.03.2014
comment
спасибо за отзыв :) Рад, что кому-то пригодится :) - person Yuriy Polezhayev; 27.03.2014
comment
У меня есть кнопка, которую нужно настроить в файле xib. Как я могу сделать эту кнопку как мою кнопку программно? - person ttotto; 06.05.2015
comment
Простой и полезный обходной путь. Просто позвольте мне добавить, что это похоже на tag в представлениях Android. Они могут хранить любой объект. И вы также можете сохранять объекты по ключу как в Словаре/Карте. - person Ferran Maylinch; 04.12.2015

Target-Action допускает три различных формы селектора действий:

- (void)action
- (void)action:(id)sender
- (void)action:(id)sender forEvent:(UIEvent *)event
person Benoît    schedule 21.10.2010

Нужно больше, чем просто (int) через .tag? Используйте КВЦ!

Вы можете передавать любые данные, которые хотите, через сам объект кнопки (путем доступа к CALayers keyValue dict).


Установите цель следующим образом (с помощью :)

[myButton addTarget:self action:@selector(buttonTap:) forControlEvents:UIControlEventTouchUpInside];

Добавьте свои данные к самой кнопке (ну, .layer кнопки, которая есть) следующим образом:

NSString *dataIWantToPass = @"this is my data";//can be anything, doesn't have to be NSString
[myButton.layer setValue:dataIWantToPass forKey:@"anyKey"];//you can set as many of these as you'd like too!

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

-(void)buttonTap:(UIButton*)sender{

    NSString *dataThatWasPassed = (NSString *)[sender.layer valueForKey:@"anyKey"];
    NSLog(@"My passed-thru data was: %@", dataThatWasPassed);

}
person Albert Renshaw    schedule 14.10.2016
comment
в UIButton.layer нет метода для установки или добавления объекта. Где взять это решение? - person mAc; 07.05.2017
comment
UIButton — это тип UIView, все UIView имеют свойство CALayer .layer. CALayer содержит поведение NSDictionary. Обратите внимание, что это Objective-C, а не Swift, в этом может быть проблема? Вот документы Apple по кодированию ключ-значение CALayer: developer.apple.com/library/content/documentation/Cocoa/ - person Albert Renshaw; 07.05.2017
comment
Я делаю только в Objective-C, но когда вы пытаетесь написать addObject для компилятора UIButton, он говорит, что нет метода для UIbutton. Пытались сделать это в Xcode? - person mAc; 08.05.2017
comment
@mAc Мои извинения, это setValue:, а не addObject:. Исправление моего кода выше сейчас - person Albert Renshaw; 08.05.2017
comment
Это нормально, я сделал по-другому, но ваш ответ мне понравился больше, чем другие в этом посте. +1 - person mAc; 08.05.2017
comment
Это должен быть ответ сверху. Безусловно, самый простой способ сделать это!. Благодарность! - person danielrosero; 18.09.2018
comment
Это должен быть принятый ответ. Это намного проще и умнее. - person Emon; 17.04.2019
comment
Отличный ответ! Хотел бы я знать это давным-давно. - person John; 30.04.2019

Я сделал решение, частично основанное на приведенной выше информации. Я просто устанавливаю titlelabel.text в строку, которую хочу передать, и устанавливаю titlelabel.hidden = YES

Нравится :

UIButton *imageclick = [[UIButton buttonWithType:UIButtonTypeCustom] retain];
imageclick.frame = photoframe;
imageclick.titleLabel.text = [NSString stringWithFormat:@"%@.%@", ti.mediaImage, ti.mediaExtension];
imageclick.titleLabel.hidden = YES;

Таким образом, нет необходимости в наследовании или категории и нет утечки памяти.

person Abhinav Singh    schedule 29.10.2013

Я создавал несколько кнопок для каждого телефонного номера в массиве, поэтому каждой кнопке для звонка требовался отдельный номер телефона. Я использовал функцию setTag, когда создавал несколько кнопок в цикле for:

for (NSInteger i = 0; i < _phoneNumbers.count; i++) {

    UIButton *phoneButton = [[UIButton alloc] initWithFrame:someFrame];
    [phoneButton setTitle:_phoneNumbers[i] forState:UIControlStateNormal];

    [phoneButton setTag:i];

    [phoneButton addTarget:self
                    action:@selector(call:)
          forControlEvents:UIControlEventTouchUpInside];
}

Затем в моем методе call: я использовал тот же цикл for и оператор if, чтобы выбрать правильный номер телефона:

- (void)call:(UIButton *)sender
{
    for (NSInteger i = 0; i < _phoneNumbers.count; i++) {
        if (sender.tag == i) {
            NSString *callString = [NSString stringWithFormat:@"telprompt://%@", _phoneNumbers[i]];
            [[UIApplication sharedApplication] openURL:[NSURL URLWithString:callString]];
        }
    }
}
person rjdunwoody91    schedule 25.02.2014
comment
вы можете улучшить этот код: - (void)call:(UIButton *)sender { NSString *phoneNumber = _phoneNumbers[i]; NSString *callString = [NSString stringWithFormat:@"telprompt://%@", phoneNumber]; [[UIApplication sharedApplication] openURL:[NSURL URLWithString:callString]]; } - person Ladessa; 31.07.2014

Поскольку здесь упоминается много способов решения, кроме функции категории.

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

Например (бывший):

@interface UIButton (myData)

@property (strong, nonatomic) id btnData;

@end

в вашем представлении Controller.m

 #import "UIButton+myAppLists.h"

UIButton *myButton = // btn intialisation....
 [myButton set btnData:@"my own Data"];
[myButton addTarget:self action:@selector(buttonClicked:) forControlEvents:UIControlEventTouchUpInside];

Обработчик события:

-(void)buttonClicked : (UIButton*)sender{
    NSLog(@"my Data %@", sender. btnData);
}
person Kumar KL    schedule 13.10.2014
comment
Вы не можете добавлять свойства в категории. - person HotFudgeSunday; 28.10.2015

Вы можете заменить целевое действие замыканием (блоком в Objective-C), добавив вспомогательную оболочку замыкания (ClosureSleeve) и добавив его в качестве связанного объекта с элементом управления, чтобы он сохранялся. Таким образом, вы можете передавать любые параметры.

Свифт 3

class ClosureSleeve {
    let closure: () -> ()

    init(attachTo: AnyObject, closure: @escaping () -> ()) {
        self.closure = closure
        objc_setAssociatedObject(attachTo, "[\(arc4random())]", self, .OBJC_ASSOCIATION_RETAIN)
    }

    @objc func invoke() {
        closure()
    }
}

extension UIControl {
    func addAction(for controlEvents: UIControlEvents, action: @escaping () -> ()) {
        let sleeve = ClosureSleeve(attachTo: self, closure: action)
        addTarget(sleeve, action: #selector(ClosureSleeve.invoke), for: controlEvents)
    }
}

Использование:

button.addAction(for: .touchUpInside) {
    self.switchToNewsDetails(parameter: i)
}
person Marián Černý    schedule 05.07.2017
comment
Вышеупомянутый метод (self.switchToNewsDetails(parameter: i)) срабатывает 2 раза, зачем мне помогать - person Nagendar; 04.01.2018
comment
Код работает для меня. Протестировано в новом проекте приложения Single View pastebin.com/tPaqetMb. Так что проблема, вероятно, в вашем коде, например, вызов button.addAction() дважды. Добавьте точку останова на button.addAction, а также на первую строку внутри замыкания и попробуйте отладить ее самостоятельно. - person Marián Černý; 04.01.2018

Есть еще один способ, которым вы можете получить indexPath ячейки, где была нажата ваша кнопка:

используя обычный селектор действий, например:

 UIButton *btn = ....;
    [btn addTarget:self action:@selector(yourFunction:) forControlEvents:UIControlEventTouchUpInside];

а затем в вашей функции:

   - (void) yourFunction:(id)sender {

    UIButton *button = sender;
    CGPoint center = button.center;
    CGPoint rootViewPoint = [button.superview convertPoint:center toView:self.tableView];
    NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:rootViewPoint];
    //the rest of your code goes here
    ..
}

так как вы получаете indexPath, это стало намного проще.

person Anton Lisitsyn    schedule 02.04.2018

См. мой комментарий выше, и я считаю, что вам нужно использовать NSInvocation, когда есть более одного параметра.

больше информации о NSInvocation здесь

http://cocoawithlove.com/2008/03/construct-nsinvocation-for-any-message.html

person Aaron Saunders    schedule 21.10.2010
comment
На самом деле я не хочу передавать несколько параметров, я просто хочу целое число. - person Pierre Espenan; 21.10.2010
comment
на самом деле есть метод PerformSelector:withObject:withObject:, который позволяет вызывать селекторы с двумя параметрами. Но для большего вам нужен NSInvocation - person Vladimir; 21.10.2010

Это исправило мою проблему, но она разбилась, если я не изменил

action:@selector(switchToNewsDetails:event:)                 

to

action:@selector(switchToNewsDetails: forEvent:)              
person iphaaw    schedule 12.02.2011

Я создал подкласс UIButton в CustomButton и добавил свойство, в котором я храню свои данные. Итак, я вызываю метод: (CustomButton*) sender, и в этом методе я только читаю свои данные sender.myproperty.

Пример пользовательской кнопки:

@interface CustomButton : UIButton
@property(nonatomic, retain) NSString *textShare;
@end

Действие метода:

+ (void) share: (CustomButton*) sender
{
    NSString *text = sender.textShare;
    //your work…
}

Назначить действие

    CustomButton *btn = [[CustomButton alloc] initWithFrame: CGRectMake(margin, margin, 60, 60)];
    // other setup…

    btnWa.textShare = @"my text";
    [btn addTarget: self action: @selector(shareWhatsapp:)  forControlEvents: UIControlEventTouchUpInside];
person iNiko84    schedule 24.05.2016

Если вы просто хотите изменить текст для leftBarButtonItem, отображаемый навигационным контроллером вместе с новым представлением, вы можете изменить заголовок текущего представления непосредственно перед вызовом pushViewController на нужный текст и восстановить его в обратном вызове viewHasDisappered для будущих показов текущий вид.

Этот подход сохраняет функциональность (popViewController) и внешний вид показанной стрелки без изменений.

У нас это работает по крайней мере с iOS 12, построенной с помощью Xcode 10.1...

person Bubu    schedule 11.12.2018
comment
Я не думаю, что этот ответ связан с вопросом. - person Pierre Espenan; 17.12.2018