Passing selector to a custom method to create UIButton programmatically - ios

I am using XCode 5 under ios 7 to do a simple project. And I get the following error when I press a button that is created programmatically:
2013-11-10 09:16:02.969 Project2[446:70b] +[KNSunDynamicUIButton buttonSavePressed:]: unrecognized selector sent to class 0x221424
Details:
I create a custom method that is used to create a UIButton object programmatically. That custom method is used in any view controller. That custom method needs passed-in parameters like x, y, width, height, button title, and selector that is an event handler and passed in like "(SEL)selector".
Custom method (belongs to a helper class):
+(UIButton*)kNSunSetupWithSelector:(SEL)selector withX:(int)x y:(int)y width:(int)width height:(int)height
buttonTitle:(NSString*)buttonTitle backGroundColor:(CGColorRef) backGroundColor
{
UIButton* button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[button setTitle:#"Save" forState:UIControlStateNormal];
button.frame = CGRectMake(x, y, width, height);
button.backgroundColor = [UIColor greenColor];
// selector is used over here
[button addTarget:self
action:selector
forControlEvents:UIControlEventTouchDown];
return button;
}
And then in a view controller .m file, I call that custom method like:
-(void)setUpButtons
{
SEL selector = #selector(buttonSavePressed:);
_buttonSave = [KNSunDynamicUIButton kNSunSetupWithSelector:selector withX:470 y:410 width:160 height:40 buttonTitle:#"Save" backGroundColor:(_buttonSaveColor)];
[self.view addSubview:_buttonSave];
}
#interface MyViewController ()
{
...
// buttons
UIButton* _buttonSave;
}
#end
And the definition of button's event handler in the same view controller .m file like:
- (void)buttonSavePressed:(UIButton*)button
{
NSLog(#"Button Save clicked.");
}
When I run my code and hit over the button, I see the exception as mentioned above. Please help thank you.
P.S. If I rewrite the custom method as an alternative that does not have "(SEL)selector" parameter in its signature, and let controller view, who calls that custom method, does the job of coding selector, then there is no exception:
-(void)setUpButtons
{
//Note: the codes are in my view controller .m file
_buttonSave = [KNSunDynamicUIButton kNSunSetupWithX:470 y:410 width:160 height:40 buttonTitle:#"Save" backGroundColor:_buttonSaveColor];
// Note: selector coding is taken care by codes of my view controller instead of by custom method
[_buttonSave addTarget:self
action:#selector(buttonSavePressed:)
forControlEvents:UIControlEventTouchDown];
[self.view addSubview:_buttonSave];
[_buttonSave setupView];
}
And the alternative custom method (I do not like the method because it does not take care of dynamic passed-in selector):
+(UIButton*)kNSunSetupWithX:(int)x y:(int)y width:(int)width height:(int)height
buttonTitle:(NSString*)buttonTitle backGroundColor:(CGColorRef) backGroundColor
{
UIButton* button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[button setTitle:#"Save" forState:UIControlStateNormal];
button.frame = CGRectMake(x, y, width, height);
button.backgroundColor = [UIColor greenColor];
return button;
}

The problem is line in helper class:
[button addTarget:self action:selector forControlEvents:UIControlEventTouchDown];
Here you said that the helper class will have method you're passing through selector, in you're case buttonSavePressed: method.
You should add inController parameter in you're helper method to tell in which controller is selector method placed.
In your particular case, the helper method should looks something like this (Note that inController parameter is added right after selector parameter):
+(UIButton*)kNSunSetupWithSelector:(SEL)selector
inController:(UIViewController*)controller
withX:(int)x
y:(int)y
width:(int)width
height:(int)height
buttonTitle:(NSString*)buttonTitle
backGroundColor:(CGColorRef) backGroundColor
{
UIButton* button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[button setTitle:#"Save" forState:UIControlStateNormal];
button.frame = CGRectMake(x, y, width, height);
button.backgroundColor = [UIColor greenColor];
// selector is used over here
[button addTarget:controller
action:selector
forControlEvents:UIControlEventTouchDown];
return button;
}
and you should call it, from your controller this way:
-(void) setupButtons
{
SEL selector = #selector(buttonSavePressed:);
_buttonSaveColor = [UIColor orangeColor].CGColor;
_buttonSave = [KNSunDynamicUIButton kNSunSetupWithSelector:selector
inController:self
withX:470
y:410
width:160
height:40
buttonTitle:#"Save"
backGroundColor:(_buttonSaveColor)];
[self.view addSubview:_buttonSave];
}

Related

Seeing the UIButton in another void - iOS

I have set a UIButton like this :
UIButton *learnmorebutton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
and I have
[learnmorebutton addTarget:self action:#selector(myAction) forControlEvents:UIControlEventTouchUpInside];
and i have a void
- (void)myAction
{
}
but i cannot see learnmorebutton in the void, and thats because #property.
is this correct ?
All UIControls pass themselves as a "sender" to their target action methods IF the selector you give them takes 1 parameter.
Make these changes:(note the colon after the myAction selector on the first line)
[learnmorebutton addTarget:self action:#selector(myAction:) forControlEvents:UIControlEventTouchUpInside];
- (void)myAction:(UIButton *)sender
{
//sender is the button that was tapped
sender.backgroundColor = [UIColor redColor]; //or whatever color you wanted here.
}

Why isEqual: does not work for a property UIButton?

I have got a problem:
I have set up a property:
#property (strong, nonatomic) UIButton *buttonX;
Also I have a method to create a button:
-(void)createChooseButton : (UIButton *) button
{
button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
button.frame = CGRectMake(0, 0, 50, 50);
button.layer.borderWidth = 1;
button.layer.borderColor = [[UIColor greenColor] CGColor];
if ([button isEqual:self.buttonX]) {
NSLog(#"Hello");
[button setTitle:#"X" forState:UIControlStateNormal];
button.center = CGPointMake(self.view.frame.size.width/3, self.view.frame.size.height*2/3);
}
[self.view addSubview:button];
}
When I call this method from viewDidLoad, it does not work, but if I don't use If statement (with isEqual) everything is ok.
Please could you help me, what am I doing wrong?
isEqual will not work here, because you have two pointer here one point to button & second to buttonX. if you need to check the button , give a tag property to buttonX in didload & then check it in createChooseButton method.
When you pass thebuttonX in first place it's nil.
You have toalloc it and then call the method.
you should call
_buttonX = [UIButton buttonWithType:UIButtonTypeRoundedRect];
and then call
[createChooseButton:self.buttonX]
don't call buttonWithType:UIButtonTypeRoundedRect in the method

Setting selectors for global UIButton

I want to return a global UIButton from a global class.
+(UIBarButtonItem *) globalButton {
UIButton *myButton = [UIButton buttonWithType:UIButtonTypeCustom];
myButton.frame = CGRectMake(0.0, 0.0, 40, 40);
[myButton addTarget:self action:#selector(???)
forControlEvents:UIControlEventTouchUpInside];
return myButton;
}
So if I call [Global globalButton] from my viewController, I can get this button. The problem I'm having is how to set the selector, if I'm calling if from my view controller?
You should change your code as follow, assuming you would be calling it from UIViewController
+(UIButton *) globalButton:(UIViewController*)delegate {
UIButton *myButton = [UIButton buttonWithType:UIButtonTypeCustom];
myButton.frame = CGRectMake(0.0, 0.0, 40, 40);
if([delegate respondsToSelector:#selector(gloBalButtonTapped:)])
[myButton addTarget:delegate action:#selector(gloBalButtonTapped:) forControlEvents:UIControlEventTouchUpInside];
return myButton;
}
//Below method should be present in your view controller than only it will be added to myButton click event.
-(IBAction)gloBalButtonTapped:(id)sender{
}
You should return UIButton only if you are creating UIButton, if you need to return UIBarButtonItem than you should create UIBarButtonItem or Xcode will give warning, just a suggestion though.
Just dont add target in Global Class wherever you want to use it just use it like this.
UIBarButtonItem *btn = [Global globalButton];
btn addTarget:self action:#selector(yourSelector)
forControlEvents:UIControlEventTouchUpInside];
Or you can make globalButton a property in GlobalClass and write this method in setGlobalButton and then you can use it as a property in any viewController.
Or if you want to control it in Global only then you can mention your selector mentioned in this class only. And in that method you can control the behaviour.
Try this in your controller:
UIBarButtonItem* button = [UIBarButtonItem globalButton];
[button addTarget:self action:#selector(yourDidTapAction:) forControlEvents:UIControlEventTouchUpInside];
or this in globalButton:
+(UIBarButtonItem*)globalButtonWithTarget:(id)target andAction:(SEL)action
{
// ...
[myButton addTarget:target action:action
forControlEvents:UIControlEventTouchUpInside];
// ...
}
I found the easiest way to do this is below:
+(UIBarButtonItem *) buttonWithSelector:(SEL)selector andSender:(id)sender {
UIButton *myButton = [UIButton buttonWithType:UIButtonTypeCustom];
myButton.frame = CGRectMake(-40.0, 10.0, 40, 40);
[myButton addTarget:sender action:selector forControlEvents:UIControlEventTouchUpInside];
return myButton;
}

Button #selector not working if button defined in NSObject

I have the following problem: I am trying to define a button in an NSObject (in a separate file) and add it to my ViewController:
NSObject method:
in obj.h:
+(void)NewButtonInView:(UIView *)view withAction:(SEL)actionbutton;
in obj.m
+(void)NewButtonInView:(UIView *)view withAction:(SEL)actionbutton {
UIButton *button = [[UIButton alloc] init];
button.frame = CGRectMake(0, 0, 50, 50);
[button addTarget:self action:actionbutton forControlEvents:UIControlEventTouchUpInside];
}
in my ViewCotroller I import the obj.h and:
[obj NewButtonInView:[self view] withAction:#selector(actionB:)];
and:
-(void)actionB:(UIButton *)button {
//some code
}
The button looks fine but when I click it I get the following error:
"[obj actionB:]: unrecognized selector sent to class"
Any help is appreciated.
Thank you.
the problem is this line
[button addTarget:self action:actionbutton forControlEvents:UIControlEventTouchUpInside];
specifically, the use of self. You put the actionB: method in the viewController, but because the NewButtonInView:withAction: method is in a class method of obj self refers to the class obj. To fix it pass in the view controller as a parameter to the method like this
+(void)NewButtonInView:(UIView *)view withAction:(SEL)actionbutton target: (id) target {
UIButton *button = [[UIButton alloc] init];
button.frame = CGRectMake(0, 0, 50, 50);
[button addTarget: target action:actionbutton forControlEvents:UIControlEventTouchUpInside];
}
Try defining it with a return value, as follows:
+(UIButton *)NewButtonInView:(UIView *)view withAction:(SEL)actionbutton withTarget:(id)target{
UIButton *button = [[UIButton alloc] init];
button.frame = CGRectMake(0, 0, 50, 50);
[button addTarget:target action:actionbutton forControlEvents:UIControlEventTouchUpInside];
return button;
}
Then when you call it:
UIButton *button = [obj NewButtonInView:[self view] withAction:#selector(actionB:) withTarget:self];
Not sure why you're doing it in an NSObject instead of in your view controller, but that's the way to go about it if you do.

Objective C how to refer to a view in a class method

My app has multiple views to add buttons programmatically and instead of having the following instance method in each controller, I would like to put the method in a common class as use it as a class method.
+ (void) addLeftImageButton:(UIButton *)leftButton:(float)x:(float)y:(NSString *)name:(int) tag
{
leftButton = [UIButton buttonWithType:UIButtonTypeCustom];
[leftButton addTarget:self action:#selector(buttonPressed:) forControlEvents:UIControlEventTouchUpInside];
[leftButton setTag: tag];
[leftButton setBackgroundImage:[UIImage imageNamed:name] forState:UIControlStateNormal];
leftButton.frame = CGRectMake(x, y, 345.0, 345.0);
[self.view addSubview:leftButton];
}
The question I have is how to refer self.view to the calling view. as self.view is not known in the common class.
You can't, a class method has no concept of self. You need to pass the view as a parameter.
+ (void)addLeftImageButton:(UIButton *)leftButton:(float)x:(float)y:(NSString *)name:(int) tag forView:(UIView *)view;
My personal recommendation is to leave the step of adding the subview to the controller itself, making this the class method:
+ (UIButton *) leftButtonWithImageName:(NSString *)imageName x:(CGFloat)x y:(CGFloat)y tag:(int)tag
{
UIButton *leftButton = [UIButton buttonWithType:UIButtonTypeCustom];
[leftButton setTag: tag];
[leftButton setBackgroundImage:[UIImage imageNamed:name] forState:UIControlStateNormal];
leftButton.frame = CGRectMake(x, y, 345.0, 345.0);
return leftButton;
}
In a view controller:
UIButton *leftButton = [MyClass leftButtonWithImageName:#"image.png" x:0 y:0 tag:10];
[leftButton addTarget:self action:#selector(buttonPressed:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:leftButton];
A few reasons behind this:
If you're using this for multiple view controllers, your class method doesn't have to assume that they all implement the same buttonPressed: method, which isn't a safe assumption.
To someone just looking at your view controller code, it will look like nothing calls the buttonPressed:.
Callers of the leftButton... method will have more control over the button. For example, they might want to add the button as a property in viewDidLoad so its properties (like its frame) can be queried, but not actually add it to the screen until after an animation completes.
You might also consider making all the view controllers a subclass of a superclass, but that's a very implementation specific decision.
If this is a common method for all your controllers you should add it as an instance method in a base controller that all your other controllers inherit from.
Also, there are a few of weird things about that method:
Why do you pass leftButton as a parameter when the first thing you do is overwriting its value?
Your method declaration is really weird. Did you read the warnings? I think you want it to look more like this:
- (void)leftButtonWithImageName:(NSString *)name
x:(CGFloat)x
y:(CGFloat)y
tag:(int)tag
{
UIButton *leftButton = [UIButton buttonWithType:UIButtonTypeCustom];
[leftButton addTarget:self
action:#selector(buttonPressed:)
forControlEvents:UIControlEventTouchUpInside];
[leftButton setTag:tag];
[leftButton setBackgroundImage:[UIImage imageNamed:name]
forState:UIControlStateNormal];
leftButton.frame = CGRectMake(x, y, 345.0, 345.0);
[self.view addSubview:leftButton];
}
There are several ways to implement this . But a common class is not good one.
The best way is that you put this function as a category of UIViewController. This is instant method (-)but not class method(+)
You don't need to change others.
#import <UIKit/UIKit.h>
#interface UIViewControl (labels)
-(void) add left...
#end
#implementation UIViewControl (labels)
-(void) add left...{}
#end
Put above code in a file "labelcontrol.h" and "label control.m"
Then in your any viewcontroller.m
Add: import "labelcontrol.h" to make it work

Resources