there are two labels in view. my designer will offer me markman as following
However, I can only set the constraint in Xcode like following
I mean, the designer need me to align the two labels with baseline, however, I can set the space of two labels with frame. Is there a way to set space of two labels with baseline instead of label's frame using Autolayout?
thanks #Arkadiusz, #Fogmeister, I have implemented a NSLayoutConstraint category to do it.
#implementation NSLayoutConstraint (MarkmanSpace)
- (void)updateSpaceBetweenTopView:(UIView *)topView bottomView:(UIView *)bottomView {
UIFont *topViewFont = [self fontFromView:topView];
UIFont *bottomViewFont = [self fontFromView:bottomView];
if(topViewFont && bottomViewFont) {
CGFloat padding = fabs(topViewFont.descender) + fabs([self lowercaseGlyphTopBaselineHeightForFont:bottomViewFont]);
self.constant -= padding;
}
}
- (UIFont *)fontFromView:(UIView *)view {
UIFont *viewFont = nil;
if([view isKindOfClass:[UILabel class]]) {
viewFont = ((UILabel *)view).font;
}
return viewFont;
}
- (CGFloat)lowercaseGlyphTopBaselineHeightForFont:(UIFont *)font {
return font.ascender - font.xHeight;
}
#end
but there's a problem ,which is the font.xHeight is lower the actual height for Chinese Character,
Your designer wants you to use a baseline and an x-height, see http://en.wikipedia.org/wiki/Baseline_(typography). A format option for the x-height isn't available in Auto Layout (see NSLayoutFormatOptions), so I don't think it's possible.
Related
i am trying to get constraints correct so that the font size scales depending on device. it seems that when the vertical size of the UILabel shrinks the scaling does not happen. the scaling only happens when the horizontal size changes. i have tried fixing the aspect ratio as well but then the text does not fit in the screen vertically in that case.
i would like all the text to be centered in the gray container and scale according to the width of the container.
this is what i am trying to accomplish:
here is the xcode file i am trying to modify - https://www.dropbox.com/sh/c1yx8gk2h8d1ycg/AACnSXUq2j3EiyCnsMHJIqxBa?dl=0
My suggestion:
Create your custom UILabel, UIButton, UITextField subclasses for your project.
In that classes, you can adjust font size on initialise.
Use your custom classes in storyboard/xib/code instead of standard.
For example something like this for UILabel
- (void)awakeFromNib {
[super awakeFromNib];
CGFloat fontSizeMultiplier = 1;
if (isPhone5) {
fontSizeMultiplier = 1.1;
} else if (isPhone6) {
fontSizeMultiplier = 1.2;
} else if (isPhone6Plus) {
fontSizeMultiplier = 1.35;
}
self.font = [UIFont fontWithName:self.font.fontName size:self.font.pointSize * fontSizeMultiplier];
}
I'm working on an app where I place a UISearchBar at the top of a UIViewController that contains a UITableViewController. The UISearchBar filters the contents of the UITableView.
I've left things alone so far (aside from customizing the colors to match my app's theme, which was hard enough!), but on anything except iPhone 4/5, the UISearchBar is dramatically too small.
Therefore, I'm trying to update the size of the font and the height of the internal UITextField.
All of this has proved remarkably difficult to accomplish, requiring quite a bit of customization. So, if you know of a library that makes this easier, please let me know in the comments.
Here's the code I'm using right now:
// In a category for UISearchBar
- (void)setup {
self.tintColor = [UIColor offWhite];
for (UIView *view in self.subviews) {
[self configureView:view];
}
}
- (void)configureView:(UIView *)view {
if ([view isKindOfClass:[UITextField class]]) {
CGFloat fontSize, frameHeight;
if (IS_IPHONE_4) {
fontSize = 14.0f;
frameHeight = 24.0f;
} else if (IS_IPHONE_5) {
fontSize = 14.0f;
frameHeight = 24.0f;
} else if (IS_IPHONE_6) {
fontSize = 17.0f;
frameHeight = 28.0f;
} else if (IS_IPHONE_6PLUS) {
fontSize = 20.0f;
frameHeight = 32.0f;
} else {
// iPad
fontSize = 24.0f;
frameHeight = 36.0f;
}
UITextField *textfield = (UITextField *)view;
textfield.font = [UIFont buttonFontOfSize:fontSize];
textfield.textColor = [UIColor offWhite];
textfield.tintColor = [UIColor offWhite];
CGRect frame = textfield.frame;
frame.origin.y = (self.frame.size.height - frameHeight) / 2.0f;
frame.size.height = frameHeight;
textfield.frame = frame;
}
if (view.subviews.count > 0) {
for (UIView *subview in view.subviews) {
[self configureView:subview];
}
}
}
Note: I structured my code this way in case Apple changes the internal structure of the UISearchBar. I didn't want to hard-code index values.
So, this code "works", in that the end result is what I desire, namely, a taller UISearchBar with text sized as specified and the internal UITextField also taller, as specified. What I don't understand is the process of getting there.
If I call [self.searchBar setup] in my general AutoLayout process, it doesn't work (the internal UITextField is the wrong height). This makes sense to me, since the frame is (0,0,0,0) until the view is actually laid out.
If I call [self.searchBar setup] in my -viewWillAppear: method, it doesn't work (the internal UITextField is the wrong height). This doesn't make sense to me, since debugging shows the frames to still be (0,0,0,0), but I thought -viewWillAppear: was called when everything was laid out and set up.
If I call [self.searchBar setup] in my -viewDidLayoutSubviews method, it "works", but the internal UITextField starts out the "normal" height and then "jumps" to the correct height some time after the view actually appears.
I set up the entire UIViewController in code, using pure AutoLayout. I simply cannot get the UISearchBar set up the way I want BEFORE the view finished loading and is displayed on screen. I've seen some funky stuff in the past, but I've always been able to force a view to render as desired. Is there something special behind the scenes with UISearchBar? Does anybody know how to get this done?
You're mixing auto-layout and manual layout (someView.frame = …) on the same view. You can't do that.
Instead, to change the height, set the constant on your view's height constraint to frameHeight. Let the auto-layout engine set the frame for you.
How to uniquely identify iPhone 6 and iPhone 6 plus portrait screens using size classes?
My App looks good in iPhone 4 and iPhone 5 but the same looks with lots of empty spaces in iPhone 6 and 6 plus because of screen sizes. Though am using auto layout i can't increase the font size or view size only for iPhone 6 and 6 plus alone. I knew that we can change the font size and view size using size classes. but in my case don't know what to do.
Am using xCode 6.1 and my app supports from iOS 7 to latest iOS 8.1. Am expecting solution only in storyboards as am doing my UI designs fully in storyboard. If storyboard has limited functionality to achieve my needs please let me know how to achieve the same with code through out the app?
Another option to adjust the font size according to the iPhone type, is to use 'User Defined Runtime Attributes'.
Define an extension to UILabel:
extension UILabel {
var adjustFontToRealIPhoneSize: Bool {
set {
if newValue {
var currentFont = self.font
var sizeScale: CGFloat = 1
let model = UIDevice.CurrentDevice().modelName()
if model == "iPhone 6" {
sizeScale = 1.3
}
else if model == "iPhone 6 Plus" {
sizeScale = 1.5
}
self.font = currentFont.fontWithSize(currentFont.pointSize * sizeScale)
}
}
get {
return false
}
}
}
In order to determine the current model name, please refer to the following answer: https://stackoverflow.com/a/26962452/4165128
On the storyboard, select the label you wish to adjust, open the right pane, select the identity inspector, and add the 'adjustFontToRealIPhoneSize' property to the list of user defined runtime attributes (with type 'Boolean' and checkbox checked).
Do the same for each label you wish to adjust (copy & paste surprisingly works here).
Use Compact width and Regular Height in storyboard
Add layout constraint of hight and width relative with super view by adding multiplier.
Let's say you have image view which has size half the super view then add multiplier 0.5.
Check out adjustsFontSizeToFitWidth # UILabel Class Reference. This will allow you to do some nice adjustments based on the different devices.
label.adjustsFontSizeToFitWidth = YES;
I don't have too much idea of sizeClass for different font size in different iPhone devices but I figured out with this solution.
Add this method to your utility class.
Just pass your super view to this method, this method is recursive so if you pass self.view than all subviews are set.
Change your font size as you need.
-(void)setAllFonts:(UIView *)view
{
CGFloat fontSizeDiff = 0.0;
if (IS_IPHONE_6)
{
fontSizeDiff = 1;
}
else if (IS_IPHONE_6PLUS)
{
fontSizeDiff = 2;
}
for (UIView *vw in [view subviews])
{
if ([vw isKindOfClass:[UILabel class]] || [vw isKindOfClass:[UIButton class]])
{
if ([vw isKindOfClass:[UILabel class]])
{
UIFont *font = [(UILabel *)vw font];
[(UILabel *)vw setFont:[UIFont fontWithName:font.fontName size:font.pointSize+fontSizeDiff]];
}
else
{
UIFont *font = [(UIButton *)vw titleLabel].font;
[(UIButton *)vw titleLabel].font = [UIFont fontWithName:font.fontName size:font.pointSize+fontSizeDiff];
}
}
else if ([vw isKindOfClass:[UIView class]] || [vw isKindOfClass:[UIScrollView class]])
{
[self setAllFonts:vw];
}
}
}
Swift Version of #iBhaviks answer :
func getFontScaleForDevice() -> CGFloat {
var sizeScale = CGFloat(1.0)
if DeviceType.IS_IPHONE_6 {
sizeScale = 1.2
} else if DeviceType.IS_IPHONE_6P {
sizeScale = 1.4
} else if DeviceType.IS_IPAD {
sizeScale = 1.4
}
return sizeScale
}
func setAllFonts(targetView:UIView, scale:CGFloat) {
for vw in targetView.subviews {
if let vl = vw as? UILabel {
vl.font = vl.font.fontWithSize(round(vl.font.pointSize * scale))
} else if let vb = vw as? UIButton, vbl = vb.titleLabel {
vbl.font = vbl.font.fontWithSize(vbl.font.pointSize * scale)
} else if vw.subviews.count > 0 {
setAllFonts(vw, scale: scale)
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
let sizeScale = getFontScaleForDevice()
if sizeScale > CGFloat(1.0) {
setAllFonts(view, scale: sizeScale)
}
view.layoutIfNeeded()
}
Maybe you could set the perfect font size for the big screens, and then set the Autoshrink to minimum font size with the perfect size for the small screens, in that way you can have a dynamic font size without coding.
You will have to set the constraints for the label to adjust its size with the screen size anyway.
Hope this help
After struggling lot just found of something for my need so posting the same for the future readers.
As of now there is no way to uniquely identify the iPhone 6 model portraits using Size classes. However you can use compact width and regular height to design for all iPhones portrait screens
To change font size you have to identify which iPhone the app currently running on using code and set the font size based on the same
For my requirement - same layout for all the screen sizes - use multiplier in widths and heights constrains. Check Jignesh answers for this question to know more about it.
EDIT 24-SEP-2015
I recently found a way to customize your size class by using UITraitColleection only for iPhone 6 plus so you don't need to write much of code. i hope this link will help someone in future.
Bit late to this but I needed a label that would scale up to any device and used this method.
Create a new subclass of UILabel and in the .h file put this…
#import <UIKit/UIKit.h>
IB_DESIGNABLE
#interface OTHD_ScalableLabel : UILabel
#property (nonatomic, assign) IBInspectable CGFloat fontScale;
#end
and in the .m file put this…
#import "OTHD_ScalableLabel.h"
#implementation OTHD_ScalableLabel
- (void) layoutSubviews
{
[super layoutSubviews];
if( self.fontScale < 0.1 || self.fontScale > 1.0 ) self.fontScale = 1.0;
CGFloat height = CGRectGetHeight(self.bounds) * self.fontScale;
if( height ) self.font = [UIFont fontWithName:self.font.fontName size:height];
}
#end
You can then just change your class in IB and using IBInspectable you will be able to scale the label up or down. Obviously, for the pedantics out there, this is not a good idea for general use but there are some cases where you might need, for example, a large label that displays full screen on an iPhone as well as full screen on an iPad.
Here is an example of a function that I use to toggle between two different font size at runtime. It decides which font to use based on the horizontal size class - which essentially splits devices into two groups "iPad" and "iPhone". Here is a good refernce on whch devices belong to which size classes: http://useyourloaf.com/blog/size-classes/
iPad and Up
iPhone Plus and Down
func chooseFont(compactFont: UIFont, regularFont: UIFont) -> UIFont {
let appDelegate = UIApplication.shared.delegate as! AppDelegate
return appDelegate.window!.traitCollection.horizontalSizeClass == UIUserInterfaceSizeClass.compact ? compactFont : regularFont
}
Based on your screen shot I would suggest using Dynamic Type. That way the user is in charge of the size of the text.
How do we increase the height of UITextField while editing using autolayout. I have a UIScrollView with four UITextField's. Currently all the textfield's are all of constant height. I need only a particular textfield to grow its height based on the text while editing.
You need to set height constrain for each text field.
And on edit did end method you need to identify the textfield by tag and than change the constant of the appropriate textfield.
for ex. heightTxt1,heightTxt2,heightTxt3,heightTxt4 are constraint.
Than change while edit did end called for textfield 1
heightTxt1.constant= (count height on the basis of text and font style,size);
This should resolve your issue,
Step 1: Set no of lines in your label as 0
Step 2: Add this method in your class file
//-- Dynamic label frame depend on text
-(CGRect)getLabelHeightForIndex:(UILabel *)label label_string:(NSString *)label_string
{
CGSize maxSize = CGSizeMake(label.frame.size.width, 999);
CGRect contentFrame;
CGSize contentStringSize;
NSString *version = [[UIDevice currentDevice] systemVersion];
NSString *contentStr = label_string;
if ([version floatValue] < 7.0)
{
contentStringSize = [contentStr sizeWithFont:label.font constrainedToSize:maxSize lineBreakMode:label.lineBreakMode];
}
else
{
NSDictionary *contentDic = [NSDictionary dictionaryWithObjectsAndKeys:label.font, NSFontAttributeName, nil];
contentStringSize = [contentStr boundingRectWithSize:maxSize options:NSStringDrawingUsesLineFragmentOrigin attributes:contentDic context:nil].size;
}
contentFrame = CGRectMake(label.frame.origin.x, label.frame.origin.y, label.frame.size.width, contentStringSize.height);
return contentFrame;
}
Step 3: Access dynamic frame for label using following code
//-- Get dynamic label height from given string
CGRect contentFrame = [self getLabelHeightForIndex:Label label_string:Label.text];
Label.frame = contentFrame;
CGFloat LabelHeight = contentFrame.size.height;
First off, you will want to store the height constraint for that specific UITextField in a property.
For example: #property (nonatomic, weak) IBOutlet NSLayoutConstraint *specificTextFieldHeightConstraint;
Then, implement -[UITextFieldDelegate textFieldDidBeginEditing:] inside your UIViewController subclass:
- (void)textFieldDidBeginEditing:(UITextField *)textField {
specificTextFieldHeightConstraint.constant = expandedHeightGoesHere;
}
Finally, you probably want to return the height back to normal by implementing -[UITextFieldDelegate textFieldDidEndEditing:] inside your UIViewController subclass:
- (void)textFieldDidEndEditing:(UITextField *)textField {
specificTextFieldHeightConstraint.constant = originalHeightConstant;
}
If you set equal heights constraints across all of the UITextFields, you will need to set the priority of the equal height constraint that is attached to specificTextField to low when editing, and set it back to high when editing ends. You will still need a height constraint on specificTextField, but it should have a lower priority than the equal height constraint when specificTextField is not editing, and it should have a higher priority than the equal height constraint when specificTextField is editing.
Is it possible to auto-resize the UILabel box/bounds to fit the contained text?
(I don't care if it ends up larger than the display)
So if a user enters "hello" or "my name is really long i want it to fit in this box", it is never truncated and the label is 'widened' accordingly?
Please check out my gist where I have made a category for UILabel for something very similar, my category lets a UILabel stretch it's height to show all the content: https://gist.github.com/1005520
Or check out this post: https://stackoverflow.com/a/7242981/662605
This would stretch the height, but you can change it around easily to work the other way and stretch the width with something like this, which is I believe what you want to do:
#implementation UILabel (dynamicSizeMeWidth)
- (void)resizeToStretch{
float width = [self expectedWidth];
CGRect newFrame = [self frame];
newFrame.size.width = width;
[self setFrame:newFrame];
}
- (float)expectedWidth{
[self setNumberOfLines:1];
CGSize maximumLabelSize = CGSizeMake(CGRectGetWidth(self.bounds), CGFLOAT_MAX);
CGSize expectedLabelSize = [[self text] sizeWithFont:[self font]
constrainedToSize:maximumLabelSize
lineBreakMode:[self lineBreakMode]];
return expectedLabelSize.width;
}
#end
You could more simply use the sizeToFit method available from the UIView class, but set the number of lines to 1 to be safe.
iOS 6 update
If you are using AutoLayout, then you have a built in solution. By setting the number of lines to 0, the framework will resize your label appropriately (adding more height) to fit your text.
iOS 8 update
sizeWithFont: is deprecated so use sizeWithAttributes: instead:
- (float)expectedWidth{
[self setNumberOfLines:1];
CGSize expectedLabelSize = [[self text] sizeWithAttributes:#{NSFontAttributeName:self.font}];
return expectedLabelSize.width;
}
Using [label sizeToFit]; will achieve the same result from Daniels Category.
Although I recommend to use autolayout and let the label resize itself based on constraints.
If we want that UILabel should shrink and expand based on text size then storyboard with autolayout is best option. Below are the steps to achieve this
Steps
Put UILabel in view controller and place it wherever you want. Also put 0 for numberOfLines property of UILabel.
Give it Top, Leading and Trailing space pin constraint.
Now it will give warning, Click on the yellow arrow.
Click on Update Frame and click on Fix Misplacement. Now this UILabel will shrink if text is less and expand if text is more.
This is not as complicated as some of the other answers make it.
Pin the left and top edges
Just use auto layout to add constraints to pin the left and top sides of the label.
After that it will automatically resize.
Notes
Don't add constraints for the width and height. Labels have an intrinsic size based on their text content.
Thanks to this answer for help with this.
No need to set sizeToFit when using auto layout. My complete code for the example project is here:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var myLabel: UILabel!
#IBAction func changeTextButtonTapped(sender: UIButton) {
myLabel.text = "my name is really long i want it to fit in this box"
}
}
If you want your label to line wrap then set the number of lines to 0 in IB and add myLabel.preferredMaxLayoutWidth = 150 // or whatever in code. (I also pinned my button to the bottom of the label so that it would move down when the label height increased.)
If you are looking for dynamically sizing labels inside a UITableViewCell then see this answer.
Use [label sizeToFit]; to adjust the text in UILabel
Here's what I am finding works for my situation:
1) The height of the UILabel has a >= 0 constraint using autolayout. The width is fixed.
2) Assign the text into the UILabel, which already has a superview at that point (not sure how vital that is).
3) Then, do:
label.sizeToFit()
label.layoutIfNeeded()
The height of the label is now set appropriately.
I created some methods based Daniel's reply above.
-(CGFloat)heightForLabel:(UILabel *)label withText:(NSString *)text
{
CGSize maximumLabelSize = CGSizeMake(290, FLT_MAX);
CGSize expectedLabelSize = [text sizeWithFont:label.font
constrainedToSize:maximumLabelSize
lineBreakMode:label.lineBreakMode];
return expectedLabelSize.height;
}
-(void)resizeHeightToFitForLabel:(UILabel *)label
{
CGRect newFrame = label.frame;
newFrame.size.height = [self heightForLabel:label withText:label.text];
label.frame = newFrame;
}
-(void)resizeHeightToFitForLabel:(UILabel *)label withText:(NSString *)text
{
label.text = text;
[self resizeHeightToFitForLabel:label];
}
#implementation UILabel (UILabel_Auto)
- (void)adjustHeight {
if (self.text == nil) {
self.frame = CGRectMake(self.frame.origin.x, self.frame.origin.y, self.bounds.size.width, 0);
return;
}
CGSize aSize = self.bounds.size;
CGSize tmpSize = CGRectInfinite.size;
tmpSize.width = aSize.width;
tmpSize = [self.text sizeWithFont:self.font constrainedToSize:tmpSize];
self.frame = CGRectMake(self.frame.origin.x, self.frame.origin.y, aSize.width, tmpSize.height);
}
#end
This is category method. You must set text first, than call this method to adjust UILabel's height.
You can size your label according to text and other related controls using two ways-
For iOS 7.0 and above
CGSize labelTextSize = [labelText boundingRectWithSize:CGSizeMake(labelsWidth, MAXFLOAT)
options:NSStringDrawingUsesLineFragmentOrigin
attributes:#{
NSFontAttributeName : labelFont
}
context:nil].size;
before iOS 7.0 this could be used to calculate label size
CGSize labelTextSize = [label.text sizeWithFont:label.font
constrainedToSize:CGSizeMake(label.frame.size.width, MAXFLOAT)
lineBreakMode:NSLineBreakByWordWrapping];
// reframe other controls based on labelTextHeight
CGFloat labelTextHeight = labelTextSize.height;
If you do not want to calculate the size of the label's text than you can use -sizeToFit on the instance of UILabel as-
[label setNumberOfLines:0]; // for multiline label
[label setText:#"label text to set"];
[label sizeToFit];// call this to fit size of the label according to text
// after this you can get the label frame to reframe other related controls
Add missing constraints in storyboard.
Select UILabel in storyboard and set the attributes "Line" to 0.
Ref Outlet the UILabel to Controller.h with id:label
Controller.m and add [label sizeToFit]; in viewDidLoad
the sizeToFit() method doesn't play well with constraints, but as of iOS 9 this is all you need -
label.widthAnchor.constraint(equalToConstant: label.intrinsicContentSize.width).activate()
I had a huge problems with auto layout.
We have two containers inside table cell. Second container is resized depending on Item description (0 - 1000 chars), and row should be resized based on them.
The missing ingredient was bottom constraint for description.
I've changed bottom constraint of dynamic element from = 0 to >= 0.
Fits everytime! :)
name.text = #"Hi this the text I want to fit to"
UIFont * font = 14.0f;
CGSize size = [name.text sizeWithAttributes:#{NSFontAttributeName: font}];
nameOfAssessment.frame = CGRectMake(400, 0, size.width, 44);
nameOfAssessment.font = [UIFont systemFontOfSize:font];
you can show one line output then set property Line=0 and show multiple line output then set property Line=1 and more
[self.yourLableName sizeToFit];
There's also this approach:
[self.myLabel changeTextWithAutoHeight:self.myStringToAssignToLabel width:180.0f];