I have eight (8) UIButtons setup in my game. When one is selected it shows that it is selected and if you click it again it will show as unselected. However, I want to make it so that when you select a button and any of the other seven (7) are selected, they become unselected.
I know how to do this through the use of [buttonName setSelected:NO] but the problem is I can't pass buttonOne to buttonTwo if buttonTwo has already been passed to buttonOne because I have already imported buttonTwo's header file in buttonOne. It throws a parse error if I have both headers importing each other. I've been stuck on this for a while now and was hoping that someone might have a solution to my problem.
Thanks for any help.
Get the parent view of the current button and iterate through all the buttons inside, unselecting all of them. Then, select the current one.
// Unselect all the buttons in the parent view
for (UIView *button in currentButton.superview.subviews) {
if ([button isKindOfClass:[UIButton class]]) {
[(UIButton *)button setSelected:NO];
}
}
// Set the current button as the only selected one
[currentButton setSelected:YES];
Note: As suggested on the comments, you could keep an array of buttons and go over it the same way the above code does with the subviews of the parent view. This will improve the performance of your code in case the view containing the buttons has many other subviews inside.
I know its too late to answer this question but I did it in only small lines of code . Here is what i did :
NSArray *arrView = self.view.subviews;
for (UIButton *button in arrView) {
if ([button isKindOfClass:[UIButton class]]) {
[((UIButton *) button) setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
}
}
[button1 setTitleColor:[UIColor orangeColor] forState:UIControlStateNormal];
Simple way to do.
-(void)buttonSelected:(id)sender{
UIButton *currentButton = (UIButton *)sender;
for(UIView *view in self.view.subviews){
if([view isKindOfClass:[UIButton class]]){
UIButton *btn = (UIButton *)view;
[btn setSelected:NO];
}
}
[currentButton setSelected:YES];
}
I actually created an answer by reading all of your guys input, which I thank you greatly for. The tag property of the UIButton class was unknown to me before this post.
I created own subclass of UIButton, let's call it CustomUIButton.m. I created a NSMutableArray property for use when storing the buttons, which I'll call customButtonArray.
When I created the button, I set the tag property, and added the button to a local array on the parent view controller. After all buttons I wanted were created, I set the customButtonArray, like so:
// Initialize buttonHolderArray
NSMutableArray *buttonHolderArray = [[NSMutableArray alloc] init];
// Create a button
CustomUIButton *newButton = [[CustomUIButton alloc] initWithFrame:CGRectMake(10, 10, 50, 30)];
newButton.tag = 1;
[newButton setImage:[UIImage imageNamed:#"newButtonUnselected" forControlState:UIControlStateNormal]];
[buttonHolderArray addObject:newButton];
// Create additional buttons and add to buttonHolderArray...
// using different numbers for their tags (i.e. 2, 3, 4, etc)
// Set customButtonArray property by iterating through buttonHolderArray
NSInteger buttonCount = [buttonHolderArray count];
for (int i = 0; i < buttonCount; i++)
{
[[buttonHolderArray objectAtIndex:i] setCustomButtonArray:buttonHolderArray];
}
To deselect any other button selected when a different buttons handleTap: is called, I iterated through the customButtonArray in the subclass main file and set the selected property to NO. I also set the correct image from another array property that I manually populated with the images, which I did so the array didn't have to be populated every time a button was pressed. At the end, unselected all other buttons, like so:
// Populate two arrays: one with selected button images and the other with
// unselected button images that correspond to the buttons index in the
// customButtonArray
NSMutableArray *tempSelectedArray = [[NSMutableArray alloc] init];
[tempSelectedArray addObject:[UIImage imageNamed:#"newButtonSelected"]];
// Add the other selected button images...
// Set the property array with this array
[self setSelectedImagesArray:tempSelectedArray];
NSMutableArray *tempUnselectedArray = [[NSMutableArray alloc] init];
[tempUnselectedArray addObject:[UIImage imageNamed:#"newButtonUnselected"]];
// Add the other unselected button images...
// Set the property array with this array
[self setUnselectedImagesArray:tempUnselectedArray];
- (void)handleTap:(UIGestureRecognizer *)selector
{
// Get the count of buttons stored in the customButtonArray, use an if-elseif
// statement to check if the button is already selected or not, and iterate through
// the customButtonArray to find the button and set its properties
NSInteger buttonCount = [[self customButtonArray] count];
if (self.selected == YES)
{
for (int i = 0; i < buttonCount; i++)
{
if (self.tag == i)
{
[self setSelected:NO];
[self setImage:[[self unselectedImagesArray] objectAtIndex:i] forControlState:UIControlStateNormal];
}
}
}
else if (self.selected == NO)
{
for (int i = 0; i < buttonCount; i++)
{
if (self.tag == i)
{
[self setSelected:NO];
[self setImage:[[self selectedImagesArray] objectAtIndex:i] forControlState:UIControlStateNormal];
}
}
}
for (int i = 0; i < buttonCount; i++)
{
if (self.tag != i)
{
[self setSelected:NO];
[self setImage:[[self unselectedImagesArray] objectAtIndex:i] forControlState:UIControlStateNormal];
}
}
}
Thanks for all of the useful information though, figured I should share the final answer I came up with in detail to help anyone else that comes across this problem.
I figured out a pretty easy way to solve this. My example is for 2 buttons but you can easily add more if statements for additional buttons. Connect all buttons to the .h file as properties and name them (I did button1 & button2). Place the following code in your .m file and Connect it (via the storyboard) to all of your buttons. Make sure when you are setting up your button to set an image for BOTH the normal UIControlStateNormal & UIControlStateSelected or this wont work.
- (IBAction)selectedButton1:(id)sender {
if ([sender isSelected]) {
[sender setSelected:NO];
if (sender == self.button1) {
[self.button2 setSelected:YES];
}
if (sender == self.button2) {
[self.button1 setSelected:YES];
}
}
else
{
[sender setSelected:YES];
if (sender == self.button1) {
[self.button2 setSelected:NO];
}
if (sender == self.button2) {
[self.button1 setSelected:NO];
}
}
To answer "It throws a parse error if I have both headers importing each other"...
You should refrain from using #import in .h files as much as possible and instead declare whatever you're wanting to use as a forward class declaration:
#class MyCustomClass
#interface SomethingThatUsesMyCustomClass : UIViewController
#property (nonatomic, strong) MyCustomClass *mcc;
#end
Then #import the header in your .m file:
#import "MyCustomClass.h"
#implementation SomethingThatUsesMyCustomClass
-(MyCustomClass *)mcc
{
// whatever
}
#end
This approach will prevent errors caused by #import cycles.
Though I must say I agree with SergiusGee's comment on the question that this setup feels a bit strange.
The easiest approach here would be to get the parent UIView the buttons are on and iterate through it. Here's a quick example from my code:
for (UIView *tmpButton in bottomBar.subviews)
{
if ([tmpButton isKindOfClass:[UIButton class]])
{
if (tmpButton.tag == 100800)
{
tmpButton.selected = YES;
[tmpButton setTitleColor:[UIColor greenColor] forState:UIControlStateNormal];
[tmpButton setTitleColor:[UIColor greenColor] forState:UIControlStateHighlighted];
}else{
tmpButton.selected = NO;
[tmpButton setTitleColor:[UIColor redColor] forState:UIControlStateNormal];
[tmpButton setTitleColor:[UIColor redColor] forState:UIControlStateHighlighted];
}
}
}
Did you try using ReactiveCocoa framework and add some blocks for your code , this is not the most simple approach yet i would say it is the most effective when you have multiple dependencies and very good for scaling
I have created a small project for a solution to your problem using my suggested approach (I tried to adapt it to the good old MVC pattern instead of my preferred MVVM)
you can find it here
https://github.com/MWaly/MWButtonExamples
make sure to install cocoa pods file as we need "ReactiveCocoa" and "BlocksKit" for this sample
we will use two main classes
ViewController => The viewController object displaying the buttons
MWCustomButton => Custom UIButton which handles events
when creating the buttons a weak reference to the viewController is also created using the property
#property (weak) ViewController *ownerViewController ;
events will be handled using the help of blocksKit bk_addEventHandler method and pass it to the block of the ViewController (selectedButtonCallBackBlock)
[button bk_addEventHandler:^(id sender)
{
self.selectedButtonCallBackBlock(button);
} forControlEvents:UIControlEventTouchUpInside];
now in the ViewController for each button touched the callBackButtonBlock will be trigerred , where it will change its currently selected button if applicable
__weak __typeof__(self) weakSelf = self;
self.selectedButtonCallBackBlock=^(MWCustomButton* button){
__typeof__(self) strongSelf = weakSelf;
strongSelf.currentSelectedButton=button;
};
in the MWCustomButton class , it would listen for any changes in the property of "currentSelectedButton" of its ownerViewController and will change its selection property according to it using our good Reactive Cocoa
///Observing changes to the selected button
[[RACObserve(self, ownerViewController.currentSelectedButton) distinctUntilChanged] subscribeNext:^(MWCustomButton *x) {
self.selected=(self==x);
}];
i think this would solve your problem , again your question might be solved in a simpler way , however i believe using this approach would be more scalable and cleaner.
Loop through all views in parent view. Check if it is a UIButton(or your custom button class) and not the sender. Set all views isSelected to false. Once loop is finished, set sender button isSelected to true.
Swift 3 way:
func buttonPressed(sender: UIButton) {
for view in view.subviews {
if view is UIButton && view != sender {
(view as! UIButton).isSelected = false
}
}
sender.isSelected = true
}
Swift 4
//Deselect all tip buttons via IBOutlets
button1.isSelected = false
button2.isSelected = false
button3.isSelected = false
//Make the button that triggered the IBAction selected.
sender.isSelected = true
//Get the current title of the button that was pressed.
let buttonTitle = sender.currentTitle!
Related
I have programmatically generated UIButtons that all share the same selector method. When the method runs I would like the method to know which button was pressed and then be able to load a corresponding UIViewController.
-(void)buildButtons
{
for( int i = 0; i < 5; i++ ) {
UIButton* aButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[aButton setTag:i];
[aButton addTarget:self action:#selector(buttonClicked:)forControlEvents:UIControlEventTouchUpInside];
[aView addSubview:aButton];
}
Then:
- (void)buttonClicked:(UIButton*)button
{
NSLog(#"Button %ld clicked.", (long int)[button tag]);
// code here that picks the correct viewController to push to...
// for example tag 1 would create an instance of vcTwo.m and would then be pushed to the navigationController and be displayed on screen
}
say I have three UIViewController classes (vcOne.m, vcTwo.m, vcThree.m) and I want it so that when the button is pressed 'buttonClicked' is run and the code picks the corresponding viewController to push to. I don't want to use a series of if statements as there may be dozens/hundreds of viewControllers in the end. Would I have to instantiate all of the viewControllers and put them in an array? Is there a better way?
Are you using storyboards? so you can chose a segue according to the button tag:
int i = (int)[button tag];
[self performSegueWithIdentifier:[NSString stringWithFormat:#"Segue%d", i] sender:self];
or:
UIViewController *viewController= [controller.storyboard instantiateViewControllerWithIdentifier:NSString stringWithFormat:#"ViewControllerNumber%d", i];
In the end I went with this:
- (void) buttonClicked:(id)sender
{
NSLog(#"Button tag = %li", (long)[sender tag]);
FormularyVC *formularyVCInstance = [FormularyVC alloc];
ProceduresVC *proceduresVCInstance = [ProceduresVC alloc];
VetMedVC *vetMedVCInstance = [VetMedVC alloc];
NSArray *vcArray = [NSArray arrayWithObjects:formularyVCInstance, proceduresVCInstance, vetMedVCInstance, nil];
UIViewController *vcToLoad = [vcArray objectAtIndex:(int)[sender tag]];
vcToLoad.view.backgroundColor = [UIColor whiteColor];
[self.navigationController pushViewController:vcToLoad animated:NO];
}
I created an array of the ViewControllers I wanted to be able to load based on which button was pressed. When the button is pressed the method is run and the tag is taken as a parameter. This tag is used to find the ViewController needed by checking its location on the array's index.
I understand (I think) why this isn't working, but I don't know how to get it to work.
I have a method I call in viewWillAppear like so (I've simplified the code for the sake of this question):
- (void)viewWillAppear:(BOOL)animated
{
[self displayHour:0];
}
I then have a little for loop that builds some buttons in viewDidLoad:
NSUInteger b = 0;
for (UIButton *hourButton in hourButtons) {
[hourButton setTag:b];
[hourButton setTitle:[NSString stringWithFormat:#"%lu", (unsigned long)b] forState:UIControlStateNormal];
[hourButton addTarget:self action:#selector(showHour:) forControlEvents:UIControlEventTouchUpInside];
b++;
}
Then for the action I have this:
- (IBAction)showHour:(id)sender
{
// Logs 0, 1, etc. depending on which button is tapped
NSLog(#"Button tag is: %lu", (long int)[sender tag]);
// This currently throws this error:
// No visible #interface for 'UIView' declares the selector 'displayHour:'
// What I want to do is be able to call this method on
// the main view and pass along the button tag of the
// button that was tapped.
[mainView displayHour:(long int)[sender tag]];
}
THE QUESTION is how do I call displayHour to the main view from the showHour: action?
If you need it, the displayHour method looks like this (simplified, basically updates some labels and image views on the main view):
- (void)displayHour:(NSUInteger)i
{
// The temperature
hourTemp = [[hourly objectAtIndex:i] valueForKeyPath:#"temperature"];
// The icon
hourIcon = [[hourly objectAtIndex:i] valueForKeyPath:#"icon"];
// The precipitation
hourPrecip = [[hourly objectAtIndex:i] valueForKeyPath:#"precipProbability"];
// SET DISPLAY
[hourTempField setText:hourTemp];
[hourIconFrame setImage:hourIcon];
[hourPrecipField setText:hourPrecip];
}
Thanks!
Instead of
[mainView displayHour:(long int)[sender tag]];
do:
[self displayHour:(long int)[sender tag]];
Lets say I have 10 buttons. For each button i want to pass some text... e.g. Button 1, My New Button 2, etc....
What I want to do is print this text in NSLog.
Hence I created one method and passed this to button. But I am not getting how can I pass data into it...
[myButton addTarget:self action:#selector(btnSelected:) forControlEvents:UIControlEventTouchUpInside];
-(IBAction)btnSelected:(id)sender {
NSLog(#"btnSelected data is %#", sender);
// I want to print some text for respective button here...
}
But I am not getting... any idea how to get this done?
You can associate data with objects as :
Firstly import this class : #import <objc/runtime.h>
Then create a key as
static char * kIndexPathAssociationKeySTR = "associated_string_key";
then associate string as :
** Here you can associate any type of data with button like : NSMutableArray or NSString etcetra**
NSString *myAttachedValue = #"This is the info I am associating with button";
objc_setAssociatedObject(self.testBtn,
kIndexPathAssociationKeySTR,
myAttachedValue,
OBJC_ASSOCIATION_RETAIN);
then access it in your method you called on button event as :
- (IBAction)btnTouched:(UIButton *)sender {
NSString *valueIs = (NSString *)objc_getAssociatedObject(self.testBtn, kIndexPathAssociationKeySTR);
NSLog(#"value is : %#",valueIs);
}
Hope it helps you.
set tag for the button
[myButton addTarget:self action:#selector(btnSelected:) forControlEvents:UIControlEventTouchUpInside];
myButton.tag = 1;
now compare tag
-(IBAction)btnSelected:(id)sender {
UIButton *button = (UIButton *)
if(button.tag == 1) { //do button 1 stuff
NSLog(#"btnSelected data is %#", sender);
}
}
Also you can probably checkout Blocks, extend a uibutton to support blocks (UIButton block equivalent to addTarget:action:forControlEvents: method?).
Try this,
[myButton setAccessibilityValue:#"Some text"];
[myButton addTarget:self action:#selector(btnSelected:) forControlEvents:UIControlEventTouchUpInside];
-(IBAction)btnSelected:(id)sender {
NSLog(#"btnSelected data is %#", [sender accessibilityValue];);
// I want to print some text for respective button here...
}
I have made 20 Buttons dynamically, and I got the tag values of all Buttons.
But I need to know how to use that tag values.
I need information on every button pressed with tag values.
So, how do I use those tag values?
You need to set target-action of each button.
[button setTarget:self action:#selector(someMethod:) forControlEvents:UIControlEventTouchUpInside];
Then implement someMethod: like this:
- (void)someMethod:(UIButton *)sender {
if (sender.tag == 1) {
// do action for button with tag 1
} else if (sender.tag == 2) {
// do action for button with tag 2
} // ... and so on
}
Why do you need to use the tag to get the button. You can directly get the buttons reference from its action method.
- (void)onButtonPressed:(UIButton *)button {
// "button" is the button which is pressed
NSLog(#"Pressed Button: %#", button);
// You can still get the tag
int tag = button.tag;
}
I hope you have added the target-action for the button.
[button addTarget:self action:#selector(onButtonPressed:)
forControlEvents:UIControlEventTouchUpInside];
You can get reference to that your buttons using that tags. For example, you've added UIButtons to UIView *mainView. To get reference to that buttons you should write following:
UIButton *buttonWithTag1 = (UIButton *)[mainView viewWithTag:1];
Set the tags like this :
for (createButtonIndex=0; createButtonIndex<buttonsCount; createButtonIndex++)
{
buttonCaps.tag=createButtonIndex;
}
And add the method to trap the tags :-
-(void)buttonsAction:(id)sender
{
UIButton *instanceButton = (UIButton*)sender;
switch(instanceButton.tag)
{
case 1(yourTags):
//Code
break;
case 2:
//Code
break;
}
}
Hope this Helps !!
- (IBAction)buttonPressed:(id)sender {
UIButton selectedButton = (UIButton *)sender;
NSLog(#"Selected button tag is %d%", selectedButton.tag);
}
usefully we use btn tag if You Write One Function For (more than one) Buttons .in action if we want to write separate Action For button at that situvation we use btn tag.it can get two ways
I) case sender.tag
//if we have four buttons Add,mul,sub,div having Same selector and add.tag=10
mul.tag=20,sub.tag=30,div.tag=40;
-(IBAction) dosomthing:(id)sender
{
int x=10;
int y=20;
int result;
if(sender.tag==10)
{
result=x+y;
}else if(sender.tag==20)
{
result=x*y;
}else if(sender.tag==30)
{
result=x-y;
}else if(sender.tag==40)
{
result=x/y;
}
NSLog(#"%i",result);
}
2)Case
UIButton *btn=[self.view viewWithTag:10];
then you got object of add button uyou can Hide It With btn.hidden=YES;
UIButton *btn = (UIButton *)[mainView viewWithTag:button.tag];
I am using addTarget:action:forControlEvents like this:
[newsButton addTarget:self
action:#selector(switchToNewsDetails)
forControlEvents:UIControlEventTouchUpInside];
and I would like to pass parameters to my selector "switchToNewsDetails".
The only thing I succeed in doing is to pass the (id)sender by writing:
action:#selector(switchToNewsDetails:)
But I am trying to pass variables like integer values. Writing it this way doesn't work :
int i = 0;
[newsButton addTarget:self
action:#selector(switchToNewsDetails:i)
forControlEvents:UIControlEventTouchUpInside];
Writing it this way does not work either:
int i = 0;
[newsButton addTarget:self
action:#selector(switchToNewsDetails:i:)
forControlEvents:UIControlEventTouchUpInside];
Any help would be appreciated :)
action:#selector(switchToNewsDetails:)
You do not pass parameters to switchToNewsDetails: method here. You just create a selector to make button able to call it when certain action occurs (touch up in your case). Controls can use 3 types of selectors to respond to actions, all of them have predefined meaning of their parameters:
with no parameters
action:#selector(switchToNewsDetails)
with 1 parameter indicating the control that sends the message
action:#selector(switchToNewsDetails:)
With 2 parameters indicating the control that sends the message and the event that triggered the message:
action:#selector(switchToNewsDetails:event:)
It is not clear what exactly you try to do, but considering you want to assign a specific details index to each button you can do the following:
set a tag property to each button equal to required index
in switchToNewsDetails: method you can obtain that index and open appropriate deatails:
- (void)switchToNewsDetails:(UIButton*)sender{
[self openDetails:sender.tag];
// Or place opening logic right here
}
To pass custom params along with the button click you just need to SUBCLASS UIButton.
(ASR is on, so there's no releases in the code.)
This is myButton.h
#import <UIKit/UIKit.h>
#interface myButton : UIButton {
id userData;
}
#property (nonatomic, readwrite, retain) id userData;
#end
This is myButton.m
#import "myButton.h"
#implementation myButton
#synthesize userData;
#end
Usage:
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];
Recieving function:
- (void) touchUpHandler:(myButton *)sender {
id userData = sender.userData;
}
If you need me to be more specific on any part of the above code — feel free to ask about it in comments.
Need more than just an (int) via .tag? Use KVC!
You can pass any data you want through the button object itself (by accessing CALayers keyValue dict).
Set your target like this (with the ":")
[myButton addTarget:self action:#selector(buttonTap:) forControlEvents:UIControlEventTouchUpInside];
Add your data(s) to the button itself (well the .layer of the button that is) like this:
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!
*Note: The key shouldn't be a default key of a CALayer property, consider adding a unique prefix to all of your keys to avoid any issues arising from key collision.
Then when the button is tapped you can check it like this:
-(void)buttonTap:(UIButton*)sender{
NSString *dataThatWasPassed = (NSString *)[sender.layer valueForKey:#"anyKey"];
NSLog(#"My passed-thru data was: %#", dataThatWasPassed);
}
Target-Action allows three different forms of action selector:
- (void)action
- (void)action:(id)sender
- (void)action:(id)sender forEvent:(UIEvent *)event
I made a solution based in part by the information above. I just set the titlelabel.text to the string I want to pass, and set the titlelabel.hidden = YES
Like this :
UIButton *imageclick = [[UIButton buttonWithType:UIButtonTypeCustom] retain];
imageclick.frame = photoframe;
imageclick.titleLabel.text = [NSString stringWithFormat:#"%#.%#", ti.mediaImage, ti.mediaExtension];
imageclick.titleLabel.hidden = YES;
This way, there is no need for a inheritance or category and there is no memory leak
I was creating several buttons for each phone number in an array so each button needed a different phone number to call. I used the setTag function as I was creating several buttons within a for loop:
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];
}
Then in my call: method I used the same for loop and an if statement to pick the correct phone number:
- (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]];
}
}
}
As there are many ways mentioned here for the solution, Except category feature .
Use the category feature to extend defined(built-in) element into your
customisable element.
For instance(ex) :
#interface UIButton (myData)
#property (strong, nonatomic) id btnData;
#end
in the your view Controller.m
#import "UIButton+myAppLists.h"
UIButton *myButton = // btn intialisation....
[myButton set btnData:#"my own Data"];
[myButton addTarget:self action:#selector(buttonClicked:) forControlEvents:UIControlEventTouchUpInside];
Event handler:
-(void)buttonClicked : (UIButton*)sender{
NSLog(#"my Data %#", sender. btnData);
}
You can replace target-action with a closure (block in Objective-C) by adding a helper closure wrapper (ClosureSleeve) and adding it as an associated object to the control so it gets retained. That way you can pass any parameters.
Swift 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)
}
}
Usage:
button.addAction(for: .touchUpInside) {
self.switchToNewsDetails(parameter: i)
}
There is another one way, in which you can get indexPath of the cell where your button was pressed:
using usual action selector like:
UIButton *btn = ....;
[btn addTarget:self action:#selector(yourFunction:) forControlEvents:UIControlEventTouchUpInside];
and then in in yourFunction:
- (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
..
}
since you get an indexPath it becames much simplier.
See my comment above, and I believe you have to use NSInvocation when there is more than one parameter
more information on NSInvocation here
http://cocoawithlove.com/2008/03/construct-nsinvocation-for-any-message.html
This fixed my problem but it crashed unless I changed
action:#selector(switchToNewsDetails:event:)
to
action:#selector(switchToNewsDetails: forEvent:)
I subclassed UIButton in CustomButton and I add a property where I store my data. So I call method: (CustomButton*) sender and in the method I only read my data sender.myproperty.
Example CustomButton:
#interface CustomButton : UIButton
#property(nonatomic, retain) NSString *textShare;
#end
Method action:
+ (void) share: (CustomButton*) sender
{
NSString *text = sender.textShare;
//your work…
}
Assign action
CustomButton *btn = [[CustomButton alloc] initWithFrame: CGRectMake(margin, margin, 60, 60)];
// other setup…
btnWa.textShare = #"my text";
[btn addTarget: self action: #selector(shareWhatsapp:) forControlEvents: UIControlEventTouchUpInside];
If you just want to change the text for the leftBarButtonItem shown by the navigation controller together with the new view, you may change the title of the current view just before calling pushViewController to the wanted text and restore it in the viewHasDisappered callback for future showings of the current view.
This approach keeps the functionality (popViewController) and the appearance of the shown arrow intact.
It works for us at least with iOS 12, built with Xcode 10.1 ...