Weird behaviour of UIButton with tintColor on enabled/disabled states - ios7

UPDATE:
Even UIBarButtonItems do not respond to state change, visually.
The scenario:
I have a UIButton of type UIButtonTypeSystem initialized as below:
sendButton = [UIButton buttonWithType:UIButtonTypeSystem];
sendButton.backgroundColor = [UIColor clearColor];
[sendButton setTintColor:UIColorFromRGB(SEND_BUTTON_COLOR)];
sendButton.opaque = YES;
sendButton.clearsContextBeforeDrawing = NO;
sendButton.frame = CGRectMake(275, 6, 50, 35);
UIImage* sendImage = [UIImage imageNamed:#"toilet_paper"];
[sendButton setImage:[UIImage imageWithCGImage:sendImage.CGImage scale:sendImage.scale orientation:UIImageOrientationLeft]
forState:UIControlStateNormal];
sendButton.enabled = NO;
[sendButton addTarget:self action:#selector(post) forControlEvents:UIControlEventTouchUpInside];
The Purpose:
It is associated with a UITextView such that it is set to enabled if there is some text in the textView AND my host is available (checked through Reachability) and it's enabled property is changed in textViewDidChange: delegate method with the following:
sendButton.enabled = [APP_DELEGATE hostAvailable] && [myTextView.text stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:#" \n"]].length > 0;
According to this enabled state, the button must toggle between SEND_BUTTON_COLOR (enabled = YES) and grayColor (enabled = NO).
The Problem:
Up until now, the code used to work fine. It was gray when there was no text in the textView and it became SEND_BUTTON_COLOR as soon as there was some text in the textView. However, out of the blue, it has stopped this behaviour. What happens is, it stays gray all the time irrespective of the content of the textView. Once it is pressed, it becomes SEND_BUTTON_COLOR and stays that way, again irrespective of the textView text.
How do I regain the behaviour of the button I used to have on my UIButton?

You have to set your button's tint color again in the textViewDidChange delegate method.
In your delegate where you are setting the enabled state:
sendButton.enabled = [APP_DELEGATE hostAvailable] && [myTextView.text stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:#" \n"]].length > 0;
if(sendButton.enabled){
[sendButton setTintColor:UIColorFromRGB(SEND_BUTTON_COLOR)];
}
else{
//Set Another Color For Disabled State
}

As it appears, the problem was on my part. I had subclassed UIButton in the .m file of a different VC (not imported by the VC in question, or any other VCs it had imported):
#implementation UIButton (Border)
- (void) setEnabled:(BOOL)enabled {
if (enabled) {
self.layer.borderColor = UIColorFromRGB(0x888888).CGColor;
} else {
self.layer.borderColor = UIColorFromRGB(0xdddddd).CGColor;
}
[super setEnabled:enabled];
[self setNeedsDisplay];
}
#end
Somehow, this interfered with my button. After weeks of further subclassing, changing images, changing titles, etc, I was able to diagnose the issue (accidentally) by jumping to definition (CMD + Click) of the enabled method, and it took me straight to the source of the issue (the subclass code). Commented it out, and voila. Things were back to normal.
I hope this helps someone in the future who faces the same issue (or makes the same mistake I did).

Related

Show touch when clicked UIButton Objective-C

I have a UIButton that has only an UIImage set, when Clicked (long press) the images changes automatically with a shadow, What I want is that even if the button gets clicked but not with a long press the images should have the shadow. I already tried from IB to set the UIButton properties Reverse on Highlight and Shows Touch On Highlight but with no results
This is about UIControl Class. It's default to be with shadow. So if you wanna customize it, you should addTarget to your button to every action on UIButton like tapping,long press,touchUpInside and I think it s not about long press it's just you can't realize the difference on tapping.
in one of my project I customize my button actions like this:
buyButton = [[UIButton alloc] initWithFrame:CGRectMake(self.frame.size.width*3/4-2, 0, self.frame.size.width/4+4, self.frame.size.height)];
[buyButton addTarget:self action:#selector(buyButtonClicked:) forControlEvents:UIControlEventTouchDown];
[buyButton addTarget:self action:#selector(buyButtonNormal:) forControlEvents:UIControlEventTouchUpInside];
[buyButton addTarget:self action:#selector(buyButtonNormal:) forControlEvents:UIControlEventTouchUpOutside];
and methods of this like:
- (void) buyButtonClicked:(UIButton *) sender {
if ([self.backgroundColor isEqual:[Util UIColorForHexColor:#"fede32"]]) {
self.backgroundColor = [Util UIColorForHexColor:#"fdca2e"];
}
else if([self.backgroundColor isEqual:[Util UIColorForHexColor:#"cfd3dc"]]) {
self.backgroundColor = [Util UIColorForHexColor:#"c0c5d0"];
}
}
- (void) buyButtonNormal:(UIButton *) sender {
if ([self.backgroundColor isEqual:[Util UIColorForHexColor:#"fdca2e"]]) {
self.backgroundColor = [Util UIColorForHexColor:#"fede32"];
}
else if([self.backgroundColor isEqual:[Util UIColorForHexColor:#"c0c5d0"]]) {
self.backgroundColor = [Util UIColorForHexColor:#"cfd3dc"];
}
[delegate purchaseOffer:selectedOffer];
}
If you need more information about UIButton actions there are a lot of question on this site.

Objective C > UITableViewCell button not depressing

I have the following button being created in the cellForRowAtIndexPath section for this tableView:
UIButton *sched_button_a1 = [UIButton buttonWithType:UIButtonTypeCustom];
sched_button_a1.frame = CGRectMake(0, 0, 110, 52);
CGRect frame_a1 = sched_button_a1.frame;
// Resize height based on length of appointment
frame_a1.size.height = heightCalc;
sched_button_a1.frame = frame_a1;
[sched_button_a1 addTarget:self action:#selector(showAppointment:) forControlEvents:UIControlEventTouchUpInside];
[sched_button_a1 setTitle:[NSString stringWithFormat:#"APPT: %#", APPT_ID ] forState:UIControlStateNormal];
sched_button_a1.layer.backgroundColor = [UIColor redColor].CGColor;
[sched_a1 addSubview:sched_button_a1];
As you can see it's calling showAppointment which is presently just:
-(void)showAppointment:(UIButton*)sender {
NSLog(#"Button pushed");
}
showAppointment is also defined here:
-(void)showAppointment:(UIButton*)sender;
The button shows up fine and gets the correct text and background and appears to be in the foreground, however, when I click on it nothing happens and it doesn't look like the button even gets depressed.
What am I doing wrong or missing?
Edit: I have gone back and removed the sched_a1 view so that the button is directly created in the contentView and this had no effect. It still appears that the only gesture being recognized is the one used to scroll the table. Tapping the button does not appear to change the color of the text or otherwise indicate that the button has been pressed and no log entry is created.
Edit 2: Added cellForRowAtIndexPath to paste bin
http://pastebin.com/WXezfW96
I had a similar issue recently, which I struggled to understand. The solution I came up with was to set the bounds of the button as well and also check for UIControlEventTouchDown rather than UIControlEventTouchUpInside.
Have you tried using cancelsTouchesInView? If you added tap gesture, I think the tap gesture or the tap in table cell overrides the touches in your button. Try using this code:
UIGestureRecognizer *tap= [[UITapGestureRecognizer alloc]initWithTarget:self action:#selector(yourAction)];
tap.cancelsTouchesInView = NO;
tap.delegate = self;
[yourButton addGestureRecognizer:tap];
But don't forget to add <UIGestureRecognizerDelegate> in your .h file
But I agree on Mike. Make it short and simple when creating table cell. I think you can use custom cell subclass and xib file.
I'm also new to objective C so if I say something wrong I'm sorry, I'm just trying to help.
UIButton *sched_button_a1 = [UIButton buttonWithType:UIButtonTypeCustom];
in this change UIButtonTypeCustom to UIButtonTypeRoundedRect
Scott, you may want to try my famous troubleshooting technique:
Determine whether the button or the action is the root cause:
UIImage *image = [UIImage imageNamed:#"image.png"];
[button setBackgroundImage:image forState:UIControlStateHighlighted|UIControlStateSelected]
Instead of:
-(void)showAppointment:(UIButton*)sender {
try
-(void)showAppointment:(id*)sender {
Use breakpoints.
If none of the above works consider refactoring your code that looks a bit messy.
Hey i have checked the code you wrote, it is working fine. Can you check if you have written similar to the code below in your cellForRowAtIndexPath.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *Identifier = #"Identifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:Identifier];
if(cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:Identifier];
}
UIButton *sched_button_a1 = [UIButton buttonWithType:UIButtonTypeCustom];
sched_button_a1.frame = CGRectMake(0, 0, 110, 52);
CGRect frame_a1 = sched_button_a1.frame;
[sched_button_a1 addTarget:self action:#selector(showAppointment:) forControlEvents:UIControlEventTouchUpInside];
[sched_button_a1 setTitle:#"Your Text" forState:UIControlStateNormal];
sched_button_a1.layer.backgroundColor = [UIColor redColor].CGColor;
[cell.contentView addSubview:sched_button_a1];
return cell;
}
I was not able to debug further as you have not kept your entire cellForRowAtIndexPath code. Hope this could help you to solve your problem
Since you are using a button type of UIButtonTypeCustom, you will need to set the attributes for UIControlStateHighlighted and UIControlStateSelected so that you achieve the effect you want for appearing to press the button.
You can change the title color or the background image for the highlighted and selected states using one of the following UIButton methods:
- (void)setTitleColor:(UIColor *)color forState:(UIControlState)state
- (void)setBackgroundImage:(UIImage *)image forState:(UIControlState)state
As an additional note regarding the code in the cellForRowAtIndexPath, you shouldn't be getting the data from sql in the method it would be destructive to your table view performance, get all the data you need before and have them stored in object in an array and then in the cellForRowAtIndexPath method you would just update the values labels from the object for the row.

How to get rid of the space on the left side of a custom UINavigationItem with a UISearchBar

In my navigation bar, I have a magnifying glass icon that brings up a search bar. I'm not using a UISearchDisplayController, so I opted to build my own UINavigationItem and then push it over the standard UINavigationItem using pushNavigationItem.
The problem is that the UINavigationItem seems to be pushed around 8 pixels to the right. This causes the cancel button (with localized text 'Annuleren') to be really close to the edge of the screen.
I tried inspecting the self.mySearchBar.bounds at runtime, but the origin is 0,0. I've played around a bit with AutoLayout and programmatically added constraints, but I haven't been successful. I hope it's possible without AutoLayout.
This is my code:
- (IBAction)displaySearchBar:(id)sender {
if (!self.mySearchNavigationItem)
{
self.mySearchNavigationItem = [[UINavigationItem alloc] initWithTitle:#""];
self.mySearchNavigationItem.hidesBackButton = YES;
self.mySearchBar = [[UISearchBar alloc] initWithFrame:CGRectZero];
self.mySearchBar.showsCancelButton = YES;
self.mySearchBar.delegate = self;
[self.mySearchBar sizeToFit];
[self.mySearchBar setPlaceholder:#"Zoeken..."];
UIView *barWrapper = [[UIView alloc]initWithFrame:self.mySearchBar.bounds];
[barWrapper addSubview:self.mySearchBar];
self.mySearchNavigationItem.leftBarButtonItem = nil;
self.mySearchNavigationItem.backBarButtonItem = nil;
self.mySearchNavigationItem.titleView = barWrapper;
UIButton *cancelButton;
UIView *topView = self.mySearchBar.subviews[0];
for (UIView *subView in topView.subviews) {
if ([subView isKindOfClass:NSClassFromString(#"UINavigationButton")]) {
cancelButton = (UIButton*)subView;
}
}
if (cancelButton) {
[cancelButton setTitle:#"Annuleren" forState:UIControlStateNormal];
}
}
[self.navigationController.navigationBar pushNavigationItem:self.mySearchNavigationItem animated:YES];
NSTimeInterval delay;
if (self.tableView.contentOffset.y >1000) delay = 0.4;
else delay = 0.1;
[self performSelector:#selector(activateSearch) withObject:nil afterDelay:delay];
}
try:
self.navigationController.navigationBar.barTintColor = self.mySearchBar.barTintColor;
if that doesn't work, you can add an underlay view to the navigation controller that is the color you would like. this may be useful: Get the right color in iOS7 translucent navigation bar
After searching for many hours, I gave up and went for a dirty fix. I'll leave it open for a while, in case someone knows why my searchbar is moved 8 pixels to the right.
Right before showing the UINavigationItem, I move the whole UINavigationBar to x-coordinate -8.
self.navigationController.navigationBar.frame = CGRectMake(-8.0, self.navigationController.navigationBar.frame.origin.y, self.navigationController.navigationBar.frame.size.width, self.navigationController.navigationBar.frame.size.height);
[self.navigationController.navigationBar pushNavigationItem:self.mySearchNavigationItem animated:YES];
And then on the cancel button click, I move it back to x-coordinate 0.
- (IBAction)cancelSearchBar:(id)sender {
[self.navigationController.navigationBar popNavigationItemAnimated:YES];
self.navigationController.navigationBar.frame = CGRectMake(0.0, self.navigationController.navigationBar.frame.origin.y, self.navigationController.navigationBar.frame.size.width, self.navigationController.navigationBar.frame.size.height);
}

UIButton does not detect touch events when clicked

I've created a tableview that has custom cells (I've subclassed UITableViewCell).
I'm creating several UIButtons in it. Declaring this in the .h file:
#property (nonatomic, strong) UIButton *myButton;
and this in the .m file:
myButton = [[UIButton alloc] initWithFrame:CGRectMake(-100.0, 285.0, 60.0, 30.0)];
myButton.titleLabel.font = [UIFont boldSystemFontOfSize:16.0];
myButton.backgroundColor = [UIColor clearColor];
[myButton setTitleColor:[UIColor redColor] forState:UIControlStateNormal];
[myButton setTitle:#"text" forState:UIControlStateNormal];
[myButton addTarget:self action:#selector(myButtonClicked:) forControlEvents:UIControlEventTouchUpInside];
[self.contentView addSubview:myButton];
I'm moving the cell to the right using UIPanGestureRecognizer and then the UIButton is revealed (notice that it is in a negative X index).I can see it but it is not clickable (I guess it is not in the view any more).
Any suggestion about how to make it clickable?
More details:
In the init of the cell, I'm doing:
UIPanGestureRecognizer *panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(handlePanGesture:)];
panGesture.delegate = self;
[self.contentView addGestureRecognizer:panGesture];
Then I implemented the gesture:
-(void)handlePanGesture:(UIPanGestureRecognizer *)sender {
CGPoint translate = [sender translationInView:self.contentView];
CGRect currentFrame = self.contentView.frame;
switch ([sender state]) {
case UIGestureRecognizerStateChanged:
currentFrame.origin.x = translate.x;
self.contentView.frame = currentFrame;
break;
default:
break;
}
}
When the frame moves to the right - the button is revealed. But not clickable...
If a button's frame is outside that of its parent's frame, it will not register touches. In this case, you mention that your button stops working when its frame's x location is set to -100. This makes sense because it is outside the touch area.
Your pan gesture you implemented moves the cell's view, so while the button may appear on screen, it is still OUTSIDE of the cell's view, hence it cannot receive touches.
One possible suggestion for fixing this would be to make your cells bigger from the start. So for example, if a cell's frame size is currently 0,0,320,44, you could instead make it -100,0,420,44 and place the contents of your cell accordingly. Now, your button's frame would change to 0,0,x,y and it would be inside of the cell's view and able to receive touches.

How to use UIActivityIndicatorView on custom UIButton?

I want to use UIActivityIndicatorView on my custom UIButton.
Here is my code:
if (sender.tag == 1)
{
// Start animating
activityIndicator.hidden = NO;
[activityIndicator startAnimating];
// Check if the network is available
if ([self reachable]) {
// Stop animating
activityIndicator.hidden = YES;
[activityIndicator stopAnimating];
}
}
What I want to do here is:
Once the user touch the button, I want to start the ActivityIndicatior while Reachable checking the network availability. Once it done pass it to the next view.
Update
UIActivityIndicator is on top of my custom UIButton. It build successfully, but ActivityIndicator is not showing when I touch the button.
I'm sure you had your answer since then.
After 4 years, language has changed to Swift, but I had the same problem : UIActivityIndicatorView is not showing when I add it through :
self.myButton.addSubview(self.myIndicatorView)
I had to climb a level up :
self.myButton.superview!.addSubview(self.myIndicatorView)
Assuming everything else is correct in your project, the problem here is that you're showing and hiding an activity indicator in the same event loop, without giving a pause to draw it. Let me explain:
If you have the code:
UIView* view = someView;
view.backgroundColor = [UIColor redColor];
// various synchronous operations
view.backgroundColor = [UIColor yellowColor];
The view will only ever have a background color of yellow.
To answer your question, you probably want to animate the spinner, do some asynchronous operation, then stop the animation. The key being to not stall on the main event loop while your asynchronous task is running. If your comfortable with blocks it would look something like this:
if (sender.tag == 1) {
// Start animating
activityIndicator.hidden = NO;
[activityIndicator startAnimating];
// Check if the network is available
[self checkReachableWithCallback:^{
// Stop animating
activityIndicator.hidden = YES;
[activityIndicator stopAnimating];
}];
}