UIButton doesn't respond to touches - ios

In my app, I need to parse some data from the network, and add some customized buttons.
Later, when user click on it, I would provide more details.
An image view is the background for the app
The position of these buttons(xPos, yPos) are parsed from the server(dynamic data)
no prints when I click on these buttons that I add programmatically
The code I have for adding it is like this
...
[businessButton setImage:businessImage forState:UIControlStateNormal];
[businessButton setFrame:CGRectMake([xPos floatValue], [yPos floatValue], [businessImage size].width/2, [businessImage size].width/2)];
[businessButton addTarget:self.imageView action:#selector(serviceProviderSelected:) forControlEvents:UIControlEventTouchUpInside];
...
- (void)serviceProviderSelected:(id)sender
{
NSLog(#"sp tapped\n");
}
I created another dummy app to do (what I think is the same thing), and the button works out fine...
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
UIButton *customizedButton = [UIButton buttonWithType:UIButtonTypeCustom];
UIImage *image = [UIImage imageNamed:#"business-icon.png"];
[customizedButton setImage:image forState:UIControlStateNormal];
[customizedButton setFrame:CGRectMake(100, 100, 20, 20)];
[customizedButton addTarget:self action:#selector(customButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:customizedButton];
}
- (IBAction)buttonPressed:(id)sender {
NSLog(#"yes, pressed\n");
}
I've been suspecting that the buttons I create in code are already released, and printed out the array that I use for storing these Buttons, they are valid addresses though...
Thanks!
William

I just found out why.
Instead of [self.imageView addSubview:businessButton];, [self.view addSubview:businessButton]; works now.
I think I didn't really understand how the view relationship was after adding an imageView in storyboard.

UIImageView has user interactions disabled by default. you have to set this property to YES in order to get an UIButton or anything else working as it's subview

You need to do:
[businessButton addTarget:self action:#selector(serviceProviderSelected:) forControlEvents:UIControlEventTouchUpInside];
The target needs to be the class that defines the selector.

Related

UIButton retains the glows after it has been touched

Hi i am making customUIButtons in my app by using the following piece of code.
+ (NSArray *) createButtonItemNormalImage:(UIImage *)normalImage
highlightImage:(UIImage *)highlightImage
disabledImage:(UIImage *)disabledImage
touchUpSelector:(SEL)selector
target:(id)target
{
// HighlightImage is not used. Highlight is shown using iOS glow
UIButton *uiButton = [UIButton buttonWithType:UIButtonTypeCustom];
uiButton.bounds = CGRectMake(0,
0,
normalImage.size.width,
normalImage.size.height);
[uiButton setImage:normalImage
forState:UIControlStateNormal];
if (disabledImage)
{
[uiButton setImage:disabledImage
forState:UIControlStateDisabled];
}
[uiButton addTarget:target
action:selector
forControlEvents:UIControlEventTouchUpInside];
uiButton.showsTouchWhenHighlighted = YES;
UIBarButtonItem *buttonItem = [[UIBarButtonItem alloc] initWithCustomView:uiButton];
return [NSArray arrayWithObjects:buttonItem, uiButton, nil];
}
I have made a cancel button using the above function. The cancel button takes the user from one screen to another screen. The problem is when i come back to the first screen the cancel button is still glowing. I have seen this problem before also but a call to [self.view setNeedsLayout] used to solve it.
Why does it happen and what would be a correct way of solving it?
Thanks!
To solve this problem in not so standard way I now set the highlighted state to no for all the buttons when I enter the first screen. I use myButton.highlighted = NO;. However the documentation says following for highlighted property.
Specify YES if the control is highlighted; otherwise NO. By default, a control is not highlighted. UIControl automatically sets and clears this state automatically when a touch enters and exits during tracking and when there is a touch up.
Its not happening so in my case.I would love to know the reason behind it and standard ways of solving it

Disabled buttons not changing image in

I have a button that pushes to a settings viewController. On that settings viewController I has a switch to invert all the colors on the original view, an inversion which I've done programmatically. I made inverted button images to replace the original images.
When I return to the original view, I have viewWillAppear call the method to invert if the switch has been flipped. Everything changes accordingly except for two disabled buttons.
The switch value is saved under the default settings so that the user can exit the app and come back later and still have the colors inverted. When viewDidLoad calls the invert method, the buttons change just fine, and they even show up adjusted to show they are disabled.
Any idea what might be going on?
-(void)invert{
self.view.backgroundColor = [UIColor blackColor];
//Buttons
[self.stopButton setImage:[UIImage imageNamed:#"stopinverted"]
forState:UIControlStateNormal];
[self.playButton setImage:[UIImage imageNamed:#"playinverted"]
forState:UIControlStateNormal];
}
-(void)viewWillAppear:(BOOL)animated{
_inverted =
[[NSUserDefaults standardUserDefaults] boolForKey:#"isInvertedOn"];
[[NSUserDefaults standardUserDefaults] synchronize];
if(_inverted){
[self invert];
} else {
[self unInvert];
}
[super viewWillAppear:(BOOL)animated];
}
The issue seems to be that the image is not updated automatically, you'll need to reset the enabled value to make it work:
[self.stopButton setImage:[UIImage imageNamed:#"stopinverted"]
forState:UIControlStateNormal];
self.stopButton.enabled = ! self.stopButton.enabled;
self.stopButton.enabled = ! self.stopButton.enabled;
[self.playButton setImage:[UIImage imageNamed:#"playinverted"]
forState:UIControlStateNormal];
self.playButton.enabled = ! self.playButton.enabled;
self.playButton.enabled = ! self.playButton.enabled;
I think you are disabling stopButton and playButton before applying the image. And also you are applying UIImage for UIButton normal UIControlState. I would suggest do the following code changes
Change this
//Buttons
[self.stopButton setImage:[UIImage imageNamed:#"stopinverted"]
forState:UIControlStateNormal];
[self.playButton setImage:[UIImage imageNamed:#"playinverted"]
forState:UIControlStateNormal];
to
//Buttons
[self.stopButton setImage:[UIImage imageNamed:#"stopinverted"]
forState:UIControlStateDisabled];
[self.playButton setImage:[UIImage imageNamed:#"playinverted"]
forState:UIControlStateDisabled];
In short you have to set UIButton UIImages in disabled state.
For more reference have a look this questions answer Xcode: set image on button only for default state, not also selected
If you set the UIImages according to the state of the UIButton then in the function in which you want to invert the UIImage, you have to only need to handle the state of the UIButton and it will automatically change the image.
Thanks for the help. Here is the answer I came up with, inspired by A-Live's answer.
First I had to set the button as enabled, then set the image, and then disable the button again all within the same method. This has fixed my problem.
[self.playButton setEnabled:YES];
[self.playButton setImage:[UIImage imageNamed:#"playinverted"]forState:UIControlStateNormal];
[self.playButton setEnabled:NO];
try using :
theButton.adjustsImageWhenDisabled = false
this will use your custom image instead of the default dimming.

iOS: I set my uibuttons with tags. How do I change the specific button image based on its tag?

I have a multiple array of buttons called button. Each one is tagged. How do I change the image on the button based on its tag and tag only. As of right now, it only changes the very last button.
-(void)buttonTapped:(id)sender{
NSLog (#"%i",[sender tag])];
[button setImage:[UIImage imageNamed:#"button_change.png"] forState:UIControlStateNormal];
}
Either:
for (UIButton *btn in button) {
if(btn.tag == 1)
{
// do something
break; // don't need to run the rest of the loop
}
}
if you want to use the array (it shouldn't be called 'button', use something with a plural for an array)
or an easier way:
UIButton *btn = (UIButton *)[self.view viewWithTag:1];
However a much simpler way would be to use the param in the callback (unless thats not the button you want). Like so:
-(void)buttonTapped:(id)sender
{
UIButton *tappedBtn = (UIButton *)sender;
[tappedBtn setImage:[UIImage imageNamed:#"button_change.png"] forState:UIControlStateNormal];
}
If you just want to change the button that was tapped the following should work.
-(void)buttonTapped:(id)sender
{
NSLog (#"%i",[sender tag])];
UIButton *tappedButton = (UIButton *)sender;
[tappedButton setImage:[UIImage imageNamed:#"button_change.png"] forState:UIControlStateNormal];
}
If you want to change other buttons then you can retrieve the buttons using
[self.view viewWithTag:1000]; //1000 is the tag you assigned
You don't really need to use tags in the this case. When an IBAction is invoked, the sender parameter is a pointer to the control that triggered the IBAction. (Your button.)
Thus, you already have a pointer to the button.
So, as others have pointed out, your code could read like this:
-(void)buttonTapped:(UIButton *)sender
{
[sender setImage:[UIImage imageNamed:#"button_change.png"] forState:UIControlStateNormal];
}
Note that I changed the type of sender to UIButton so that you don't have to cast it. As long as the action is only ever connected to a button, this is safe to do and makes the code cleaner.
As the other poster pointed out, having an array of buttons called "button" is bad. I renamed it to "buttons" in the code below:
If you wanted to do it using tags and an array of buttons, you could use code like this:
-(void)buttonTapped:(UIButton *)sender
{
NSUInteger tag = [sender tag];
UIButton *aButton = buttons[tag];
[aButton setImage:[UIImage imageNamed:#"button_change.png"] forState:UIControlStateNormal];
}
Create images with names button_change1.png, button_change2.png, etc.
And:
-(void) buttonTapped:(UIButton*)sender
{
NSString* imageName = [NSString stringWithFormat: #"button_change%ld.png", (long)sender.tag];
[button setImage:[UIImage imageNamed:imageName] forState:UIControlStateNormal];
}

iOS seating plan layout

I am creating an iOS app with a seating plan layout.
Trying to use an object-oriented approach, I created a class for TableLayoutObjects as they have different properties.
And to lay these TableLayoutObjects out on the screen I am representing them as UIButtons that are created as I loop through the array of TableLayoutObjects.
- (void) loadTables
{
for (TableLayoutObjects *layoutObjs in arrTableLayoutObjects)
{
if ([layoutObjs.shape isEqualToString:#"r"]) {
// rectangle
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
......
if(layoutObjs.isInteractable) {
[button addTarget:self action:#selector(tableTouchedDown:) forControlEvents:UIControlEventTouchDown];
[button addTarget:self action:#selector(tableTouchedUpInside:) forControlEvents:UIControlEventTouchUpInside];
}
} else {
// text only. use label
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(layoutObjs.posX, layoutObjs.posY, layoutObjs.width, layoutObjs.height)];
......
}
}
}
My event handlers look like the below for now.
// reverts back to original color and perform other instructions
- (void) tableTouchedUpInside:(UIButton *) button
{
button.layer.backgroundColor = [UIColor colorWithWhite:0.2f alpha:.5f].CGColor;
}
My question is: how do I identify the UIButtons to their TableLayoutObjects? In the event handler after I change the colour of the button, I will also want to get or set some properties of the selected TableLayoutObjects. How can I do that?
I think your example is a perfect fit for implementing a UICollectionView. Solution with the buttons is less clean and more complex.
You can set the tag of the button to the index into the arrTableLayoutObjects array of the associated item.
Alternatively, create a custom class which takes the table as a parameter and is the target of the button. This object now has direct access to the button and the table item.

UIButton title text is not updated even if I update it in Main thread

I'm trying to change the title of an UIButton I've created programmatically, when the user clicks in it. So, this is my code to create the UIButton:
myButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, parentView.frame.size.width, parentView.frame.size.height)];
[myButton setBackgroundColor:[UIColor blackColor]];
[myButton setAlpha:0.7];
[myButton setTitle:#"Hello" forState:UIControlStateNormal];
[myButton addTarget:self action:#selector(userClicked:) forControlEvents:UIControlEventTouchUpInside];
[parentView addSubview:myButton];
And, in my userClicked: method, I do:
-(void) userClicked:(UIButton*)button
{
NSLog(#"USER CLICKED!!!");
if ([NSThread isMainThread])
{
NSLog(#"is main thread");
}
[button setTitle:#"Bye" forState:UIControlStateHighlighted];
[button setTitle:#"Bye" forState:UIControlStateNormal];
[button setTitle:#"Bye" forState:UIControlStateSelected];
[self someLengthyComputation];
}
The weird thing is that I can see the log messages printed:
USER CLICKED!!!
isMainThread
But, the title of the button does not change! What am I doing wrong?
EDIT: Setting the title for several states doesn't work either.
EDIT2: If I print the description of button in the debugger window of Xcode, it shows the right title!
Printing description of button->_titleView:
<UIButtonLabel: 0xa4c9310; frame = (95 216; 130 22); text = 'Bye'; clipsToBounds = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0xa44f080>>
This worked for me to update the title text (iOS 7.1, Xcode 5.1):
button.enabled = FALSE;
[button setTitle:#"Test" forState:UIControlStateNormal];
button.enabled = TRUE;
I was having the same problem, and I noticed that everywhere else I was setting the attributedTitle. So any updates to the title were not affecting the attributed title.
My solution:
[button setAttributedTitle:#"" forState:UIControlStateNormal];
instead of
[button setTitle:#"" forState:UIControlStateNormal];
At the moment, the shortest work around I came with is calling:
[button setNeedsLayout];
After updating the title.
This seems to happen in iOS 7.1.
All my buttons, which were behaving correctly in previous iOS versions (or maybe just compiled with previous SDKs) suddenly stopped doing that when compiled in Xcode 5.1.1 SDK 7.1.
Looks like Apple's bug.
I had a similar problem using storyboards. Using the answers above I had to use
[mybutton setTitle:#"SomeText" forState:UIControlStateNormal];
[button setNeedsLayout];
[button layoutIfNeeded];
AND I had to make sure that the button type was 'Custom' not 'System' in the attributes inspector.
Please see if this might help you...when the button is clicked check for condition if buttonToggled...like below when you have a function like changeButtonText
-(IBAction)changeButtonText:(id)sender {
if (!buttonToggled) {
[sender setTitle:#"Initial Text" forState:UIControlStateNormal];
buttonToggled = YES;
} else {
[sender setTitle:#"Alternative Text" forState:UIControlStateNormal];
buttonToggled = NO;
}
}
There are several issues in your code:
You are assigning callback to the button:
#selector(userClicked:)
but your code is in another method:
-(void)userTapOnTapToRefreshView:(UIButton*)button
To fix that you need to implement something like this:
-(void)userClicked:(id)sender
{
[(UIButton*)sender setTitle:#"Bye" forState:UIControlStateNormal];
}
Also this part of code does not make sense for me:
[parentView myButton];
Try to change it to:
[parentView addSubview:myButton];
Hope it will help :)
For Swift 3 to 5 just use the following:
button.setTitle("My title", for: .normal)
or for a attributed text use this:
button.setAttributedTitle(<AttributedString>, for: .normal)
This is kinda late and somewhat relates to Walter Schurter's response:
I had a similar issue where my button text was being set correctly until I updated to 7.1. Then I found that since my button was disabled, I had to set the title color and title text for the disabled state to get the text to show up. Then everything worked like a charm!
In iOS 7, UIButton's title is not updated when it is disabled. It seems like a bug on iOS 7.
As a workaround, update both normal and disabled title. It works!
[button setTitle:title forState:UIControlStateNormal];
[button setTitle:title forState:UIControlStateDisabled];
Try this:
[button setTitle:#"Bye" forState:UIControlStateHighlighted];
or:
[button setTitle:#"Bye" forState:UIControlStateSelected];
You could also modify your void function:
-(void) userClicked
{
NSLog(#"USER CLICKED!!!");
if ([NSThread isMainThread])
{
NSLog(#"is main thread");
}
[myButton setTitle:#"Bye" forState:UIControlStateNormal];
}
In my case I tried everything but nothing was working.
Then I just changed button type from system to custom from storyboard. BOOM! everything started behaving normally.
Change the button type to 'Custom', instead of 'System' and it will work as expected :)
Well, is all about the enabled state of a UIButton, Apple has changed something in 7.1 that does not allow you to change the title if you have the UIButton on a disabled state, thats all.
Thanks Apple, i have lost 10 min. debuging an app that was working fine.
Just found out this morning, got updated XCode yesterday to the 5.1.1 and iOS to 7.1
It could be the button layout refresh issue.....
Try using...
[button setNeedsLayout];
[button layoutIfNeeded];
It will force button to update the layout.
Finally, I've figured it out. There were two problems:
1) button was not in state UIControlStateNormal.
2) I was calling a method performing a long computation just after setting the title, [self someLengthyComputation].
I've solved the problem by:
1) Setting the title for all states of the button.
2) Performing that big computation in another thread, not the main thread.
My working code now is:
-(void) userClicked:(UIButton*)button
{
NSLog(#"USER CLICKED!!!");
if ([NSThread isMainThread])
{
NSLog(#"is main thread");
}
[button setTitle:#"Bye" forState:UIControlStateHighlighted];
[button setTitle:#"Bye" forState:UIControlStateNormal];
[button setTitle:#"Bye" forState:UIControlStateSelected];
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
[self someLengthyComputation];
});
}
Thank you very much to everybody who has answered/commented!
This may be trivial, but if you set some button image (instead of background image) which fills the whole button frame, this will shift the title right and thus make it invisible.
If so, try changing button image to background image.
Per Apple developer guide, you should not set either the button title label text or color directly as a property (for example, do not do this: myButton.titleLabel.text=#"SomeText" or myButton.titleLabel.textColor=[UIColor blackColor]).
Rather, you set them using the UIButton setter functionality, like this:
[myButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
...or like this:
[myButton setTitle:#"SomeText" forState:UIControlStateNormal];.
See the Apple guide.
Conclusion after trying many solutions is to use setAttributedTitle instead of setTitle.
To make the title string for AttributedString:
NSMutableAttributedString *attString = [[NSMutableAttributedString alloc] initWithString:#" new Test "];
[button setAttributedTitle:attString forState:UIControlStateNormal];
By the way, this problem is not occasionally happen in normal but it suddenly happen mainly for the following reasons:
If you change the enabled state of a UIButton and try to change the title.
If you use an attributed value and then want to change it using setTitle, the attributed value is superior to the title in that case.
If you navigate to another view controller and then return back to update the button title.
There is no requirement to use:
[button setNeedsLayout];
[button layoutIfNeeded];
Instead, first set type as DEFAULT to CUSTOM
If you applied setAttributedTitle then use:
[button setAttributedTitle:[NSAttributedString new] forState:UIControlStateNormal];
Otherwise there is no need to change anything.
If the color of the text has not changed then apply same thing and set title color with:
[button setTitleColor:[any_color] forState:UIControlStateNormal];
None of the above worked for my case (I was having a button on top of a view presented with UIPopoverController), and I had to removeFromSuperview before setTitle, then addSubview back - hope this helps someone with similar issues
I doubt if 'button' passed in as a parameter is myButton.Anyway,if myButton is a member var,you can just [myButton setTitle:#"Bye" forState:UIControlStateNormal];
Try to declare you button as a property, either in the interface or in the implementation part of you view / view controller
#property(nonatomic,strong) UIButton* myButton;
then create it
self.myButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, parentView.frame.size.width, parentView.frame.size.height)];
[_myButton setBackgroundColor:[UIColor blackColor]];
...
It should work. Don't ask me why really - I just think it's all about ARC / Modern memory management..
EDIT:
It should also work if you keep a reference on the button in your implementation..
#implementation myViewController
{
UIButton* myButton;
}
-(void)createButton
{
myButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, parentView.frame.size.width, parentView.frame.size.height)];
[myButton setBackgroundColor:[UIColor blackColor]];
...
}
...
instead of
yourButton.titleLabel?.text = "T"
use this
yourButton.setTitle("TITLE", for: .normal)
Always do addSubview before setting the text on UIButton.

Resources