How to overwrite a convenience constructor the proper way? - iphone

For example I want to overwrite from UIButton:
+ (id)buttonWithType:(UIButtonType)buttonType
So I would do:
+ (id)buttonWithType:(UIButtonType)buttonType {
UIButton *button = [UIButton buttonWithType:buttonType];
if (button != nil) {
// do own config stuff ...
}
return button;
}
is that the right way? Or did I miss something? (yeah, I have been overwriting thousands of instance methods, but never class methods ;) )

So you got recursion.
Unfortunately you can not create a button with the specified type using a method other than buttonWithType. If you need to somehow initialize button after creation, you can make your own static method:
+(id)buttonWithTypeEx:(UIButtonType)buttonType {
UIButton *button = [UIButton buttonWithType:buttonType];
if (button != nil) {
// do own config
}
return button;
}

Related

How to get instance variable name?

I created a simple iPhone screen with two UIButtons programmatically like below.
- (void)viewDidLoad
{
[super viewDidLoad];
UIButton *buttonOne = [UIButton buttonWithType:UIButtonTypeRoundedRect];
buttonOne.frame = CGRectMake(60, 70, 200, 40);
[buttonOne setTitle:#"One" forState:UIControlStateNormal];
[self.view addSubview:buttonOne];
UIButton *buttonTwo = [UIButton buttonWithType:UIButtonTypeRoundedRect];
buttonTwo.frame = CGRectMake(60, 250, 200, 40);
[buttonTwo setTitle:#"Two" forState:UIControlStateNormal];
[self.view addSubview:buttonTwo];
}
Now on press of button with title as "One", I want to get the variable name as "buttonOne", similarly on press of button with title as "Two" I want to get the variable name as "buttonTwo".
I am not finding a way to get the variable name. Any help? Thanks in advance
First off I'd like to disclaim that this is not good coding style. I assume you're doing this because of some special/unique case, or as a proof of concept. In a production app, this is NOT the way to go. You should set your buttons as properties/ivars and you can compare them when they're pressed, or you can assign tags, or separate targets/selectors for each button. Anything you can do to avoid this approach is good because to be honest this approach is kind of terrible (see note at the end of next paragraph about nil/0 values).
You can check out this code below from a previous SO answer - it will return the name of the ivar given the pointer. However, you have to declare your buttons as ivars and not local variables. Also, if two ivars are nil, it will report the same. So this will only work if all your object ivars are not nil, and your primitive type ivars are not 0.
#import <objc/objc.h>
- (NSString *)nameOfIvar:(id)ivarPtr
{
NSString *name = nil;
uint32_t ivarCount;
Ivar *ivars = class_copyIvarList([self class], &ivarCount);
if(ivars)
{
for(uint32_t i=0; i<ivarCount; i++)
{
Ivar ivar = ivars[i];
id pointer = object_getIvar(self, ivar);
if(pointer == ivarPtr)
{
name = [NSString stringWithUTF8String:ivar_getName(ivar)];
break;
}
}
free(ivars);
}
return name;
}
So add a method buttonPressed: as follows:
- (void)buttonPressed:(id)sender {
if ([sender isKindOfClass:[UIButton class]]) {
NSString *buttonName = [self nameOfIvar:sender];
//Now you can do something with your button name
}
}
Source of first block of code: Get property name as a string
we can get only button title name by this way buttonOne.titleLabel.text.
we can't get variable name

iPhone - call UISwitch that is generated in a UIView when a button is pressed

To clarify my question, my program has three lightbulb on the screen (Customized UIButton)
when any lightbulb is pressed, I programatically generate a UIView with a switch on it
when I turn on the switch, corresponding lightbulb will light up (change its background image)
However, I have trouble accessing this UISwitch since I can't declare it publicly
My code goes something like this:
#property buttonA;
#synthesize buttonA;//all three buttons have their background image set to 'off.png'
- (IBAction)lightBulbPressed:(UIButton *)sender
{
UIView *myView = [[UIView alloc] initWithFrame:CGRectMake(1,1, 64, 64)];
UISwitch *mySwitch = [[UISwitch alloc] initWithFrame:CGRectMake(0,0,64,64)];
[mySwitch addTarget:self action:#selector(onOrOff) forControlEvents:UIControlEventValueChanged];
[myView addSubview:mySwitch]
[self.view addSubview:myView];
}
So what troubles me is how to program the selector onOrOff, so that it knows which switch is being touched and change the background image of corresponding button accordingly.
Think about your method:
- (IBAction)lightBulbPressed:(UIButton *)sender {
// your method
}
You already know who called it. This piece of information is stored in sender.
So you can save it and use later in onOrOff
By the way, if you are using UISwitch you have to check
UIControlEventValueChanged
and not UIControlEventTouchUpInside.
EDIT: To pass your sender you can store its value to a NSString *buttonTapped declared in your .h file
- (IBAction)lightBulbPressed:(UIButton *)sender {
if (sender == bttOne) {
buttonTapped = #"ButtonOneTapped";
} else if (sender == bttTwo) {
buttonTapped = #"ButtonTwoTapped";
} else if (sender == bttThree) {
buttonTapped = #"ButtonThreeTapped";
}
// your method
}
- (void)onOrOff {
if ([buttonTapped isEqualToString:#"ButtonOneTapped"]) {
// Button One
} else if ([buttonTapped isEqualToString:#"ButtonTwoTapped"]) {
// Button Two
} else if ([buttonTapped isEqualToString:#"ButtonThreeTapped"]) {
// Button Three
}
}
One way to do so, is taht you give them distinct tag numbers in IB, and in - (IBAction)lightBulbPressed:(UIButton *)sender method, get their tag. e.g. NSInteger pressedButtonTag = [sender tag];, and go from there.
Also, instead of alloc/init myView every time user presses a button, you can add that view in IB, add the switch to it, put in the hierarchy of the owner but not the view, and set an outlet to it in .h. Call it whenever you need it, and again, access the switch by tag e.g. ( UISwitch *mySwitch = (UISwitch *)[myView viewWithTag:kSwitchTag]; ) and do whatever you want to do (on or off), add it to the subview and remove it later. This is more efficient.

The same button should do multiple task

I want one of my button to act different on different taps. Because its the same button i am using every time a particular action happens.
Is there a way to do this?
Thanks,
Add an additional UIGestureRecognizer ;)
Single tap is the action what will be linked but you can add other kind of gestures like double tap, swipe, etc.
You can use the tag property of button; so inside your IBAction method.
-(void)buttonClicked:(id)sender{
UIButton *button = (UIButton *)sender;
if (button.tag == 1) {
// perform your required functionality
button.tag = 2;
}
else if (button.tag == 2) {
// perform your required functionality
button.tag = 3;
}
else if (button.tag == 3) {
// perform your required functionality
button.tag = 1;
}
}
And don't forget to set initial tag value to 1.
If you want your button to act different you would create different methods to do the different actions. Then whenever you want the buttons behaviour to change you should set the button to handle the desired action.
So for the first action:
[button addTarget:self action:#selector(method1:) forControlEvents:UIControlEventTouchUpInside];
- (void) method1
{
//set button to handle method 2
[button addTarget:self action:#selector(method2:) forControlEvents:UIControlEventTouchUpInside];
}
- (void) method 2
{
}
The button just calls a method in your view controller when tapped. From there you do something like this:
if (internalState == FOO) {
[self doA];
} else {
[self doB];
}

UIButton sender title problem

seems like a quite easy problem but I don't get it.
It have two UIButtons, one is titled 'next' the other is titled 'previous'. Both are linked to the same method. All I wanna do is change the variable 'helpStatus'depending on which button is pressed:
if([sender currentTitle] == #"next"){
helpStatus++;
}
if ([sender currentTitle] == #"previous"){
helpStatus--;
}
NSLog(#"%#", [sender currentTitle]);
the logged titels are 'next' and 'previous' just like it should be but it doesn't work and I don't know why.
You need to use isEqualToString otherwise you are just comparing if they are the same object, not if they are equal :)
if([[sender currentTitle] isEqualToString:#"next"]){
helpStatus++;
}
if ([[sender currentTitle] isEqualToString:#"previous"]){
helpStatus--;
}
I would not use the string. What happens if you decide to change the label? Consider using a tag instead.
e.g. b
button.tag = 100;
...
- (void)buttonPressed:(id)sender {
UIButton *button = (UIButton*)sender;
if(button.tag == 100) {
}
}
or in your case (I am kidding, sort of), even:
button1.tag = 1;
button2.tag = -1;
...
- (void)buttonPressed:(id)sender {
UIButton *button = (UIButton*)sender;
helpStatus+= button.tag;

How can I pass a parameter to this function?

I have the following code:
[replyAllBtn addTarget:self.target action:#selector(ReplyAll:) forControlEvents:UIControlEventTouchUpInside];
- (void)replyAll:(NSInteger)tid {
// some code
}
How can I send a parameter to the ReplyAll function?
The replyAll method should accept (id)sender. If a UIButton fired the event, then that same UIButton will be passed as the sender. UIButton has a property "tag" that you can attach your own custom data to (much like .net winforms).
So you'd hook up your event with:
[replyAllBtn addTarget:self.target action:#selector(ReplyAll:) forControlEvents:UIControlEventTouchUpInside];
replyAllBtn.tag=15;
then handle it with:
(void) ReplyAll:(id)sender{
NSInteger *tid = ((UIControl*)sender).tag;
//...
A selector function will normally be defined as such:
- (void) ReplyAll:(id)sender;
So the only parameter an action will ever receives is the actual control that called it.
You could just add a property to your control that can be read in replyAll
If you want to send an int value, set the tag of the button = the int value you want to pass. Then you can access the tag value of the button to get the int you wanted.
NSInteger is not a pointer. Try this
NSInteger tid = sender.tag;
It's working now :D.
{
NSInteger tid = [sender tag];
}
The MVC model used in Cocoa works differently. Basically, the idea is that a control (=view) such as a button only lets a function know it was pressed, not knowing what this means. The function then has to know all the dynamics and dependencies. In your case, it's the function that has to find the parameter. To accomplish that, you'll "bind" other objects to the function (= controller).
I suggest you read a few Cocoa tutorials first if you want to get ahead with iPhone programming.
There's a few good ways to do this. The two most commonly implemented would be to have the controller (who's receiving the action) know about possible senders, or having the sender itself have a method that you end up using to determine the proper behavior.
The first (my preferable way, but it's easy to argue the opposite) would be implemented like such:
#interface Controller : NSObject {
UIButton *_replyToSender;
UIButton *_replyToAll;
}
- (void)buttonClicked:(id)sender;
#end
#implementation Controller
- (void)buttonClicked:(id)sender {
if (sender == _replyToSender) {
// reply to sender...
} else if (sender == _replyToAll) {
// reply to all...
}
}
#end
The second way would be implemented in a manner such as:
typedef enum {
ReplyButtonTypeSender = 1,
ReplyButtonTypeAll,
} ReplyButtonType;
#interface Controller : NSObject {
}
- (void)buttonClicked:(id)sender;
#end
#interface MyButton : UIButton {
}
- (ReplyButtonType)typeOfReply;
#end
#implementation Controller
- (void)buttonClicked:(id)sender {
// You aren't actually assured that sender is a MyButton, so the safest thing
// to do here is to check that it is one.
if ([sender isKindOfClass:[MyButton class]]) {
switch ([sender typeOfReply]) {
case ReplyButtonTypeSender:
// reply to sender...
break;
case ReplyButtonTypeAll:
// reply to all...
break;
}
}
}
#end