Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

Hawkeye75

macrumors member
Original poster
Mar 29, 2010
32
0
Hi folks, I'm still new to Cocoa Touch so bear with me. It's probably a concept I am missing but here goes.

I want to create a custom UIButton class so it can behave different from a regular button. I want the class to inherit most of it methods from it's superclass and just draw the button from interface builder. The problem is I'm looking to simply change the background color back and and forth every time the button is pressed. However I can't seem to find any appropriate methods that can do this when reviewing the docs.

Any Ideas?

Thanks
 
if you just want the button color to change, you probably can do this on a the action associated with the button. Since UIButton inherits from UIView, you should be able to access the backgroundColor property.
 
if you just want the button color to change, you probably can do this on a the action associated with the button. Since UIButton inherits from UIView, you should be able to access the backgroundColor property.

I realize I can tie the button to an IBAction method in the UIViewController. However I had hoped to make it more as behavioural change within the Button Class. That way every time I created the button it would be automatic instead of adding the code each time.
 
Ok I think I can make it work with the inherited method from UIControl
addTarget:action:forControlEvents:

But I'm just having a hell of a time trying to initialize the button itself.
 
Well doing some research online it doesn't seem like people recommend subclassing UIButton.

To save me the hassle I just decided to create the code in the UIViewController after all. I've created a custom button and just set the background image. However now I have an entirely different problem. The button property currentBackgroundImage doesn't seem to contain the image name as I expected because my if statement does not work.

Code:
- (IBAction)buttonPressed {
	
	if(testButton.currentBackgroundImage == @"whiteButton.png") {
		
		UIImage *buttonImageNormal = [UIImage imageNamed:@"blueButton.png"];
		UIImage *stretchableButtonImageNormal = [buttonImageNormal stretchableImageWithLeftCapWidth:12 topCapHeight:0];
		[testButton setBackgroundImage:stretchableButtonImageNormal forState:UIControlStateNormal];
	}
	else {
		UIImage *buttonImageNormal = [UIImage imageNamed:@"whiteButton.png"];
		UIImage *stretchableButtonImageNormal = [buttonImageNormal stretchableImageWithLeftCapWidth:12 topCapHeight:0];
		[testButton setBackgroundImage:stretchableButtonImageNormal forState:UIControlStateNormal];
	}
    
}

I've already set the button's background image to "whiteButton.png" in the ViewDidLoad method.
 
If you're going to create a custom button you can subclass UIButton with no problems. Create the button in code with initWithFrame.

In your case, call addTarget:action:forControlEvents: for the events you're interested in and set the target to the button itself. The button subclass can then manage its own state.

If you want a rounded rect button then the only way to subclass it that I know is to create the button in a nib. Create a nib with a rounded rect button and then set the class to your subclass. This should work.

You can't compare strings with ==. You must use isEqualToString: But keeping the state of a button by setting a string is bogus. Use a BOOL or an enum.
 
If you're going to create a custom button you can subclass UIButton with no problems. Create the button in code with initWithFrame.

The only time I've ever used initWithFrame is within a method I know that get's triggered by the system. i.e. viewDidLoad. Can you use an example of how you would use initWithFrame from within a UIButton subclass?

You can't compare strings with ==. You must use isEqualToString: But keeping the state of a button by setting a string is bogus. Use a BOOL or an enum.

Although I would normally agree with you, it's at least a known property of the button that I didn't have to implement myself.
 
From my button subclass:

Code:
- (id)initWithFrame:(CGRect)frame 
{
    if ((self = [super initWithFrame:frame])) 
	{
             // Initialization code
             [self setUpButton];
    }
    return self;
}

You create your button with

Code:
MyButton* button = [[MyButton alloc] initWithFrame:frame];

The problem with UIButton is that you normally create buttons using the [UIButton buttonWithType:] factory method. But it won't create a button of your kind. At any rate you need a way to create a button of your kind. initWithFrame is that way. Obviously setUpButton adds the setup to your button that you need.
 
The problem with UIButton is that you normally create buttons using the [UIButton buttonWithType:] factory method. But it won't create a button of your kind. At any rate you need a way to create a button of your kind. initWithFrame is that way. Obviously setUpButton adds the setup to your button that you need.

Thanks, I'll give that a try tonight.
 
Although I would normally agree with you, it's at least a known property of the button that I didn't have to implement myself.

Personally I would use the tag property of the button (actually I believe it is inherited from UIView). It's just an int value that you have control of and can use it to check the identity of your views. Just make sure you assign some non-zero value to it before you try to check its value later.
 
Code:
- (IBAction)buttonPressed {
	if(testButton.currentBackgroundImage == @"whiteButton.png")

This is the wrong way to check for string equality. Use either the isEqual: method or the isEqualToString: method.

The posted code is testing whether the returned property has the same id as the literal string @"whiteButton.png". In other words, it's checking if they are the same NSString pointer.
 
This is the wrong way to check for string equality. Use either the isEqual: method or the isEqualToString: method.

I know, I completely forgot about this. I currently work with 2 other programming languages and I took a little break from cocoa. I think sometimes my head is going to explode.

I'm going to try and subclass UIButton again tonight and if that doesn't work I'll just go with the way I know and assign property to track it's current background.
 
Phew...had to dig deep for this one but I finally got it. I found out initWithFrame never gets called when you subclass the button in IB. Instead you use initWithCoder and it works like a charm. Here is the code I used for those interested.

CustomButton.h

Code:
#import <UIKit/UIKit.h>

@interface CustomButton : UIButton {
	NSInteger toggle;
}

@property (nonatomic, assign) NSInteger toggle;

- (void)buttonPressed: (id)sender;

@end

CustomButton.m

Code:
#import "CustomButton.h"


@implementation CustomButton

@synthesize toggle;

- (id) initWithCoder:(NSCoder *)coder
{
	if (self = [super initWithCoder:coder]) {
		
		self.toggle = 0;
		
		UIImage *buttonImageNormal = [UIImage imageNamed:@"whiteButton.png"];
		UIImage *stretchableButtonImageNormal = [buttonImageNormal stretchableImageWithLeftCapWidth:12 topCapHeight:0];
		[self setBackgroundImage:stretchableButtonImageNormal forState:UIControlStateNormal];
		
		UIImage *buttonImagePressed = [UIImage imageNamed:@"blueButton.png"];
		UIImage *stretchableButtonImagePressed = [buttonImagePressed stretchableImageWithLeftCapWidth:12 topCapHeight:0];
		[self setBackgroundImage:stretchableButtonImagePressed forState:UIControlStateHighlighted];
		
		[self addTarget: self action: @selector(buttonPressed:) forControlEvents: UIControlEventTouchUpInside];
		
	}
	return self;
}

- (void)buttonPressed: (id)sender
{
	if (toggle == 0) {
		UIImage *buttonImageNormal = [UIImage imageNamed:@"blueButton.png"];
		UIImage *stretchableButtonImageNormal = [buttonImageNormal stretchableImageWithLeftCapWidth:12 topCapHeight:0];
		[self setBackgroundImage:stretchableButtonImageNormal forState:UIControlStateNormal];
		self.toggle = 1;
	}
	else {
		UIImage *buttonImageNormal = [UIImage imageNamed:@"whiteButton.png"];
		UIImage *stretchableButtonImageNormal = [buttonImageNormal stretchableImageWithLeftCapWidth:12 topCapHeight:0];
		[self setBackgroundImage:stretchableButtonImageNormal forState:UIControlStateNormal];
		self.toggle = 0;
	}
}


- (void)dealloc {
    [super dealloc];
}


@end
 
Glad you got it figured out. Not complicated at all in the end.

Minor point. If your toggle is a BOOL then you can swap it with

Code:
self.toggle = ! self.toggle;

As an int you can swap it like this

Code:
self.toggle = 1 - self.toggle;

You can always toggle an int between two values by subtracting the current value from the sum of the two possible values.
 
Glad you got it figured out. Not complicated at all in the end.

Minor point. If your toggle is a BOOL then you can swap it with

Code:
self.toggle = ! self.toggle;

As an int you can swap it like this

Code:
self.toggle = 1 - self.toggle;

You can always toggle an int between two values by subtracting the current value from the sum of the two possible values.

At the time I just wanted proof of concept but that is absolutely a better way to go. I went back and changed my code to use a boolean value instead. Thanks for the tip!
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.