Programmatically Creating Controls in Cocoa - objective-c

According to Cocoa Programming for Mac OS X, 3rd Edition, on page 245 (chapter 17), you will usually create views in Interface Builder. However, it is possible to create them in code, a la:
NSView *superview = [window contentView];
NSRect frame = NSMakeRect(10, 10, 200, 100);
NSButton *button = [[NSButton alloc] initWithFrame:frame];
[button setTitle:#"Click me!"];
[superview addSubview:button];
[button release];
That’s all well and good, but how would I wire up said control’s outlets to actions in code? (In .NET, this is an easy thing; add a delegate ... I’m hoping it’s similarly easy in Cocoa/Obj-C.)

You can wire them up using a simple assignment. To continue your code from above:
[button setTarget: self];
[button setAction: #selector(myButtonWasHit:)];

And if you want to target the first responder rather than a particular object:
[button setTarget:nil];
[button setAction:#selector(myAction:)];

Swift:
button.target = self
button.action = "myAction:"
And of course add myAction function to self:
func myAction(sender: NSButton) {
println("click!")
}

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".

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.

Can you hard code IBActions and IBOutlets, rather than drag them manually in Interface Builder?

Is it possible to to hard code the IBActions and IBOutlets in code, instead of drag-connecting them in Interface Builder?
Yes it is poosible...
sample code
UIButton *btnDetail = [UIButton buttonWithType:UIButtonTypeRoundedRect] ;
[btnDetail setFrame:CGRectMake(250.0f, 15.0f, 65.0f, 20.0f)] ;
//btnDetail.backgroundColor = [UIColor grayColor];
[btnDetail setTitle:#"Detail" forState:UIControlStateNormal];
[btnDetail setTitleColor: [UIColor redColor] forState: UIControlStateNormal];
[btnDetail.titleLabel setFont:[UIFont fontWithName:#"Verdana" size:12]];
[btnDetail setBackgroundColor:[UIColor clearColor]];
[btnDetail sizeThatFits:btnDetail.frame.size];
[self.view addSubview:btnDetail];
//IBAction
[btnDetail addTarget:self
action:#selector(ShowSavingAccountDetail:)
forControlEvents:UIControlEventTouchUpInside];
[btnDetail setImage:[UIImage imageNamed:#"btn-detail.png"] forState:UIControlStateNormal];
The concept and sole purpose of IBAction and IBOutlet is to provide Interface Builder with means to connect the xib with your code.
If you don't want to use Interface Builder with your code, you don't need IBAction or IBOutlet, you need them ONLY to use objects (buttons, textfields, etc.) that were instanciated in your xib from your classes.
With that said, mihirpmehta's answer is the correct way to programmatically add UI elements to your view and add actions to them.

UIButton like in UIActionSheet

I would like put UIButton on my UIViewController with style like have buttons in UIActionSheet. What should i add here:
UIButton *button = [UIButton buttonWithType: UIButtonTypeCustom];
[ button setFrame: CGRectMake(10, 10, 50, 35) ];
[button setTitle:#"Button from UIActionSheet" forState: UIControlStateNormal];
[self addSubview: button];
You need to get the background image from somewhere and then set it using: - (void)setBackgroundImage:(UIImage *)image forState:(UIControlState)state
I believe the .psd linked here has the action button background image http://media.photobucket.com/image/uiactionsheet%20button%20psd/visionwidget/iphone-gui-psd-file.jpg
you can using segmented control - very good trick
segmented = [[[UISegmentedControl alloc] initWithItems:[NSArray arrayWithObjects:#"Info",#"Configuration", nil]] autorelease];
[segmented addTarget:self action:#selector(changeView:) forControlEvents:UIControlEventValueChanged];
segmented.tintColor = [UIColor colorWithRed:0.20 green:0.20 blue:0.52 alpha:1.0];
segmented.segmentedControlStyle = UISegmentedControlStyleBar;
Maybe exists some simple way like set some property?
Here is a post on using Apple's private UIGlassButton class.
You can't submit an app like this to the app store, but you can capture the generated background image to reuse in your published apps.