UIButton Multiple Labels Using UIControlStates - ios

I know with a UIButton, I can add additional UILabels as subviews:
[myButton addSubview: myLabel];
And (at least, with the default title label) I can set its text color when tapped by using:
[myButton setTitleColor:someColor forState:UIControlStateHighlighted]
My question is, how can I implement this functionality for additional UILabels added to the UIButton (if this is possible)?

Subclass UIButton and add your additional labels in there as instance variables. Then override -setHighlighted and -setSelected to adjust the additional labels as desired.
FYI - you call [myButton setTitleColor...], not [myButton.titleLabel setTitleColor...]

It seems my way of going about it isn't easy, but I realized I can just add an action to the UIButton for the event UITouchDown, and change the labels accordingly in the action.

You would have to set myLabels text color, before you added it as a subView.
Otherwise, you'll have to enumerate through the button's subviews and change each of your added label's text colors.
Update:
You can change the button title's font as follows:
myButton.titleLabel!.font = UIFont(name: "...", 10)
You can change the button's title color as follows:
colorsBtn.setTitleColor(UIColor.brownColor(), forState: UIControlState.Highlighted)

Related

setImage:forState does not work for subclass of UIButton

I create a class RadioButton that inherited from UIButton ,and rewrite the method layoutSubViews where reset the frame of self.imageView and self.titleLabel ,when I call the method setImage:forState: of RadioButton instance,it does not work, but the method radioButton.imageView.image = [UIImage imageNamed:#""] work. It is an interesting problem ,I appreciate so much if anyone know why and share the reason .
When we set title,image for button we need to use below code
For Button
[button setTitle:#"Your Title" forState:UIControlStateNormal];
For Image
[button setImage:[UIImage imageNamed:#"Your Image Name"] forState:UIControlStateNormal];
why should not we use titleLabel
Do not use the label object to set the text color or the shadow color.
Instead, use the setTitleColor:forState: and
setTitleShadowColor:forState: methods of this class to make those
changes.
The titleLabel property returns a value even if the button has not
been displayed yet. The value of the property is nil for system
buttons.
why should we use setTitle
Use this method to set the title for the button. The title you
specify derives its formatting from the button’s associated label
object. If you set both a title and an attributed title for the
button, the button prefers the use of the attributed title over this
one.
At a minimum, you should set the value for the normal state. If a
title is not specified for a state, the default behavior is to use the
title associated with the UIControlStateNormal state. If the value for
UIControlStateNormal is not set, then the property defaults to a
system value.
For Image

Difference between setTitle:forState: and titleLabel.text

After looking over the internet and other SO questions(this one is great iOS: UIButton titleLabel -- does it do anything at all?), it is unclear to me what is the difference between these two, more accurately, how these two work.
I know that setTitle:forState: let me set text of the button for different states(Normal, Disabled,Highlighted etc.). I know, as well, that titleLabel is read only, but its properties are read/write.
At this point you might ask: What is the problem then?
I will explain it through example. I have following hierarchy:
UITableViewCell - MyView - MyButton
MyView is xib in which, through interface builder, I set button. When I set buttons title like:
self.myButton.titleLabel.text = #"Something"; // some string I get from server
It works. But if I try the similar approach when only MyView is included (somewhere else in the project) and try:
myView.myButton.titleLabel.text = #"Something else";
It doesn't work. Let me be more specific. In one part of the second(even in viewDidApper) buttons title is what I want. After that, the buttons label returns to its default value. The one I set in the interface builder. When I change to
[myView.myButton setTitle:#"Something else" forState:UIControlStateNormal];
It works as expected.
What I want to know is why does this happen? It is unclear to me why does this glitch occurs with the title? Is this strange thing documented somewhere(looked over apple documentation)? Is it possible to get implementation of setTitle:forState:?
I'm not sure how the internals of UIButton are actually implemented but this is a guess. There are times when iOS needs to redraw the button ie. the button is tapped so the button's state changes (let's say from UIControlStateNormal to UIControlStateHighlighted). Then iOS would find the title associated to UIControlStateHighlighted then display that text by using something like.
myButton.titleLabel.text = #"Title for UIControlStateHighlighted";
Sample scenario:
[myButton setTitle:#"Normal" forState:UIControlStateNormal];
[myButton setTitle:#"Highlighted" forState:UIControlStateHighlighted];
// somewhere in the code, you call this to change the label
myButton.titleLabel.text = #"Something else";
// when user taps the button, iOS will do something like
myButton.titleLabel.text = #"Highlighted"; // will overwrite "Something Else"
// when user releases, iOS will again do something like
myButton.titleLabel.text = #"Normal"; // will overwrite "Highlighted"
so it's required to use setTitle:forState: in order to associate the given title to a certain state. The instances where iOS redraws the button is of course not limited to the the user interacting with the button. It could also be triggered by layout changes.
You should not use button.titleLabel.text = to set the button title.
The documentation for the titleLabel property says
Although this property is read-only, its own properties are read/write. Use these properties primarily to configure the text of the button. For example:
UIButton *button = [UIButton buttonWithType:UIButtonTypeSystem];
button.titleLabel.font = [UIFont systemFontOfSize: 12];
button.titleLabel.lineBreakMode = NSLineBreakByTruncatingTail;
Do not use the label object to set the text color or the shadow color. Instead, use the setTitleColor(:for:) and setTitleShadowColor(:for:) methods of this class to make those changes. To set the actual text of the label, use setTitle(_:for:) (button.titleLabel.text does not let you set the text).
I normally don't set button titles in interface builder, but do it in the viewDidLoad, and get the title from Localizable.strings..
You must use setTitle:forState and setTitleColor:forState to change text and color. All other label properties can be changed directly though.
"Do not use the label object to set the text color or the shadow color. Instead, use the setTitleColor:forState: and setTitleShadowColor:forState: methods of this class to make those changes."
-source

How to create and add UIButton with multiline label from code using autolayout?

There's are a lot of links how to size a label, however there's a lack of info how to resize UIButton according to its label size. Currently, I have screen view set with autolayout (all the system constraints are added from IB) and everything works fine, but need to add dynamic content at the bottom of the screen. I need to create and add random number of buttons with random length titles. So, here's code fragment for creating and adding the buttons:
// Loop
UIButton *myButton = [UIButton new];
UIButton.frame = CGRectMake(0, previousButtonOriginY, self.view.bounds.size.width, 20);
myButton.titleLabel.lineBreakMode = NSLineBreakByWordWrapping;
myButton.titleLabel.textAlignment = NSTextAlignmentLeft;
myButton.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft;
[myButton setTitle:#"very long title..." forState:UIControlStateNormal];
myButton.titleLabel.numberOfLines = 0;
[self.footerView addSubview:myButton];
The problem is the frame height of the button stays 20px but the label string is shown on three lines and so overlaps with the other button title. If I add [myButton sizeToFit] then button width is resized to fit all the text into one line and so the title goes beyond the screen.
If I add [myButton sizeThatFits:CGSizeMake(320, 100)] then resize is not working at all. I know sizeToFit should not be called at all because it's not the part of autolayout, however need suggestions on that how easily to make button fit its label to screen 320 width.
I feel I need to add system constraints from code, but haven't done that before, so not sure how it should look like. I probably need a ton of constraints to be added from code in order to get this simple thing working :)
Instead of using Autolayout, you could just use a collection view which better options for you to lay out elements such as buttons.
It is better able to handle layouts under rotation as well.
Or you can use this code, it worked for me...
For example, substitute your desired padding values here:
UIButton* myButton = [[UIButton alloc] init];
// setup some autolayout constraints here
myButton.titleEdgeInsets = UIEdgeInsetsMake(-desiredBottomPadding,
-desiredRightPadding,
-desiredTopPadding,
-desiredLeftPadding);
Combined with the right autolayout constraints, you end up with an auto-resizing button which contains an image and text!
For this kind of customizations you'll need to create a subclass of UIButton. In the subclass you can add a customized label which benefits your needs. You can also override to setTitle:forState method to automatically update your customized label.

Add an image inside UIButton as an accesory

I have the following UIButton I want to add an Image inside to the far right. Like an accessoryView
I am already using backgroundImage and I want to avoid combining the 2 images into 1.
Is it possible to add another image inside the UIButton that is an AccesoryView lets say an image that has a PLUS to the far right? ( I am looking to a way to insert the new image inside the Button)
#luisCien comment
I tried
[_addImage setImage:[UIImage imageNamed:#"shareMe"] forState:UIControlStateNormal];
_addImage.contentHorizontalAlignment =
UIControlContentHorizontalAlignmentRight;
and it looks like this, I would like to image to be on the right side of the text.
Just add it as a subview and choose your coordinates..Try something like this:
UIImageView *yourPlusSign = [[UIImageView alloc]initWithImage:[UIImage imageNamed:#"yourPlusSignImageTitle"]];
yourPlusSign.frame = CGRectMake(x, y, width, height);//choose values that fit properly inside the frame of your baseButton
//or grab the width and height of yourBaseButton and change accordingly
yourPlusSign.contentMode=UIViewContentModeScaleAspectFill;//or whichever mode works best for you
[yourBaseButton addSubview:yourPlusSign];
You can use the contentHorizontalAlignment property of UIButton and set it to UIControlContentHorizontalAlignmentRight this will position the image you add to it to the far right.
Edit:
As suggested by #danypata you can also use UIButton's property imageEdgeInsets to leave
some margin around your 'accessoryView' like so:
[_addImage setImageEdgeInsets:UIEdgeInsetsMake(5, 0, 5, 10)];
Regarding having the text on the left and the image on the right, I believe it's not possible (someone please correct me). For that I would either create my own custom button by extending UIControl or add a UILabel and set it's frame (as opposed to using UIButton's setTitle:forState:) and add it as a subview of the button.
Hope this helps!

Clear button on UITextView

How can I add a clear button (cross inside a circle) for UITextView like UITextField has?
Based on the answer from GhostRider a more accurate and up to date implementation:
int kClearButtonWidth = 15;
int kClearButtonHeight = kClearButtonWidth;
//add the clear button
self.clearButton = [UIButton buttonWithType:UIButtonTypeCustom];
[self.clearButton setImage:[UIImage imageNamed:#"UITextFieldClearButton.png"] forState:UIControlStateNormal];
[self.clearButton setImage:[UIImage imageNamed:#"UITextFieldClearButtonPressed.png"] forState:UIControlStateHighlighted];
self.clearButton.frame = CGRectMake(0, 0, kClearButtonWidth, kClearButtonHeight);
self.clearButton.center = CGPointMake(self.textView.frame.size.width - kClearButtonWidth , kClearButtonHeight);
[self.clearButton addTarget:self action:#selector(clearTextView:) forControlEvents:UIControlEventTouchUpInside];
[self.textView addSubview:self.clearButton];
And the method
- (void)clearTextView:(id)sender{
self.textView.text = #"";
}
You can use this images for the two states of the button:
just make a uibutton and put it on uitextview and set its action for clear text view;
uitextview.frame = (0,0,320,416);
uibutton.frame = (310,0,10,10);
[uibutton setimage:#"cross.png" forcontrolstate:uicontrolstatenoraml];
[uibutton addTarget:self action:#selector(clearButtonSelected:) forControlEvents:UIControlEventTouchUpInside];
-(void)clearButtonSelected{
uitextview=#"";
}
hope you want to clear the text view text when you click on cross button above is help
if not understand then i can send you proper program for that
From product perspective, if you're going to have a clear button, you probably want to use a UITextField instead of a UITextView and UITextField supports a clear button natively - set the clearButtonMode property as such:
UITextField *textfield = ...;
textfield.clearButtonMode = UITextFieldViewModeAlways;
See screenshot:
You could use UITextFieldViewModeWhileEditing to only present the clear button while the user is actively updating the content.
There's nothing built in like there is for the UITextField. You'd have to add the view yourself (probably a UIButton) and place it correctly and also somehow get the text to wrap around it correctly. (And I don't think the latter is really possible.)
Maybe instead you should display a toolbar above the keyboard (or an inputAccessoryView if you're targeting 3.2 and later) that provides a clear button.
For me changing the .frame or the .contentInset properties did not work.
For me the best result came from:
1) adding a UIView to the controller, give it round corners and a border to mimic a UITextView.
self.viewTextBackground.layer.borderColor = [UIColor colorWithRed:171/255.0 green:171/255.0 blue:171/255.0 alpha:1.0].CGColor;
self.viewTextBackground.layer.borderWidth = 1.0f;
self.viewTextBackground.layer.cornerRadius = 9.0f;
2) place UITextView on top of this UIView. Place it so that the borders of the underlying UIView stay visible.
3) give the UITextView round corners:
self.textNote.layer.cornerRadius = 9.0f;
3) Make its width fe. 30pixels less compared to the underlying UIView. You now have space for a clear-button.
4) simply add a UIButton to your controller and place it in the top-right corner of the underlying UIView.
5) change the buttons properties: set its type to 'custom' and set its image to the image of a grey cross.
6) bind an action to the button te clear the UITextView
You can add a clear button like the one in the attached screenshot with minimal coding. Just follow these steps:
Select the storyboard and drag a UIButton into your UITextView
Set the buttons constraints
Assign a title or a background image
Create the button's IBOutlet reference and the action (see onClearPressed(_:) below) for "Touch Up Inside" in the ViewController
Implement the textViewDidChange(_:) UITextViewDelegate delegate method, and make sure to set the button's isEnabled property based on the textfield content, e.g.:
func textViewDidChange(_ textView: UITextView) {
clearButton.isEnabled = !textView.text.isEmpty
}
Implement the onClearPressed(_:) action:
#IBAction func onClearPressed(_ sender: Any) {
textView.text = ""
clearButton.isEnabled = false
}
That's it.

Resources