Custom Labels Implementation and Use for iOS Apps - ios

I use Storyboards heavily for my iPhone development. To keep the Labels look the same I create custom UILabels and set the font and size in the custom classes. Then in the Storyboard I assign those classes to the labels displayed in the view.
This works fine but I have 4-5 different kind of labels which only differ by size or weight. How do I deal with this situation? Currently I have the following:
PrimaryLabel
PrimaryLabelBold
DescriptionLabel
DescriptionLabelSmall
DescriptionLabelBold
I think this is too much work and these have to be a better way!!

No need to subclass UILabel so many times. Just create one subclass, like so:
MyLabel.h
typedef NS_ENUM(NSUInteger, MyLabelStyle) {
MyLabelStyleSmall,
MyLabelStyleMedium,
MyLabelStyleBig,
};
#interface MyLabel : UILabel
#property (nonatomic) MyLabelStyle style;
#end
MyLabel.m
#import "UILabel+Styles.h"
#implementation UILabel (Styles)
- (void)setStyle:(MyLabelStyle)style
{
switch (style) {
case MyLabelStyleSmall:
self.font = [UIFont systemFontOfSize:12.0];
break;
case MyLabelStyleMedium:
self.font = [UIFont systemFontOfSize:17.0];
break;
case MyLabelStyleBig:
self.font = [UIFont systemFontOfSize:22.0];
break;
default:
self.font = [UIFont systemFontOfSize:17.0];
break;
}
}
#end
In your storyboard, set the style of a particular label using User Defined Runtime Attributes:
2 corresponds to MyLabelStyleBig. Use strings instead of an enum if you want.

You can subclass the UILabel class and than create the constructor method in which you can define the type of label you want ie
PrimaryLabel
PrimaryLabelBold
DescriptionLabel
DescriptionLabelSmall
DescriptionLabelBold
For this you can create an enumeration depecting the types of Label's you want. Within the constructor method you can set this enum value.
Happy coding :)

Related

Subclassing UIFont

Is it possible to subclass UIFont and initialize it with another one (or a font descriptor)? The problem is, I can't call super.init(descriptor:size:) for initialization because it's a convenience initializer. The purpose of doing this is to change the attributes for ascender and descender (override the read-only properties) due to the fact that I only display numbers and there's too much whitespace above and below the letters (which I draw directly in a graphics context). When there is another elegant solution, it would be very welcome.
I appreciate your help, thanks in advance.
What you're asking for breaks encapsulation, and perhaps doesn't make sense either.
Rather than using inheritance, and with a few assumptions, a tidier way of achieving this would be to add a category to UIFont and provide your own convenience factory (e.g. class) method.
#interface UIFont (MyUIFont)
#property (assign) CGFloat ascender;
#property (assign) CGFloat descender;
+(instancetype) UIFontWithLessSpacing;
#end
#implementation UIFont (MyUIFont)
+(instancetype) UIFontWithLessSpacing
{
UIFont *font = [UIFontWIthName:#"MyFont" size:12.0f];
font.ascender = 0.0;
font.descender = 0.0;
return font;
}
There are caveats:
That UIFont actually responds to selectors setAscender and setDescender - they are hidden on the public interface
That setting these properties has an effect on layout and that conversely it doesn't have unwanted side effects.
You're poking around in a black-box with no guarantees that what you do will work, or will continue to work.

Change font of UILabel with autolayout

Normally, you use:
label.adjustsFontSizeToFitWidth = YES;
to make a label reduce the size of its font whenever it is no longer possible to increase the size of its frame.
Is there any way to make it try an alternate font, like a condensed version of the same font family before it reduces font size?
There are a few good ways to handle this. In IB select your label:
Method 1:
If you click the plus button, you can add specific fonts for a given size class.
In code:
- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection
{
if(self.view.traitCollection.horizontalSizeClass == UIUserInterfaceSizeClassRegular && self.view.traitCollection.verticalSizeClass == UIUserInterfaceSizeClassRegular)
{
//iPad here
myLbl.Font = [UIFont fontWithName:#"Heyo" size:10];
}
}
The better way, in my opinion, is Method 2:
With this approach, you can either tell the font to shrink up to a certain size, or instead identify a scale factor. Toy with these values until you get the desired result. I also talk about this topic in this tutorial.
In code:
//Minimum font size
[myLbl setMinimumScaleFactor:MIN_FONT_SIZE/[UIFont labelFontSize]];
//Scale factor
[myLbl setMinimumScaleFactor:0.4];
This didn't work for me, but this did.
You also need to use the system font in IB
#import <UIKit/UIKit.h>
#interface UILabelEx : UILabel
#end
#import "UILabelEx.h"
#import "Constants.h"
#implementation UILabelEx
- (void) traitCollectionDidChange: (UITraitCollection *) previousTraitCollection {
[super traitCollectionDidChange: previousTraitCollection];
self.font = [UIFont fontWithName:APP_FONT size:self.font.pointSize];
}
#end

Changing NSAttributedString text value for a UIControl

Currently all my buttons and textfields have attributedText values defined to be attributed strings.
Consider the case with a simple UILabel. Whenever I have to change the text for this UILabel (based on some user action), I have to redefine the attributes on the NSAttributedString. One way is to simply create a subroutine that generates these attributes whenever I require them but that's a concern given there could be a number of different labels (or attributed strings) that would require such convenience methods.
Another could be simply changing the text field and having observers add those attributes but that's the same amount of work and now probably more complicated.
Is there a simple way of achieving the above without redefining attributes?
Exploring #Harry's ideas, here are a few ideas :
Category on NSAttributedString, category on UILabel, or category on NSDictionary, and maybe a mix of them, according to which one best suits you and your project.
Using a category on NSAttributedString in priority before UILabel could be more interesting in case you wanted to use the custom NSAttributedString for others kind of object (like a UITextView).
A good start:
typedef enum : NSUInteger {
AttributeStyle1,
AttributeStyle2,
} AttributeStyles;
A possible category method on NSDictionary:
-(NSDictionary *)attributesForStyle:(AttributeStyles)style
{
NSDictionary *attributes;
switch(style)
{
case AttributeStyle1:
attributes = #{}//Set it
break;
case AttributeStyle2:
attributes = #{}//Set it
break;
default:
attributes = #{}//Set it
break;
}
return attributes;
}
Category possible on UILabel:
-(void)setString:(NSString *)string withAttributes:(NSDictionary *)attributes
{
[self setAttributedText:[[NSAttributedString alloc] initWithString:string attributes:attributes];
}
Category possible on NSAttributedString:
-(NSAttributedString *)initWithString:(NSString *)string withStyle:(AttributedStyles)style
{
//Here, a mix is possible using the first method, or doing here the switch case
//Ex: return [[NSAttributedString alloc] initWithString:string attributes:[NSDictionary attributesForStyle:style];
//And to use like this: [yourLabel setAttributedText:[[NSAttributedString alloc] initWithString:string withStyle:AttributeStyle1];
}

Font Size Resizes by Stepper

I want my app to be when you click the stepper it will resize the font in a UITextView. The problem is I'm getting errors.
- (IBAction)myStepper:(id)sender {
[myStepper setMinimumValue:14.0]
self.myStepper.maximumValue =20.0;
UIFont newSize = [myTextView fontWithSize:self.stepper.value];
self.myTextView.font = newSize;
}
This all of my code, am I missing something?
You are missing a self and a semicolon in the first line:
[self.myStepper setMinimumValue:14.0];
And an asterisk and self in this line, and stepper should be myStepper:
UIFont *newSize = [self.myTextView fontWithSize:self.myStepper.value];
I would clean your method a bit like this:
- (IBAction)myStepperValueChanged:(UIStepper *)sender {
[sender setMinimumValue:14.0];
[sender setMaximumValue:20.0];
UIFont * newSize = [UIFont fontWithName:myTextView.font.fontName size:sender.value];
[self.myTextView setFont:newSize];
}
but I'd put the first two lines into the -viewDidLoad method as part of the standard init procedure, if they are static values in runtime using the outlet's name (I assume it is myStepper in your class):
[myStepper setMinimumValue:14.0];
[myStepper setMaximumValue:20.0];
or I'd set these values in the Interface Builder, and you'd not need to deal with any outlet for your stepper at all.
NOTE: it is hard to tell which solution would fit better for you, you have not shared too much information which could help me to recommend a specific solution.
For easier code-reading, you shouldn't give your action method the same name of your UISteppert property. Change it to -(IBAction)stepperValueChanged:(id)sender for example.
You don't need to set the min and max value in this method, but in your initialization methods
- (void) viewDidLoad
{
[super viewDidLoad];
// Various implementation
self.myStepper.minimumValue = 14;
self.myStepper.maximumValue = 20.0;
}
- (IBAction) stepperValueChanged:(id)sender
{
self.myTextView.font = [UIFont fontWithSize:self.myStepper.value];
}
It looks like you are some issues understanting the differences between method and properties, you should read some documentation and tutorials if you're beginning development (and welcome to the development world ;) ).
EDIT :
The property myStepper doesn't exists in your class. If you use interface builder, you have to link your stepper to an UIStepper property : this apple tutorial will help you : https://developer.apple.com/library/ios/recipes/xcode_help-interface_builder/articles-connections_bindings/CreatingOutlet.html .
Otherwise, if you don't use Interface Builder, you must add an UIStepper property to your class and allocate it by yourself.

Changing UILabel font

Say I have three UILabels.
I create a font and assign that font to
all three labels.
Now I edit this font's type/size etc and expect the fonts of those labels to change too, which does not happen. I basically need to apply a single font to multiple labels and I know this can be done with an outlet collection but i'd just like some help in understanding the flaw in my logic here so here's my code...
self.labelFont = [UIFont fontWithName:#"System" size:12];
self.label1.font = self.labelFont;
self.label2.font = self.labelFont;
self.label3.font = self.labelFont;
self.labelFont = [UIFont fontWithName:#"Courier" size:30];
Thank
In your code you create two objects, [UIFont fontWithName:#"System" size:12], and [UIFont fontWithName:#"Courier" size:30].
Initially, your labelFont variable points to the first font object. Then you copy that pointer in the assignments to fonts of the three labels. At this moment, you have four pointers referencing the same object - namely, the object returned by the [UIFont fontWithName:#"System" size:12] call.
Next, you change labelFont to point to [UIFont fontWithName:#"Courier" size:30]. Once you do that, the labelFont starts pointing to that new object; it no longer points to the old object. However, the three labels are still pointing to the old font, so their appearance does not change.
This can not be done immediately, you would require a pointer to pointer able to change the font of every label. But when a setter is called it triggers a method that may also cause some side effects, so the better way to do this is to create a setter to handle the behavior:
- (void) setLabelFont: (UIFont*) labelFont {
_labelFont= labelFont;
self.label1.font = self.label2.font = self.label3.font = labelFont;
}
You should just subclass
Y
UILabel, modify the font there and then any labels that you make part of the subclass will have your font. It is less coding and can save you headaches in the long term when you have 100+ labels you want to make the same font.

Resources