Casting sender parameter

10,235

Solution 1

My first response is that it's probably a mistake to do so. UIButton and UIImageView are distinct subclasses of UIView (and UIButton is a subclass of UIControl) so the only things they have in common are UIView methods. If sender is really a UIButton, treating it as a UIImageView is likely to result in errors, such as "unrecognized selector sent to instance" and the like.

In general, a cleaner solution is to create a separate method that is the action for a UIImageView. (Unlike Java, Objective-C and Cocoa prefer to not shoehorn UI response code into a single method based on the event type — rather, you organize the code logically based on the operation that needs to happen.)

If you must call this method from both buttons and image views, you can separate the logic like this:

- (IBAction) startClick:(id)sender {
  if ([sender isKindOfClass:UIButton]) {
    ...
  }
  else if ([sender isKindOfClass:UIImageView]) {
    ...
  }
}

Solution 2

I seem to think you would have done this.

UIButton *button = (UIButton*) id;
// then do all you button code here.

This should work. Otherwise, it might be better to change the function to

 (IBAction)startClick:(UIButton *)theButton

This way you want have to cast the button.

Solution 3

Do you have an outlet for your button? Is the button the only UI element which will send -startClick:? If so, then write the action method like this:

- (IBAction)startClick:(id)sender { 
    NSParameterAssert(sender == self.startClickButton);
    if (sender == self.startClickButton) {
        UIButton *button = self.startClickButton;
        [...]
    }
}

If you don't have an outlet/property for the button, you can test the sender's class, and cast if appropriate.

- (IBAction)startClick:(id)sender { 
    NSParameterAssert([sender isKindOfClass: [UIButton class]]);
    if ([sender isKindOfClass: [UIButton class]]) {
        UIButton *button = (UIButton *)sender;
        [...]
    }
}

Finally, it is permissible (at compile time) to send any message to an id typed value. (You'll get an exception at runtime if the receiver doesn't respond to that message.) You cannot, however, use dot syntax.

So if you know that the sender is always a button, you can write your method without casting at all. You'll have to use messages send syntax instead of dot syntax.

[button setAnimationImages: [NSArray ...]];
[button setAnimationDuration: 1];

This is important to understand - dot syntax is equivalent to message send syntax - it isn't mystical.

Share:
10,235
Bramble
Author by

Bramble

Updated on June 04, 2022

Comments

  • Bramble
    Bramble almost 2 years

    I have a method that accepts a sender and is called with a UIbutton. How do I get that object to casted into a UIImageView? Basically how do I re-use the code for the button.

    - (IBAction)startClick:(id)sender { 
        button.animationImages = [NSArray arrayWithObjects:
                                           [UIImage imageNamed:@"Pic_1.png"],
                                           [UIImage imageNamed:@"Pic_2.png"],
                                           [UIImage imageNamed:@"Pic_3.png"],
                                           nil];
        [button setAnimationRepeatCount:1];
        button.animationDuration = 1;
        [button startAnimating];
    }
    
  • Quinn Taylor
    Quinn Taylor almost 15 years
    The latter suggestion probably won't work. IB action methods always have return type "IBAction" (which is defined as void) and the parameter is of type id.
  • chostDevil
    chostDevil about 12 years
    if i was able to use - (IBAction) i couldn't have posted here :) but the problem is that , this button is in different class, i can't make an IBAction for it in that class
  • Priebe
    Priebe about 10 years
    It should be "if ([sender isKindOfClass:[UIButton class]]) { ... }". With square brackets and 'class' included.