UIAppearance not taking effect on UILabels created programmatically - ios

We have extended UILabel to be able to apply standard fonts and colors for all uses of a given label type in our apps. Eg.
#interface UILabelHeadingBold : UILabel
#end
In our AppDelegate, we apply fonts and colors like this
[[UILabelHeadingBold appearance] setTextColor:<some color>];
[[UILabelHeadingBold appearance] setFont:<some font>];
When adding a UILabel in our XIB's, we can now select the class to be of type UILabelHeadingBold, and it works as expected. The label is shown with the correct font and color, as specified in our AppDelegate.
However, if we create a label programmatically, eg.
UILabelHeadingBold *headingLabel = [[UILabelHeadingBold alloc] initWithFrame:CGRectMake(10, 10, 100, 30)];
[self.mainView addSubview:headingLabel];
the UILabel does not get the expected font/color applied. We have to manually apply these attributes.
Is there a way to make UIAppearance take effect on programatically created UI elements, or does it only work when used within XIB's?

From Apple documentation :
To support appearance customization, a class must conform to the
UIAppearanceContainer protocol and relevant accessor methods must be
marked with UI_APPEARANCE_SELECTOR.
For example in UINavigationBar.h, tintColor is marked with UI_APPEARANCE_SELECTOR
#property(nonatomic,retain) UIColor *tintColor UI_APPEARANCE_SELECTOR;
But in UILabel.h you can see that the textColor and font propertys are not marked with UI_APPEARANCE_SELECTOR but somehow it works when added in Interface Builder (following the documentation it shouldn't work at all).

Simple hack that is working for me with no issues is to create a category with a UIAppearance setter that modifies UILabel properties.
Following UIAppearance conventions I created a method:
- (void)setTextAttributes:(NSDictionary *)numberTextAttributes;
{
UIFont *font = [numberTextAttributes objectForKey:UITextAttributeFont];
if (font) {
self.font = font;
}
UIColor *textColor = [numberTextAttributes objectForKey:UITextAttributeTextColor];
if (textColor) {
self.textColor = textColor;
}
UIColor *textShadowColor = [numberTextAttributes objectForKey:UITextAttributeTextShadowColor];
if (textShadowColor) {
self.shadowColor = textShadowColor;
}
NSValue *shadowOffsetValue = [numberTextAttributes objectForKey:UITextAttributeTextShadowOffset];
if (shadowOffsetValue) {
UIOffset shadowOffset = [shadowOffsetValue UIOffsetValue];
self.shadowOffset = CGSizeMake(shadowOffset.horizontal, shadowOffset.vertical);
}
}
In UILabel category:
#interface UILabel (UISS)
- (void)setTextAttributes:(NSDictionary *)numberTextAttributes UI_APPEARANCE_SELECTOR;
#end
I'm still trying to figure out why the original setter does not work.

I was having this exact same issue, but in Swift. A custom UILabel's appearance would work if added from a storyboard, but not if added from code.
Here's a solution I found in Swift that's working for me:
class MyLabel: UILabel { }
extension UILabel {
#objc dynamic var customFont: UIFont! {
get { return self.font }
set { self.font = newValue }
}
#objc dynamic var customColor: UIColor! {
get { return self.textColor }
set { self.textColor = newValue }
}
}
Then add these lines where you configure your app appearance:
MyLabel.appearance().customFont = UIFont.systemFont(ofSize: 20)
MyLabel.appearance().customColor = UIColor.magenta

#robert.wijas solution works great !
For iOS 7 and upwards I had to update the key since the one he used are deprecated for 7+ :
- (void)setTextAttributes:(NSDictionary *)numberTextAttributes;
{
UIFont *font = [numberTextAttributes objectForKey:NSFontAttributeName];
if (font) {
self.font = font;
}
UIColor *textColor = [numberTextAttributes objectForKey:NSForegroundColorAttributeName];
if (textColor) {
self.textColor = textColor;
}
UIColor *textShadowColor = [numberTextAttributes objectForKey:NSShadowAttributeName];
if (textShadowColor) {
self.shadowColor = textShadowColor;
}
NSValue *shadowOffsetValue = [numberTextAttributes objectForKey:NSShadowAttributeName];
if (shadowOffsetValue) {
UIOffset shadowOffset = [shadowOffsetValue UIOffsetValue];
self.shadowOffset = CGSizeMake(shadowOffset.horizontal, shadowOffset.vertical);
}
}

A workaround that I've used is to manually apply the color from the appearance that is set:
let label = UILabel()
label.textColor = UILabel.appearance().textColor
This way you don't need to reference anything new, or explicitly define the color. This also works for context specific coloring:
label.textColor = UILabel.appearance(whenContainedInInstancesOf:[MyView.self]).textColor

Related

Multiple fonts inside a single UITextField

I have a TextField and three buttons which are 40pts above the TextField. These buttons provide the changing of font size of TextField's text when I clicked on any of them for eg first button set font size to 17 second changes it to 20 and third change it to 24. So I add IbAction to all buttons like
- (IBAction)setRegularText:(id)sender {
self.additionalInfo.font = [UIFont systemFontOfSize:20];
}
And according to button. But it will change the previous entered text too. I want the text font to be change only when user selet the option. Previously entered text's font size must not be changed.
You will need to use the attributed string NSAttributedString. With text field it is best to have a delegate and implement the method on changing the characters in range. This will handle all the cases even when the user pasts the text from somewhere else.
So the NSMutableAttributedString has a method to replace the string in range with a mutable attributed string which is perfect for this method. The new string received by the delegate must simply be converted to the attributed one with a currently set font.
Try something like this:
#interface AttributedTextField : NSObject<UITextFieldDelegate>
#property (nonatomic, strong) NSMutableAttributedString *attributedString;
#property (nonatomic, strong) UIFont *currentFont;
#end
#implementation AttributedTextField
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
// ensure having a font
UIFont *font = self.currentFont;
if(font == nil) {
font = [UIFont systemFontOfSize:12.0f];
}
// ensure having a base string
if(self.attributedString == nil) {
self.attributedString = [[NSMutableAttributedString alloc] initWithString:#""];
}
// append the new string
[self.attributedString replaceCharactersInRange:range withAttributedString:[[NSMutableAttributedString alloc] initWithString:string attributes:#{NSFontAttributeName: font}]];
textField.attributedText = self.attributedString; // assign the new text which is attributed
return NO; // return false as we are overriding the text
}
#end
set the tag of every button as the font size that button should change.
i-e
self.button1.tag = 17;
self.button2.tag = 20;
self.button3.tag = 24;
and use the tag as font size.
i-e
- (IBAction)setRegularText:(UIButton *)sender {
self.additionalInfo.font = [UIFont systemFontOfSize:sender.tag];
}
You can set different text size in textfield like this way:
- (void)setFontString:(NSString *)setString setFontSize: (double) fontSize {
self.txtAnswer.text = #"";
self.txtAnswer.text = setString;
self.txtAnswer.font = [UIFont systemFontOfSize:fontSize];
}
- (IBAction)btn1Tap:(id)sender {
[self setFontString:#"Good Morning" setFontSize:20.0f];
}
- (IBAction)btn2Tap:(id)sender {
[self setFontString:#"Good Afternoon" setFontSize:15.0f];
}
- (IBAction)btn3Tap:(id)sender {
[self setFontString:#"Good Evening" setFontSize:10.0f];
}

UiTextField changes font while editing in Swift 1.2 & 2.0

I have a UITextField with a custom font, everything worked fine until Swift update to 1.2 and 2.0. Afterwards, each time I try to edit a text field, it changes its font to a different one that seems a sort of Times New Roman. Does anyone have experience of that?
I came across this same issue and figured out a solution. The problem boils down to setSecureTextEntry changing the font when it is set, and not changing it back when it is unset. In fact, you can never change the font back as long as your UITextField has first responder.
The trick is to resignFirstResponder before you call setSecureTextEntry: and then becomeFirstResponder again. This will work (as of iOS 9.2), but it triggers the keyboard show/hide animation and will cause the screen to "shake". To get around that, you'll need to kill the keyboard animation as well.
Here's my full solution:
- (void)setSecureTextEntry:(BOOL)secureTextEntry {
__weak typeof(self) weakSelf = self;
[UIView performWithoutAnimation:^{
BOOL resumeResponder = NO;
if([[weakSelf textEntryField] isFirstResponder]) {
resumeResponder = YES;
[[weakSelf textEntryField] resignFirstResponder];
}
[[weakSelf textEntryField] setSecureTextEntry:secureTextEntry];
if(resumeResponder) {
[[weakSelf textEntryField] becomeFirstResponder];
}
}];
}
PS: This isn't a Swift bug. It's a UIKit bug. I had the same issue with Objective-C.
I had a weird case of fonts changing its size and font type, when secureTextEntry for an UiTextField was toggled by using a button action.
Had to explicitly manage font for the UiTextField by using these lines of code:
password.font = UIFont(name: "systemFont", size: 14)
password.font = UIFont.systemFontOfSize(14)
Complete Code used in the Show Password Button:
//Function associated with the button for show password option
#IBAction func switchShowPasswordAction(sender: AnyObject) {
if showPassword{
showPassword = false
password.secureTextEntry = !showPassword
}else{
showPassword = true
password.secureTextEntry = !showPassword
}
//Changing font fix
password.font = UIFont(name: "systemFont", size: 14)
password.font = UIFont.systemFontOfSize(14)
}
Post applying this change:
Since I used custom fonts we need to preserve the original font. Create an extension to UITextField:
extension UITextField {
func enablePasswordModeWithShowHide() {
secureTextEntry = false
let showButton = UIButton(type: UIButtonType.System)
showButton.setTitle("HIDE", forState: .Normal)
showButton.titleLabel?.textAlignment = .Right
showButton.sizeToFit()
rightView = showButton
rightViewMode = .Always
showButton.addTarget(self, action: "handleShowHideTapped", forControlEvents: .TouchUpInside)
showButton.tintColor = UIColor.blackColor()
}
func handleShowHideTapped() {
secureTextEntry = !secureTextEntry
let font = self.font
self.font = nil
self.font = font
if let oldText = text {
text = "";
text = oldText;
}
if let button = rightView as? UIButton {
button.setTitle(secureTextEntry ? "SHOW" : "HIDE", forState: .Normal)
button.sizeToFit()
}
}
}
Where it could be implemented like this:
passwordTextField.enablePasswordModeWithShowHide()
All of these answers pretty much work, but I had to use a different solution to achieve the results I needed, considering I'm using a custom font.
You need to make the text field attributed in the storyboard inspector pane for the UITextField, as follows:
Then, in code, you need to manage the toggling of the visibility, setting the attributed text each time, to ensure its properly formatted. I also resignFirstResponder() on the field just to take care of some positioning glitch that I still haven't figured out yet.
func toggleShowPass() {
self.showing = !showing
txtpassword.secureTextEntry = !showing
textFieldPassword.resignFirstResponder()
let string = textFieldPassword.text!
let attrString = NSMutableAttributedString(string: string)
textFieldPassword.addAttribute(NSFontAttributeName, value: UIFont(name: "AvenirNext-Regular", size: 16.0)!, range: NSMakeRange(0, string.characters.count))
textFieldPassword.attributedText = attrString
}
Set defaultTextAttributes with custom font attribute after toggling the secureTextEntry flag
NSDictionary *attrsDictionary =
#{ NSFontAttributeName://customfont};
_passwordtextfield.defaultTextAttributes = attrsDictionary;
I had to apply the following solution with latest Xcode 7.1.1 which actually worked in my case I suspect this issue is of framework.
- (IBAction)btnPasswordShowAction:(id)sender {
self.txtPassword.secureTextEntry = !self.txtPassword.secureTextEntry;
NSString *tmpString = self.txtPassword.text;
self.txtPassword.text = #" ";
self.txtPassword.text = tmpString;
[self.txtPassword setFont:nil];
self.txtPassword.font = [UIFont fontWithName:#"OpenSans-Regular" size:16.0];
}
#pragma mark - Textfield Delegate Methods
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
[self.txtPassword setFont:nil];
self.txtPassword.font = [UIFont fontWithName:#"OpenSans-Regular" size:16.0];
}
- (BOOL)textFieldShouldEndEditing:(UITextField *)textField
{
[self.txtPassword setFont:nil];
self.txtPassword.font = [UIFont fontWithName:#"OpenSans-Regular" size:16.0];
return YES;
}

UITextField set border color using storyboard

I'd like to set border color using storyboard if possible. I've seen answer here: UITextField border color
And I followed answer in storyboard:
All properties set, but TextField doesn't show border. Any suggestions?
Well as Bogdan pointed out, you could very well do that with simple subclassing and just a few bits of code. After that everything will be editable in Storyboards.
Subclass UITextField
Create two properties, one for border width and one for border color
Make those variables IBInspectable and entire class IBDesignable
You'll be able to change color and width of border and see the change in real time.
Code for illustration (Swift 3.1):
#IBDesignable
class FormTextField: UITextField {
#IBInspectable var borderColor: UIColor? {
didSet {
layer.borderColor = borderColor?.cgColor
}
}
#IBInspectable var borderWidth: CGFloat = 0 {
didSet {
layer.borderWidth = borderWidth
}
}
}
Edit: You'll see this in your Attributes Inspector
As Bogdan pointed out it's true that you can't find the layer.borderColor property in storyboard as it's a run time thing.
However still you can set the borderColor without using IB_DESIGNABLE, on any view(or UIView Subclass) with a little bit of coding.
Below are the steps how to achieve it,
Create a category on CALayer class. Declare a property of type UIColor with a suitable name, I'll name it as borderUIColor .
Write the setter and getter for this property.
In the 'Setter' method just set the "borderColor" property of layer to the new colors CGColor value.
In the 'Getter' method return UIColor with layer's borderColor.
P.S: Remember, Categories can't have stored properties. 'borderUIColor' is used as a calculated property, just as a reference to achieve what we're focusing on.
Please have a look at the below code sample;
Objective C:
Interface File:
#import <QuartzCore/QuartzCore.h>
#import <UIKit/UIKit.h>
#interface CALayer (BorderProperties)
// This assigns a CGColor to borderColor.
#property (nonatomic, assign) UIColor* borderUIColor;
#end
Implementation File:
#import "CALayer+BorderProperties.h"
#implementation CALayer (BorderProperties)
- (void)setBorderUIColor:(UIColor *)color {
self.borderColor = color.CGColor;
}
- (UIColor *)borderUIColor {
return [UIColor colorWithCGColor:self.borderColor];
}
#end
Swift 3.1:
extension CALayer {
var borderUIColor: UIColor {
set {
self.borderColor = newValue.cgColor
}
get {
return UIColor(cgColor: self.borderColor!)
}
}
}
And finally go to your storyboard/XIB, follow the remaining steps;
Click on the View object for which you want to set border Color.
Click on "Identity Inspector"(3rd from Left) in "Utility"(Right side of the screen) panel.
Under "User Defined Runtime Attributes", click on the "+" button to add a key path.
Set the type of the key path to "Color".
Enter the value for key path as "layer.borderUIColor". [Remember this should be the variable name you declared in category, not borderColor here it's borderUIColor].
Finally chose whatever color you want.
Edit: You've to set layer.borderWidth property value to at least 1 to see the border color.
Build and Run.
Happy Coding. :)
I'm not sure you can change the border colour of a UITextfield in storyboard. You can change it programmatically with something along the lines of;
UITextField *myTextField = (UITextField *)[self.view viewWithTag:1];
myTextField.borderStyle = UITextBorderStyleLine;
myTextField.layer.borderWidth = 2;
myTextField.layer.borderColor = [[UIColor redColor] CGColor];
Hope this helps.
Adition to markus, put the full code:
import UIKit //IMPORTANT
#IBDesignable
class BorderTextField: UITextField {
#IBInspectable var borderColor: UIColor? {
didSet {
layer.borderColor = borderColor?.cgColor
}
}
#IBInspectable var borderWidth: CGFloat = 0 {
didSet {
layer.borderWidth = borderWidth
}
}
}
This is a variant over #rameswar answer which I think it's correct. Since we're applying a UIColor, I think it's preferable to write an extension for the UITextField instead (UI things together):
extension UITextField {
var borderColor : UIColor? {
get {
if let cgcolor = layer.borderColor {
return UIColor(CGColor: cgcolor)
} else {
return nil
}
}
set {
layer.borderColor = newValue?.CGColor
// width must be at least 1.0
if layer.borderWidth < 1.0 {
layer.borderWidth = 1.0
}
}
}
}
The runtime property would be then borderColor (so you don't need to type layer. and I think it's a bit cleaner than borderUIColor).
The borderColor of CALayer is optional so it's this property. It gets black when set to nil
And finally, the layer.borderWidth it's set to a minimum 1.0 because the color it's not set otherwise.
It doesn't show any border because of the layer.borderColor property. It's of type CGColor and Runtime attributes doesn't yet support that by default so, setting just one attribute wrong, disables the other ones as well.
To do it from the storyboard but also involving some code and subclassing, you can use this method:
Subclass UITextField and make an IB_DESIGNABLE UIColor property, that you'll then transform into CGColor and apply it to self.layer.borderColor.

How can I get the iOS 7 default blue color programmatically?

I'm creating custom elements in my app and want to match the look and feel of the new iOS. iOS 7 introduced to us a very common lighter blue color, the default color or tint for several elements, including the system button, segmented control, etc. They've made it easy to select the color using IB, as seen here:
However, I haven't found how to easily access the color programmatically. I checked out the UIColor documentation, and there doesn't seem to be any accessor for the blue system color in the class itself.
Here's my question: does a simple accessor exist for this color? [UIColor ?] or something like it? If not, does someone know the exact RGB values for that color?
Use self.view.tintColor from a view controller, or self.tintColor from a UIView subclass.
It appears to be [UIColor colorWithRed:0.0 green:122.0/255.0 blue:1.0 alpha:1.0].
iOS 7 default blue color is R:0.0 G:122.0 B:255.0
UIColor *ios7BlueColor = [UIColor colorWithRed:0.0 green:122.0/255.0 blue:1.0 alpha:1.0];
According to the documentation for UIButton:
In iOS v7.0, all subclasses of UIView derive their behavior for tintColor from the base class. See the discussion of tintColor at the UIView level for more information.
Assuming you don't change the tintColor before grabbing the default value, you can use:
self.view.tintColor
Here is a simple method to get the default system tint color:
+ (UIColor*)defaultSystemTintColor
{
static UIColor* systemTintColor = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
UIView* view = [[UIView alloc] init];
systemTintColor = view.tintColor;
});
return systemTintColor;
}
Hex Color code
#007AFF
and you need this libary
https://github.com/thii/SwiftHEXColors
ps. iOS, Swift
swift 4 way:
extension UIColor {
static let system = UIView().tintColor!
}
Native extension with predefined system colors gives what you're looking for:
// System colors
extension UIColor {
/* Some colors that are used by system elements and applications.
* These return named colors whose values may vary between different contexts and releases.
* Do not make assumptions about the color spaces or actual colors used.
*/
...
#available(iOS 7.0, *)
open class var systemBlue: UIColor { get }
...
}
You can use it directly:
myView.tintColor = .systemBlue
Get the color automatically by using this code:
static let DefaultButtonColor = UIButton(type: UIButtonType.System).titleColorForState(.Normal)!
The UIWindow.tintColor method wasn't working for me in iOS8 (it was still black), so I had to do this:
let b = UIButton.buttonWithType(UIButtonType.System) as UIButton
var color = b.titleColorForState(.Normal)
This gave the proper blue tint seen in a UIBarButtonItem
From iOS 7 there is an API and you can get (and set) the tint color with:
self.view.tintColor
Or if you need the CGColor:
self.view.tintColor.CGColor
In many cases what you need is just
[self tintColor]
// or if in a ViewController
[self.view tintColor]
or for swift
self.tintColor
// or if in a ViewController
self.view.tintColor
Please don't mess with view.tintColor or extensions, but simply use this:
UIColor.systemBlue
while setting the color you can set color like this
[UIColor colorWithRed:19/255.0 green:144/255.0 blue:255/255.0 alpha:1.0]
Adding a category to UIColor the following way will make it available to you anytime you need it or even change its definition accross your code:
#interface UIColor (iOS7Colors)
+ (instancetype)iOS7blueColor;
#end
#implementation UIColor (SpecialColors)
+ (instancetype)iOS7blueColor;
{
return [UIColor colorWithRed:0.0f green:0.22f blue:122.0/255.0 alpha:1.0f];
}
Once you import the Category in your code you can call the color by using:
UIColor *myBlueColor = [UIColor iOSblueColor];

iOS: how to set custom background colour with sliders?

First off I want to say I saw a couple of posts on this site about how to do this, although none seemed to work for me so please don't close this down until I get it working.
What I want to do is make the background of the view change depending on the value of the sliders are, so that the user can choose the background colour they want.
self->colorView.backgroundColor = [UIColor myColor];
myColor =
I figure I'll need a bit of code like that, although I don't know how to define what my colour will be something; like "red: redSlider / 255" and so on for the other colours? I also don't know where to implement the code above as I need it to continuously update when the use changes the values of the sliders.
I am quite basic at programming as you may have picked up because I'm only a teenager doing it as a hobby and I'd appreciate simple instructions telling me clearly where I need to put code etc.
p.s. It won't let me post an image of the view, sorry :(
In your ViewController.h file define
#property (nonatomic, strong) IBOutlet UISlider *mySlider;
In ViewController.m file, add this:
- (void) sliderValueChanged:(UISlider *)slider
{
// Handle your color changing logic here
myView.backgroundColor = [UIColor colorWithRed:0.4f green:0.5f blue:1.0f alpha:1.0f];
}
In Interface Builder,
Drag UISlider to view and set its "Value Changed" event outlet to sliderValueChanged method.
Now as you change the slider on screen, the color should changed based on your logic in the method sliderValueChanged
Below is the logic as per your requirement:
- (void) sliderValueChanged:(UISlider *)slider
{
// Assuming slider minimum is 0 and maximum is 1
CGFloat redVal = 0.0f;
CGFloat yellowVal = 0.0f;
CGFloat blueVal = 0.0f;
if (slider == redSlider)
{
redVal = slider.value;
}
else if (slider == yellowSlider)
{
yellowVal = slider.value;
}
else if (slider == blueSlider)
{
blueVal = slider.value;
}
myView.backgroundColor = [UIColor colorWithRed:redVal green:greenVal blue:blueVal alpha:1.0f];
}
As UISlider implements the UIAppearence protocol you can set its background color like:
mySlider.backgroundColor = [UIColor lightGrayColor]; // Or any other color
or:
[[mySlider appearance] setBackgroundColor:[UIColor lightGrayColor]];

Resources