UIButton addTarget: self ... causes "Does not recognize selector" error - objective-c

I initialised my UIButton-deriver liek this:
Button * it = [[Button alloc] initWithFrame:CGRectMake(x, y, image.size.width, image.size.height)];
Then, I do the next:
[(UIButton *)self addTarget:self action:#selector(buttonClicked:) forControlEvents:UIControlEventTouchUpInside];
[view addSubview:self];
The first line causes "Does not recognize selector" error.
Selector buttonClicked: looks like this:
-(IBAction) buttonClicked:(id)sender {
if (action) action();
else NSLog(#"Clicked.\n");
}
What am I doing wrong?

Add the action with:
[it addTarget:self
action:#selector(buttonClicked:)
forControlEvents:UIControlEventTouchUpInside];
and then add the button with
[view addSubview:it];
And don't create UIButtons with init... use the class method + buttonWithType:.
Be careful to to subclass UIButton, I am not sure if you really want this. Have a look at create uibutton subclass.

You create buttons with +(id)buttonWithType:(UIButtonType)buttonType

Related

need Clarification on NSButton Creation and Usage

I have below code to create simple NSButton in a separate function
-(void)myFunction
{
NSButton *btn = [self createButton:#"Button_Name"];
if(some condition )
{
[btn setEditable:YES];
}
}
- (NSButton*)createButton:(NSString *)buttonName
{
NSButton *btn = [[NSButton alloc] initWithFrame:NSMakeRect(20, 0, 20, 20)];
[btn setButtonType:NSSwitchButton];
[btn setImagePosition:NSImageOnly];
[btn setTarget:self];
[btn setTitle: buttonName];
return btn;
}
In my same It is working fine.I am using this code in a Big project.Will It work normally or will cause some problem.Is this a correct way?
Few things I would like to bring in your notice:
You pass buttonName and buttonTitle but never uses it.
You create an object of type NSButton but your object name is against the convention, by reading btnCell someone will expect it to be NSButtonCell.
In the above code I cant see any reference to the newly created button and even you are not adding it to any view. (I hope in your real Big project you are not missing those.)

Changing the colour of a UIButton based on its tag

Got a bunch of UIButtons, some of which need to change colour depending on circumstance, and currently it is handled like thus:
UIButton *button;
button = [self.view viewWithTag:positionInArray];
[button setBackgroundColor:[UIColor cyanColor]];
button = [self.view viewWithTag:positionInArray-1];
[button setBackgroundColor:[UIColor cyanColor]];
button = [self.view viewWithTag:positionInArray+3];
[button setBackgroundColor:[UIColor cyanColor]]
button = [self.view viewWithTag:positionInArray+4];
[button setBackgroundColor:[UIColor cyanColor]];
It works, but the code that sets a button to a tag throws this warning:
"Incompatible pointer types initializing 'UIButton *__strong' with an expression of type 'UIView *'"
How would I go about doing this correctly?
The issue is, that viewWithTag: may return any subclass of UIView. If you know that it will return a UIButton for sure you can cast it like this:
button = (UIButton *)[self.view viewWithTag:positionInArray];
This will hide the warning, but may produce unexpected result when the view isn't a button! A nicer solution would be to check if the returned UIView subclass is a UIButton:
UIView *view = [self.view viewWithTag:positionInArray];
if ([view isKindOfClass:[UIButton class]]) {
button = (UIButton *)view;
[button setBackgroundColor:[UIColor cyanColor]];
} else {
NSLog(#"Ooops, something went wrong! View is not a kind of UIButton.");
}
The problem is that viewWithTag: returns a UIView because it can be of any subclass of UIView, including UIButton.
This is design dependent, if you don't have any other subview that has this tag, then you should simply cast the result to a UIButton like the other answers and be done with it :)
You need to cast your UIViews to UIButtons like this:
button = (UIButton *)[self.view viewWithTag:positionInArray];
It is best though to verify that your views are actually buttons by doing something like:
UIView *button = [self.view viewWithTag:positionInArray];
if ([button isKindOfClass[UIButton class]] {
[button setBackgroundColor:[UIColor cyanColor]];
}
In this example there is no need to caste to UIButton because UIViews have this method too. You would just want the if-statement if you only wanted to change UIButton's colors.
Downcasting alternative
viewWithTag: returns a UIView, but it may point to a whatever subclass of UIView object.
Since polymorphism is valid, and the messaging is dynamic, you can just do that:
UIView *button;
button = [self.view viewWithTag:positionInArray];
[button setBackgroundColor:[UIColor cyanColor]];
You inherit the backgroundColor from UIView, so there isn't any problem.
However you can always use the type id, which is a kind of "jolly".

How to create a action to a button in Cocoa programmatically?

I'm trying to create a button in Cocoa in mac programmatically, but I don't know how to put a action on this, I'm trying like this:
NSRect frame = NSMakeRect(10, 200, 80, 100);
NSButton *btn = [[NSButton alloc]initWithFrame:frame];
[btn setButtonType:NSMomentaryPushInButton];
[btn setBezelStyle:NSRoundedBezelStyle];
[btn setTitle:#"Click me"];
[btn setAction:#selector(hideLabels:)];
[view addSubview:btn];
but the line [btn setAction:#selector(hideLabels:)]; don't work, how can I create a action here?
the method hideLabels is in function, because I used it with another button.
Does your hideLabels method take an argument?
if not, leave off the : from the selector

Selector is not called

I can't understand why the selector is not called.
//EDITED
[self.scrollView setContentSize:CGSizeMake(320, 600)];
UIButton *btn = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[btn addTarget:self
action:#selector(validateTextFields:)
forControlEvents:UIControlEventTouchUpInside];
[btn setTitle:#"Продължи" forState:UIControlStateNormal];
btn.frame = CGRectMake(55, 580, 210, 50);
[self.scrollView addSubview:btn];
-(IBAction)validateTextFields:sender
{
NSLog(#"Called");
}
When I touch the button "Called" is not logged in console. If I change UIControlEventTouchUpInside to UIControlEventTouchDown validateTextFields method is executed.
You are adding a button on UIScrollView, this might be creating problem. Please check this question: iPhone: adding button to scrollview makes button inaccessible to interaction. It may be helpful.
The type for action should be
- (IBAction)validateTextFileds:(id)sender
{
// do your stuff
}
and your #selector parameter in addTarget should look like this: #selector(validateTextFields:)
try this:
[addContact addTarget:self action:#selector(buttonPressed:)forControlEvents:UIControlEventTouchUpInside];
-(IBAction)buttonPressed:(id)sender{
}

Adding context to a UI control or NSObject

It's great to be able to addTarget on a UIButton. I only wish there was some sneaky way I could attach state to the UIButton so that when the target method is invoked, I could magically pull that state (any id) from the sender.
Something like:
[button shoveMyObjectInThere:foo];
[button addTarget:self action:#selector(touchyTouchy:) forControlEvents:UIControlEventTouchUpInside];
Followed by:
-(void) touchyTouchy:(id) sender {
UIButton button = (UIButton*)sender;
id foo = [button getByObjectBack];
// do something interesting with foo
}
Would be great if UIButton had an 'id context' property where developers could shove stuff, but that doesn't seem to be the case. Objective-C is a very dynamic language though, so I wonder if there is some sneaky way I can add method or fields to an object at runtime?
You could try making an associative reference
#import <objc/runtime.h>
objc_setAssociatedObject(id object, void *key, id value, objc_AssociationPolicy policy);
objc_getAssociatedObject(id object, void *key);
What about something like setValue:forKey:, a part of the Key-Value coding feature of Objective-C?
So I just did a quick test and found some interesting results.
UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[button setTitle:#"Hello" forState:UIControlStateNormal];
[button addTarget:self action:#selector(buttonClicked:) forControlEvents:UIControlEventTouchUpInside];
button.frame = CGRectMake(100, 100, 100, 100);
[self.window addSubview:button];
// ...
- (void)buttonClicked:(id)sender {
NSLog(#"button clicked %#", [sender class]);
}
This prints: button clicked UIRoundedRectButton
So it seems like this should be possible... Truth be told, I ran into some problems subclassing UIButton to get the full example working but this seems promising. :)
The official solution is to use the "tag" property:
[self.someMutableArray addObject:foo];
button.tag = self.someMutableArray.count - 1;
[button addTarget:self action:#selector(touchyTouchy:) forControlEvents:UIControlEventTouchUpInside];
Then:
-(void) touchyTouchy:(id) sender {
UIButton button = (UIButton*)sender;
id foo = self.someMutableArray[button.tag];
// do something interesting with foo
}
In most situations you'd use an enum or constant for the tag, but an array is obviously more flexible.