I am having trouble with the behavior of one of my UIButtons. I am trying to essentially make it a toggle button, but I am running into the problem below.
I have the code:
UIButton *likeButton = [[UIButton alloc] initWithFrame:CGRectMake(horizontalOffset+buttonWidth, verticalOffset, buttonWidth, buttonHeight)];
[likeButton setImage:[UIImage imageNamed:#"like-off.png"] forState:UIControlStateNormal];
[likeButton setImage:[UIImage imageNamed:#"like-on.png"] forState:UIControlStateHighlighted];
[likeButton setImage:[UIImage imageNamed:#"like-on.png"] forState:UIControlStateSelected];
[likeButton addTarget:self action:#selector(likeButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
which fires the method:
-(void)likeButtonPressed:(id)sender {
UIButton *button = (UIButton *)sender;
[button setSelected:!button.selected];
}
The behavior I am seeing is that when I tap the button down (and highlight it), it works as expected, and the 'like-on.png' image is used for the highlighted state, and it remains on in the 'selected' state.
However, when I tap the button again, to toggle it off, I see a gray highlighted state when I press my finger. When I release my finger, I see the 'like-off' image is shown as expected.
I would like to avoid seeing the gray highlighted state when I press my finger down on the button when I go to toggle it off. Instead I would like to make sure that the highlighted state on toggle-off uses the 'like-on.png' image as specified in the code.
What's going on here? Any ideas where my code could be incorrect?
Many thanks,
Brett
You're missing the image for the selected and highlighted state:
[likeButton setImage:[UIImage imageNamed:#"like-on.png"] forState:UIControlStateSelected | UIControlStateHighlighted];
If you don't set it, the image of the normal state is used. From the -[UIButton setImage:forState:] documentation:
In general, if a property is not specified for a state, the default is to use the UIControlStateNormal value.
If you don't want your images to be modified when they are highlighted, set:
likeButton.adjustsImageWhenHighlighted = NO;
I think the selected property of UIButton is meant for something different (think of the desktop UI).
It would be more consistent to change the for all states according to a BOOL that tracks if it is "on" or "off".
Thus,
-(void)likeButtonPressed:(id)sender {
UIButton *button = (UIButton *) sender;
liking = !liking;
if (liking) {
// configure the four states with "like-on" and other images
}
else {
// configure the four states with "like-off"
}
}
Otherwise you would use the state of a UI element to represent your program logic, which is basically flawed. The only instance where this is sort of acceptable (but not really) is a UISwitch.
Related
I have a tableview that contains a row with a custom cell that contains a UIButton. However, the button doesn't always fire the action. Here's my code:
submitButton = [[UIButton alloc] init];
[[submitButton layer] setBorderColor:[[UIColor whiteColor] CGColor]];
[submitButton setClipsToBounds: YES];
submitButton.backgroundColor = [UIColor grayColor];
[submitButton setTitle:#"Send" forState:UIControlStateNormal];
[self.contentView addSubview:submitButton];
[submitButton addTarget:self action:#selector(buttonAction) forControlEvents:UIControlEventTouchUpInside];
[submitButton release];
This is called in the custom cell's -(id)initWithStyle:
The buttonAction method looks like this:
-(void)buttonAction
{
NSLog(#"Button Clicked!");
}
It seems that the only way I can get the buttonAction to fire is if I press down on the button and release somewhere inside the cell's frame, but not inside the button itself. Why would that be?
*UPDATE*
Problem still exists, but I found that the more consistent way to get the button to fire is to click and drag to the left or right and then let go, as long as I let go within the bounds of the cell/row.
UPDATE #2
It looks like if I use iOS 6.0, it works as intended. But on 5.0 or 5.1 it does not.
Try assigning the same method call to TouchUpOutside as well - you should then see it work every time. TouchUpInside is only fired if you lift your finger while still within the bounds of the button.
Try [submitButton sizeToFit]. I'm wondering whether your button has any size (since I don't see you giving it any).
Also: Create your button with [UIButton buttonWithType: UIButtonTypeCustom] instead of alloc-init.
something strange happened during the development of my app. I have a view with several UIButtons in it. All are using custom artwork and are UIButton with CostumType.
For me everything felt right. In the simulator and on the phone.
But when I give the app in someone else's hand the person taps on a button and it won't work. It feels like the button is just reaction to a certain tap which in fact doesn't feel right (if you compare it to normal behavior).
What can I do to make it behave normal? Normal means that somebody who is used to iOS Apps can use it like he except it works.
Here is an example code for a button:
focusButton = [UIButton buttonWithType:UIButtonTypeCustom];
[focusButton setFrame:CGRectMake(self.bounds.size.width-(self.bounds.size.width/widthFactor)+108, self.bounds.origin.y+(self.bounds.size.height/(heightFactor*2))+2, 36, 40)];
focusButton.contentHorizontalAlignment = false; // I think these lines doesn't effect the behavior
focusButton.contentVerticalAlignment = false;
[focusButton setBackgroundImage:[UIImage imageNamed:#"arrowUp.png"] forState:UIControlStateNormal];
[focusButton setBackgroundImage:[UIImage imageNamed:#"arrowUpH.png"] forState:UIControlStateHighlighted];
[focusButton addTarget:self action:#selector(focusOrDefocusCourse) forControlEvents:UIControlEventTouchUpInside];
[self addSubview:focusButton];
The Button Background looks like this:
this is woking fine.. Once just test with the frame..
UIButton *btn_foucs = [UIButton buttonWithType:UIButtonTypeCustom];
[btn_foucs setFrame:CGRectMake(20, 20, 200, 40)];
[btn_foucs setImage:[UIImage imageNamed:#"btn_erase.png"] forState:UIControlStateNormal];
[btn_foucs setImage:[UIImage imageNamed:#"btn_erase_h.png"] forState:UIControlStateHighlighted];
[self.view addSubview:btn_foucs];
You may like to take a look at the UIButton property imageEdgeInsets. This allows you to make the image draw into only part of the frame, so that the touchable area is bigger than the image itself (reducing the chance of 'missing' the button). You could do the following, for example:
int touch_offset = 10;
[focusButton setFrame:CGRectMake(self.bounds.size.width-(self.bounds.size.width/widthFactor)+108-touch_offset, self.bounds.origin.y+(self.bounds.size.height/(heightFactor*2))+2-touch_offset, 36+(touch_offset*2), 40+(touch_offset*2))];
focusButton.imageEdgeInsets = UIEdgeInsetsMake(touch_offset, touch_offset, touch_offset, touch_offset);
This should make the touchable area 10 pixels wider than the image on each side, adjustable by changing the touch_offset value. As a general guideline, Apple recommend using touchable areas no smaller than 44x44 pixels.
I have created a custom UIButton for an iOS 4.3+ application. I wanted to make the background image stretch, so I and used the following methods to set its background image:
[myButton setBackgroundImage:[[UIImage imageNamed:myImagePath] stretchableImageWithLeftCapWidth:leftStretchValue topCapHeight:topStretchValue] forState:UIControlStateNormal]
[myButton setBackgroundImage:[[UIImage imageNamed:myHighlightedImagePath] stretchableImageWithLeftCapWidth:eftStretchValue topCapHeight:topStretchValue] forState:UIControlStateHighlighted]
[myButton setBackgroundImage:[[UIImage imageNamed:mySelectedImagePath]
stretchableImageWithLeftCapWidth:eftStretchValue topCapHeight:topStretchValue] forState:UIControlStateSelected]
I have added an event handler to the custom button
[myButton addTarget:self action:#selector(buttonHighlighted:) forControlEvents:UIControlEventTouchDown];
[myButton addTarget:self action:#selector(buttonSelected:) forControlEvents:UIControlEventTouchUpInside];
In the above mentioned methods, I have set the selected and the highlighted state of the button accordingly. I have changed the frame of the UIButton to adjust the dimensions of an irregular image (mySelectedImage and myHighlightedImage, neither of which is a rectangle), so that it gets aligned properly along with myImage.
- (void) buttonSelected {
myButton.selected = !myButton.selected; //setting the selected property to NO in the viewDidLoad
//code to change the frame of the UIButton to accomodate the irregular image
}
- (void) buttonHighlighted {
myButton.highlighted = YES;
}
The problem I am facing is that when I select myButton, a gray color overlay is displayed on myImage for a very short time before the images myHighlightedImage and mySelectedImage are displayed as its background. I have set the background color of myButton as clearColor, thinking that might be the reason, but it is not getting solved.
What could be the possible issue... ?
button.adjustsImageWhenHighlighted = NO;
I think that is what you are looking for..
Trying to do simple button functionality.
Button A. It has 3 states: default, highlighted, selected. Should UNSELECT when clicked again.
For the life of me, I can't even get a simple 3 state functionality established.
Highlight (on press) appears built in (goes to "stock" blue).
I"ve used Button Attributes to load in image for selected state...PLayed with Control/Content to click Highlight and Selected on and off, trying to find the right combo...
I thought it'd simply be selecting in dropdown the state I want to edit....and it would register my edits for that state...images loaded/ colors changed/ etc....
NO!!!!
What am I missing..?
What are you trying to do set the images or titles for the different states? For that you can just use methods such as
-(void)setImage:(UIImage *)image forState:(UIControlState)state;
OR
-(void)setTitle:(NSString *)title forState:(UIControlState)state;
is that what you are asking. May be i am misunderstanding your question because its not clear enough.
I couldn't find a straight answer about this anywhere. So I brewed my own solution that seems to work great.
Dont forget to wire up your buttons in IB you will need
to bind both
touch up inside to setButtonPressed
file owner to button ex. 1stButton
Psuedo Code
Clear all buttons selected
Set sender to selected
CODE
//Declare your buttons in .h
UIButton *1stButton;
UIButton *2ndButton;
UIButton *3rdButton;
#property(nonatomic, retain) IBOutlet UIButton *1stButton;
#property(nonatomic, retain) IBOutlet UIButton *2ndButton;
#property(nonatomic, retain) IBOutlet UIButton *3rdButton;
//In .m file - write 2 methods
-(void)clearButtons
{
[1stButton setSelected:FALSE];
[2ndButton setSelected:FALSE];
[3rdButton setSelected:FALSE];
}
//attach to touch up inside event in IB for each button
-(void) setButtonPressed:(UIButton *)sender
{
self.clearButtons;
[sender setSelected:TRUE];
[sender setTitleColor:[UIColor whiteColor] forState:UIControlStateSelected];
}
You really should post some code (or an IB screenshot) as it's hard to fully parse what you are saying.
The default behavior of a UIButton is to show the Selected state on press, then revert back to the normal state. If you want it to "lock down" a press, you need to do something like this in the IBAction hooked to the UIButton:
BOOL pressed;
UIButton *button
- (IBAction) toggleButton:(id)sender
{
button.selected = ! pressed;
pressed = ! pressed;
}
AH! Well, not being a programmer, I was hoping for simple "stock" solution within Interface Builder.
To be clear of the "want/wish:
Button A-E. Each button should have 3 states: Default, highlight, selected.
Default: resting state.
Highlight: some treatment using interface builder features or import a graphic that will appear on Highlight. This state appears while button is being pressed.
Selected: when button A-E is released, it shows that it has been selected by displaying a new state, again by using tweaks within interface builder (font color, shadow) or import a graphic. When you click the SELECTED button again, it returns to Default state.
I guess I just thought it'd be "easy" because SDK 3.0 Interface Builder has dropdowns for the 3 states. I thought code would "magically" be assigned to the button allowing it to function as desired along with it's aesthetic shift.
I TOTALLY appreciate any and all feedback. I can pass along any coding advice to partner on this App.
Try this:
UIImage *img1 = [UIImage imageNamed:#"image1.png"];
UIImage *img2 = [UIImage imageNamed:#"image2.png"];
UIImage *img3 = [UIImage imageNamed:#"image3.png"];
[button setImage:img1 forState:UIControlStateNormal];
[button setImage:img2 forState:UIControlStateHighlighted];
[button setImage:img3 forState:UIControlStateSelected];
[button setImage:img2 forState:(UIControlStateHighlighted+UIControlStateSelected)];
[img1 release];
[img2 release];
[img3 release];
This is because the state variable is actually a bit flag.
I have an imageView and after i add it to the screen I release it.
Before that I adds a button onto it with this code.
button = [[UIButton buttonWithType:UIButtonTypeCustom] retain];
button.frame = CGRectMake(128.00, 128.00, 23.00, 40.00);
[button setBackgroundImage:[UIImage imageNamed:#"move.png"] forState:UIControlStateNormal];
[button addTarget:self action:#selector(showAddress:) forControlEvents:UIControlEventTouchUpInside];
[imageView addSubview:button];
- (void) showAddress:(id)sender
{
NSLog(#"Working");
}
The button appears but nothing happens when I click on it to call a function. Not even the pressing-unpressing. Is this because I am releasing the imageView?
Edited:Check if userInteractionEnabled property of your UIImageView is set to YES - it is set to NO by default and that's why UIImageView (and its subviews) does not respond to touch events. I've just set it to YES in my code and it works fine.
One more note - when you add your button to another view it takes the ownership of the button and as you retained it in your code you should also release it. (Or just not retain in this case)