Передача параметрів для addTarget: action: forControlEvents


125

Я використовую 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];

Будь-яка допомога буде вдячна :)


що таке підпис методу для SwitchToNewsDetails?
Аарон Сондерс

- (недійсний) switchToNewsDetails: (id) відправник; - (void) switchToNewsDetails: (int) i: (id) відправник;
П’єр Еспенан

але від чого я залежить? це специфічно для кожної кнопки? Дивіться мою відповідь - чи не властивість тегу - це те, що вам потрібно?
Володимир


@PierreEspenan Ваш поточний прийнятий відповідь дозволяє лише передавати цілі числа через тег, що дуже обмежує. Я закликаю вас змінити його на мою відповідь, яка дозволяє передавати будь-який об’єкт. stackoverflow.com/a/40051706/2057171
Альберт Реншо

Відповіді:


173
action:@selector(switchToNewsDetails:)

Тут ви не передаєте параметри switchToNewsDetails:методу. Ви просто створите селектор, щоб кнопка змогла викликати його, коли відбувається певна дія (підключіть у вашому випадку). Елементи керування можуть використовувати 3 типи селекторів для реагування на дії, всі вони мають заздалегідь визначене значення своїх параметрів:

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

    action:@selector(switchToNewsDetails)
  2. з 1 параметром, що вказує на керування, яке надсилає повідомлення

    action:@selector(switchToNewsDetails:)
  3. З двома параметрами, що вказують на керування, яке надсилає повідомлення, та подію, яка викликала повідомлення:

    action:@selector(switchToNewsDetails:event:)

Не ясно, що саме ви намагаєтеся зробити, але враховуючи, що ви хочете призначити конкретний індекс деталей кожній кнопці, ви можете зробити наступне:

  1. встановіть властивість тегу для кожної кнопки, рівну необхідному індексу
  2. у switchToNewsDetails:методі ви можете отримати цей індекс і відкрити відповідні дилери:

    - (void)switchToNewsDetails:(UIButton*)sender{
        [self openDetails:sender.tag];
        // Or place opening logic right here
    }

4
що робити, якщо ми намагаємося передати щось інше, ніж цілі числа? що робити, якщо мені потрібно передати об'єкт, як рядок?
користувач102008

@user, який у тебе контекст? Здається, вам потрібно буде пройти його окремо
Володимир

дуже дякую. де ви знайшли ці дані? Я завжди ціную посилання, так що, можливо, я міг би знайти його сам наступного разу.
ведмідьМаунт

1
@bearMountain Ви можете перевірити це посилання: developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/… наприклад
Володимир

3
Не потрібно передавати його окремо. Якщо ви передаєте будь-який NSObject, ви можете просто сказати, [button.layer setValue:yourObject forKey:@"anyKey"];а потім у методі просто перевірити (objectClass *)[button.layer valueForKey:@"anyKey"];Це як більш безкоштовна версія.tag
Альберт Реншо

64

Для передачі користувальницьких параммерів разом із кнопкою просто натисніть кнопку SUBCLASS UIButton .

(ASR увімкнено, тому в коді немає випусків.)

Це myButton.h

#import <UIKit/UIKit.h>

@interface myButton : UIButton {
    id userData;
}

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

@end

Це мійButton.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;
}

Якщо ви хочете, щоб я був більш конкретним щодо будь-якої частини вищевказаного коду, не соромтесь запитати про це у коментарях.


Я думаю, що це найкращий спосіб
Çağatay Gürtürk

Найелегантніше рішення.
Віктор Ч.

2
Чудова відповідь ... дуже настроюється. Саме те, що я шукав. МОЛОДЕЦЬ! :)
denikov

дякую за відгук :) Рада, що комусь це корисно :)
Юрій Поляжаєв,

1
Простий і корисний спосіб вирішення. Дозвольте додати, що це подібне до tagвластивості в представленнях Android . Вони можуть зберігати будь-який об’єкт. Ви також можете зберігати об'єкти за ключем, як у словнику / карті.
Ферран Мейлінч

19

Цільова дія дозволяє три різних форми вибору дії:

- (void)action
- (void)action:(id)sender
- (void)action:(id)sender forEvent:(UIEvent *)event

16

Вам потрібно більше, ніж просто (int) через .tag? Використовуйте KVC!

Ви можете передати будь-які потрібні дані через сам об’єкт кнопки (відкривши доступ до карти CALayers keyValue).


Встановіть ціль таким чином (за допомогою ":")

[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);

}

у UIButton.layer немає способу встановити або додати об’єкт. Звідки ви берете це рішення?
mAc

1
UIButton - це тип UIView, усі UIView мають властивість. CALayer містить поведінку NSDictionary. Зауважте, це "Objective-C", а не "Swift", можливо, це проблема? Ось Документи Apple щодо кодування ключових значень CALayer: developer.apple.com/library/content/documentation/Cocoa/…
Альберт Реншо

1
Це має бути відповідь зверху. На сьогодні найпростіший спосіб зробити це! Дякую!
danielrosero

1
На це слід прийняти відповідь. Це набагато простіший та розумний спосіб.
Емон

1
Чудова відповідь! Хотілося б, я це знав давно.
Джон

7

Я прийняв рішення частково, грунтуючись на наведеній вище інформації. Я просто встановив titlelabel.text на рядок, який я хочу пройти, і встановив titlelabel.hidden = ТАК

Подобається це :

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

Таким чином, немає потреби в спадкуванні або категорії і немає витоку пам'яті


5

Я створював декілька кнопок для кожного номера телефону в масиві, тому для кожної кнопки потрібен різний номер телефону для дзвінка. Я використовував функцію 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];
}

Тоді у своєму виклику: метод я використовував те саме для циклу та оператора 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]];
        }
    }
}

Ви можете вдосконалити цей код:- (void)call:(UIButton *)sender { NSString *phoneNumber = _phoneNumbers[i]; NSString *callString = [NSString stringWithFormat:@"telprompt://%@", phoneNumber]; [[UIApplication sharedApplication] openURL:[NSURL URLWithString:callString]]; }
Ladessa

2

Оскільки тут вирішено багато способів рішення, крім функції категорії.

Використовуйте функцію категорії, щоб розширити визначений (вбудований) елемент у свій налаштований елемент.

Наприклад (наприклад):

@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);
}

Дійсно не можна додати властивості в категорії.
HotFudgeSunday

2

Ви можете замінити цільову дію закриттям (блоком у 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)
}

Вищеописаний метод ( self.switchToNewsDetails(parameter: i)) стріляв 2 рази, чому мені допомогти
Нагендар

Код працює для мене. Тестовано в новому проекті програми Single View pastebin.com/tPaqetMb . Тому проблема, ймовірно, у вашому коді, як дзвінки button.addAction()двічі. Додайте точку перерви до button.addActionта також на першому рядку всередині закриття та спробуйте відладкувати її самостійно.
Marián Černý

2

Є ще один спосіб, за допомогою якого ви можете отримати 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, це стає набагато простішим.


1

Дивіться мій коментар вище, і я вважаю, що вам доведеться використовувати NSInvocation, коли є більше одного параметра

Більше інформації про NSInvocation тут

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


Насправді я не хочу передавати декілька парам. Я просто хочу ціле число.
П’єр Еспенан

насправді існує метод performSelector: withObject: withObject:, який дозволяє викликати селектори з 2 параметрами. Але для більше вам потрібна NSInvocation
Володимир

1

Це вирішило мою проблему, але вона вийшла з ладу, якщо я не змінився

action:@selector(switchToNewsDetails:event:)                 

до

action:@selector(switchToNewsDetails: forEvent:)              

0

Я підкласифікував UIButton у CustomButton і додаю властивість, де зберігаю свої дані. Тому я називаю метод: (CustomButton *) відправника, і в методі я читаю лише свої дані sender.myproperty.

Приклад CustomButton:

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

0

Якщо ви просто хочете змінити текст для leftBarButtonItem, показаний контролером навігації разом з новим представленням, ви можете змінити заголовок поточного перегляду безпосередньо перед тим, як викликати pushViewController на потрібний текст і відновити його у виглядHasDisappered callback для майбутніх показів поточний вигляд.

Такий підхід зберігає функціональність (popViewController) та зовнішній вигляд показаної стрілки.

Він працює для нас як мінімум з iOS 12, побудований з Xcode 10.1 ...


Я не думаю, що ця відповідь пов'язана з питанням.
П’єр Еспенан
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.