iOS SDK: Difference between UIButton setTitleForState and UIButton titleLabel.text - ios

I have this issue with a custom UIView where I have a UIButton subview, I want to set the button's text on initialization based on some condition like this:
- (void)awakeFromNib {
[super awakeFromNib];
//check for some conditions
self.testButton.titleLabel.text=#"Some Title";
}
Nothing happens and the button's text is the same as defined in the nib file, however if I change the implementation to:
- (void)awakeFromNib {
[super awakeFromNib];
//check for some conditions
[self.testButton setTitle:#"Some Title" forState:UIControlStateNormal];
}
It works as expected.
Can somebody please explain to me the difference between the two approaches? and when to use each?
EDIT:
the suggested answer doesn't explain my situation, I tested changing the button's text from another button's action like this:
- (IBAction)otherButtonClicked:(id)sender {
self.testButton.titleLabel.text=#"Some Title";
}
and the button's text changed. I just want to understand that behaviour.

Exact answer is
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.
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.

Title label access is given to adjust other properties of the label such as font but setting titleLabel text does not work.
It is because UIButton class has inner implementation to set the text based on different states of the button like selected/highlighted etc which overrides label text.

The accepted answer is correct, I just add this here to elaborate a bit (comments are too short)
There's not much to explain here. The title is simply not meant to set the text at all. My guess is that the internal workings of UIButton make it save the text somewhere else as well (for different states). It uses a normal UILabel to eventually display that, because that's convenient and easy to understand. Setting the text of that does not change the button in all cases, which probably ultimately depends on the drawing cycle. I assume when it's drawn, laid out, or the like it "replaces" the label's text with its other saved variant at some point.
Now you might wonder why Apple did then expose the UILabel and thus seemed to make the text editable then.
Legacy is probably one aspect of this decision (IIRC you could once set the button's title that way). Although old code doesn't result in the desired behavior, it at least didn't crash immediately. Also, a lot of code simply wants to get the text and expects a label, that works perfectly fine as ever.
Second, designing it totally different seems overkill. To avoid that, they would have to use a subclass of UILabel which prevents you from setting the text or something and use that. Or skip it (and thus legacy support) completely and only offer the setTitle:forState: method. That seems like a bit much for a simple text container like a Label.
Ultimately it's a design choice made by Apple. You can't set the title text directly and there's no case in which you should do it any way other than by using setTitle:forState:

Related

UITextField font-size when content and losing focus

I have a UITextField for which I would like to assign a custom font and font-size like this
self.txtEmail.font = UIFont("HelveticaNeue-Light", size: 24)
When I start the app everything looks good. My placeholder text gets the new font and if I click on the input field and start typing, the text has the new font. However, when I click somewhere else and the input field lose focus the font is reset to the default font. What I find even more strange is that I only have set this default font for UILabels and not UITextField by adding this line in the application method inside AppDelegate. If I try to set the default font for UITextField nothing happens.
UILabel.appearance().font = UIFont(name: "HelveticeNeue", size: 18)
So my question is why I get the behavior where the UITextField has the correct font until it gets content and lose focus.
I have 1 general remark on UI elements' appearance management and one more on UITextField.
A) Appearance:
The documentation says:
To customize the appearance of all instances of a class, use appearance to get the appearance proxy for the class.
In fact, that means that your changes will be applied to every instance of UILabel that enter your application window (as an iOS app has only 1 "public" window). If you want to customize the appearance of labels contained within a particular container class (e. g. inside UIView), you should consider using
+ appearanceForTraitCollection:whenContainedIn: (unfortunately, it's been deprecated since iOS 9.0). A much safer way is to use inheritance for your purposes as follows: MySpecialLabel -[inherits from and applies custom style attributes within its initializers and/or the awakeFromNib: method]-> UILabel.
B) Why is it related to UITextField?
That's because UITextField utilizes UILabels under the hood (like _placeholderLabel and _promptLabel).
I hope it will help you solve your problem.

iOS Accessibility - is there a way to tell when VoiceOver has changed focus?

I'd like to call a method every time a different element is focused while VoiceOver is active. I was hoping there would be some UIAccessibilityNotification for this, but I can't seem to find any.
Ultimately, my goal is to add an additional condition prior to reading the accessibility label. For example, as opposed to saying (by default) "If UIButton becomes focused: read label", I'd like to be able to say "When UIButton becomes focused AND UIButton's background color is blue: read label".
So my question is: how do I either add an additional condition prior to reading the label, or receive a notification when a new element becomes focused?
You can't explicitly tell when the user moves the VoiceOver cursor (just like you can't tell where a sighted user is looking).
For the behavior you want, you have two options:
Set the button's accessibilityLabel to an appropriate value whenever the other conditions change.
Subclass UIButton and override its accessibilityLabel getter method:
- (NSString *) accessibilityLabel {
if (SOME_CONDITION) {
return #"Hooray!";
} else {
return #"Womp womp";
}
}
If you need to disable an item entirely, rather than returning nil or a blank string, you should set its accessibilityElementsHidden property to YES.
You can use the UIAccessibilityFocus protocol to detect changes in focus by accessibility clients (including VoiceOver). Note that UIAccessibilityFocus is an informal protocol that each accessibility element must implement independently.
That said, for your use case, Aaron is right to suggest returning a different accessibilityLabel under each condition.

why cant I change button title in ios

I am new to ios, and trying to change a simple button text from implementation side in an action.
I have tried two of these: while one of them works, the other one doesnt !
self.Btn_Analyse.titleLabel.text = #"Stop Anlaysing"; // didnt work
And...
[self.Btn_Analyse setTitle:#"Deneme" forState:UIControlStateNormal]; //worked
why doesnt the first one work?
PS: I have connected the button as IBOutlet
Why are you telling us what worked?
If it worked then there isn't a problem.
Anyway, the reason the first one didn't work is because a button is not just a label. It has various states and each state can have different properties.
You set the image for the state. You set the title for the state. etc...
Then with different states you can have different properties.
UIButton sets the title on its own, so if you directly change the text in label, button changes it back.
That's because button can have different titles for different states (label can't do that), and sets the appropriate one automatcally.

Clear Label text used in Storyboard

Wondering if anyone know hows to complete the following...
In a Storyboard I set my Labels with dummy text, they are not Static text. For example 'User1 Username'.
What I would to know if there is a setting to clear the value of this label when it is loaded by the view. I have some other code that runs off and collects the relevant information. However, it might take a few seconds so a HUD is shown to the user whilst it loads.
Of course in the background of the HUD you can see the example text shown. I know in viewDidLoad I could simply clear all the label texts setting them back to #"", but is there no setting in the storyboard or anything for this?
You can use User Defined Runtime Attributes to achieve this. Simply set "text" attribute for any UILabel, UITextField or UITextView to get what you want:
KVC and runtime attributes are really powerfull when working with Storyboard (e.g. did you know that you can set "layer.cornerRadius" attribute to any UIView to get rounded corners?).
Unfortunately, there is no other way to do it.
You would have to set the text of the UILabel manually in the ViewDidLoad method:
- (void)viewDidLoad
{
[super viewDidLoad];
self.myLabel.text = #"";
}

UIScroll and its nested elements

I created a UIScrollView.
I set up the dimensions and then I am trying to add UILabels.
However the labels are all white text (annoying because I have to change the property per label).
Is there a way to make all labels (new ones that are dragged from IB to the view) have a default text color of black?
Edited to match comments
I want to use IB as much as I can. Therefore I want to drag UILabel from the Library palette to the UIView. When I do this, the UILabel is set to white (default). I want the default color to be Black. I know I can do this programatically but I am trying to avoid that unless I really really need to.
There's no easy way to do exactly what you want. But what you can do is create a label with the properties you want, store it somewhere on the drawing board but not in the view, then duplicate it each time you want a new label instead of dragging on a new one. You can duplicate easily using option+drag.
I think the short answer is "no, there's not an easy way to do what you're describing."
The easiest way I can think of would be to create all your UILabels (with the default setting of white text), then control-click them all and set their text color all at once – all the other ways are less convenient, or would essentially require that Apple open-source Xcode or UIKit so that we can get at their internals.
Yes, there is a way. You could loop the subviews of the target view such as:
UIView * targetView;
[...]
for(id subView in targetView.subViews){
if([subView isKindOfClass:[UILabel class]]){
[subView setBackgroundColor:clearColor];
}
}
why do the labels have to come from the object library? You could get the functionality that you want by dragging only one UILable from the library to your view set all the properties to the defaults that you want and hit copy(command+c) once. Now you can paste(command+v) your UILabel with the special property values as many times as you want, IBActions and outlets will also be retained in the copys.
If you plan to tweak more involved properties than font color and size, then I would suggest a more custom approach that will require only minimul coding before you do the bulk drag and drop work in IB.
Subclass a UILable in Xcode, set all of your properties just once in a simple return method and than call this method from both "init" and "awakeFromNib" Now go back to IB and do all your drag/dropping making sure that the labels are of your subclass.
However, it is my opinion that if you are doing this a lot, especially if you will be doing something similar again in the future, you will save a substantial amount of time and energy to implement this "label factory" in code. Its likely less code than you are imagining it will be and the kicker is that you can reuse it in the next app. anyway thats my 2cents, Good Luck

Resources