How to scale the button text based on screen design swift? - ios

In my project, i am using scaling for UI components. I am able to scale the text for UIlabel like below and it's working in all device:
1. Autoshrinks - minimum font scale set it to 0.5
2. No of lines - 0
3. Enable dynamic type in attribute inspector
4. adjustFontSizeToWidth to true
But when i am trying to adjust font for UI Button using beolow steps and i am not able to scale the text for UI button.
button.titleLabel?.numberOfLines = 1 // Tried with 0 also
button.titleLabel?.adjustsFontSizeToFitWidth = true
button.titleLabel?.lineBreakMode = // tried differenet linebreakmode
Could anyone have an idea of scaling UI Button text?

Are you sure it's not working?
Edit - After comments...
UIKit elements such as UILabel / UIButton / etc to not have a built-in "auto-adjust font height" property.
I don't work for Apple, so just guessing that is (at least in part) due to the fact that, in general...
Based on screen height, the UI is designed to either:
provide more or less information, e.g. more rows in a table, or
adjust vertical spacing between elements
That doesn't mean you can't or shouldn't adjust your font sizes... it just means you have to do it manually.
Couple options:
set the font-size at run-time, as suggested by Duncan
use a UIAppearance proxy to set the font-size, again at run-time
in either case, you could use a height-to-fontSize table or a "percentage" calculation.
Another option would be a custom class that sets the font-size based on the constrained button height.
Here's a simple example (note: for demonstration purposes only):
class AutoFontSizeButton: UIButton {
override func layoutSubviews() {
super.layoutSubviews()
guard let fnt = titleLabel?.font else { return }
// default system type button has 6-pts top / bottom inset
// and font size is 15/18ths of that height
let h = ((bounds.height - 12.0) * (15.0 / 18.0)).rounded()
let fs = fnt.pointSize
if h != fs {
titleLabel?.font = UIFont(descriptor: fnt.fontDescriptor, size: h)
}
}
}
Result - the top three (yellow) buttons are 30, 40 and 50-points in height, with the default font-size of 15. The bottom three (green) buttons are again 30, 40 and 50-points in height, but the font-size is automatically set at run-time:

I don't think there is a way to get the font to auto-size. However, if you set the button's titleLabel.font to a specific font size the button will update to use the new font size, including resizing the button.
Use code like this:
let size: CGFloat = useLargeFont ? 50.0 : 17.0 //Change as needed
if let buttonFont = button.titleLabel?.font {
button.titleLabel?.font = buttonFont.withSize(size)
}

Related

Is it possible to set the alignment of segmented Control titles to the left?

I have been looking around for a way to set the alignment of the segmented control titles to the left but I don't seem to be able to achieve what I want.
I have created this little function to change the frame of the subviews of the segment control.
It works at first.
func modifyFrameOfSegment() {
for segment in segmentedControl.subviews {
guard segment.subviews.isNotEmpty else { return }
segment.contentMode = .left
for label in segment.subviews where label is UILabel {
label.frame = CGRect(x: 0, y: label.frame.origin.y, width: label.frame.size.width, height: label.frame.size.height)
(label as! UILabel).textAlignment = .left
}
}
}
But everytime I select a new segment it resets the frames of all the subviews and center align all the titles again.
Is there a way to achieve a permanent left alignment for the segment titles in a segmented control?
Any tips or advice would be greatly appreciated.
Thank you for your time.
Let's use this method
self.segmentedControl.setContentPositionAdjustment(UIOffset(horizontal: -20, vertical: 0), forSegmentType: .left, barMetrics: .default)
And you can do what you want (Of course, you can change the horizontal & vertical value by your needs). Here is the result:
Update:
There's apparently no way to set the alignment of the items, but you can fake it by adjusting the position of each individual item using setContentOffset(_ offset: CGSize, forSegmentAt segment: Int). Here's a kludgy example:
class LeftSegmentedControl: UISegmentedControl {
var margin : CGFloat = 10
override func layoutSubviews() {
super.layoutSubviews()
leftJustifyItems()
}
func leftJustifyItems() {
let fontAttributes = titleTextAttributes(for: .normal)
let segments = numberOfSegments - 1
let controlWidth = frame.size.width
let segmentWidth = controlWidth / CGFloat(numberOfSegments)
for segment in 0...segments {
let title = titleForSegment(at: segment)
setWidth(segmentWidth, forSegmentAt: segment)
if let t = title {
let titleSize = t.size(withAttributes: fontAttributes)
let offset = (segmentWidth - titleSize.width) / 2 - margin
self.setContentOffset(CGSize(width: -offset, height: 0), forSegmentAt: segment)
}
}
}
}
Here's what it looks like:
There are a few caveats:
This version sets the segments to all have equal width, which might not be what you want.
I used a fixed left margin of 10px because it seems unlikely that you'd want to vary that, but you can obviously change it or make it a settable property.
Just because you can do this doesn't mean you should. Personally, I don't think it looks great, and it suffers in the usability department too. Users expect segmented control items to be centered, and left-justifying the items will make it harder for them to know where to tap to hit the segment. That seems particularly true for short items like the one labelled "3rd" in the example. It's not terrible, it just seems a little weird.
Original answer:
UIControl (of which UISegmentedControl is a subclass) has a contentHorizontalAlignment property that's supposed to tell the control to align its content a certain way, so the logical thing to do would be to set it like this:
let segmented = UISegmentedControl(items: ["Yes", "No", "Maybe"])
segmented.frame = CGRect(x:75, y:250, width:250, height:35)
segmented.contentHorizontalAlignment = .left
But that doesn't work — you still get the labels centered. If you've got a compelling use case for left-aligned segments, you should send the request to Apple.
One way you could work around this problem is to render your labels into images and then use the images as the segment labels instead of plain strings. Starting from the code in How to convert a UIView to an image, you could easily subclass UISegmentedControl to create images from the item strings.

Make responsive label font size for iphone devices

I am new to iOS Development.I am having problem with font size with phone screen size.For example Font size in iPhone 8 Plus looks fine but that text size is bigger in iPhone SE.I tried check Dynamic Type to Automatically Adjusts Font.And try to play with Autoshrink in StoryBoard.And i also tried to Add Font Variation in storyBoard.But I didnt get any good solution.Hope you understand my problem.Thanks in advance
Try this
class func dynamicFontSizeForIphone(fontSize : CGFloat) -> CGFloat
{
var current_Size : CGFloat = 0.0
current_Size = (UIScreen.main.bounds.width/320) //320*568 is my base
let FinalSize : CGFloat = fontSize * current_Size
return FinalSize
}
hope this work
You can change font size by using constraints.
1.take a label give its basic two constraint to satisfy. give one more constraint of equal.width to parent view. keep width as wide as your label text is.(a bit more than label text).
In attribute inspector there is a property name 'auto shrink' set it to 'minimum font size'
thats it.
Note: This will work fine if your Label text is constant. For changeable text there will be other approaches.

How to make text size relative to view in IOS?

In the XCode storyboard, I have set up my ViewController as a bunch of stackviews and everything is relative -- view dimensions are expressed as fractions of other view dimensions... lots of constraints, etc.
This is to make sure it looks decent on all IOS devices (phones and Ipads, anyway).
It does look acceptable in different aspect ratios, but I've noticed that the font size of my UILabels and TextViews are NOT changing -- not getting LARGER along with their containing views.
So, for example, if I switch from an iPhone to an iPad preview, a UILabel size may increase drastically and yet the text that it contains stays the same... so it's tiny text in a big box.
SO... the question is:
Is there a way to express font/text sizes as relative to the view that contains the text?
Something like this:
text.height = 0.7 * container.height
text.width = maintain aspect ratio with height
Thanks.
The same problem i had when i was designing an application for both iPhone and iPad and i tried this solution which is working fine but took a little efforts to manage. You need to create a custom label class which will inherits from UILabel class. There in your awakeFromNib function you can check the device and you can multiply whatever the font size with a ratio you feel ok for iPhone and iPad. You can also add checking for different iPhone sizes. If you wish to use different ratios for different label, make a IBDesignable property named dynamicRatio in your custom class and take that value to increase font. You can play around this. The effort is assigning the class to all your labels and setting properties which i use to do parallel during designing.
Below are the set of code which am using.
import UIKit
class MyLabel: UILabel {
#IBInspectable var autoFont: Bool = false
#IBInspectable var fontSize: CGFloat = 0
override open func awakeFromNib() {
super.awakeFromNib()
let baseHeight: CGFloat = (((UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiom.pad)) ?1024.0:568.0)
if autoFont == true {
if (isDevice() == DEVICES.iPhoneX) {
let size: CGFloat = 667.0 * (fontSize / baseHeight)
self.font = UIFont(name: self.font!.fontName, size: size)
} else {
let size: CGFloat = UIScreen.main.bounds.size.height * (fontSize / baseHeight)
self.font = UIFont(name: self.font!.fontName, size: size)
}
}
}
}
Hope this idea helps.
Increase text font size of UILabels or UITextFields as you can, then set MinimumFontScale or MinimumFontSize attribute for them in "Attributes Inspector" tab, now font size increases as the UITextFields or UILabels size increases.
UILabel
UITextField

Adjust font size of text to fit in UIButton

I want to make sure the button text fits into a UIButton, while the UIButton has a fixed size.
Of course I can access the titleLabel of the UIButton.
In a label I would set autoshrink to minimum font scale which seems to correspond to
self.myButton.titleLabel.adjustsFontSizeToFitWidth = YES;
, but doesn't really behave the same, since it only makes the text fits horizontally into the bounds, not vertically, thereby not changing the font size.
How can i actually adjust the font size of a label programmatically to make the text fit into the label bounds (as shown in Goal in the picture below) ?
I already tried
self.myButton.titleLabel.numberOfLines = 0;
self.myButton.titleLabel.minimumScaleFactor = 0.5f;
without success, always ended up as in adjustsFontSizeToFitWidth on the left side of the pic above.
Edit: The solution also has to be ios7 compliant
self.mybutton.titleLabel.minimumScaleFactor = 0.5f;
self.mybutton.titleLabel.numberOfLines = 0; <-- Or to desired number of lines
self.mybutton.titleLabel.adjustsFontSizeToFitWidth = YES;
... did the trick, after layoutIfNeeded in viewDidLoad
As it turns out, all those must be set to actually adjust the font-size, not just making it fit into the frame.
Update for Swift 3:
mybutton.titleLabel?.minimumScaleFactor = 0.5
mybutton.titleLabel?.numberOfLines = 0 <-- Or to desired number of lines
mybutton.titleLabel?.adjustsFontSizeToFitWidth = true
Updated code for Swift 3.0:
yourButton.titleLabel?.minimumScaleFactor = 0.5
yourButton.titleLabel?.numberOfLines = 0
yourButton.titleLabel?.adjustsFontSizeToFitWidth = true
You can also set a User Defined Runtime Attribute in Interface Builder for the button where
titleLabel.adjustsFontSizeToFitWidth = true
to make text horizontally and vertically fit with button bounds :
1 - set button alignment like image (* VERY IMPORTANT *)
2 - add this lines of code in your ViewController
btnTest.setTitle("Test for multi line in button show line in button show Test for multi line in button show line in button show", for: .normal)
btnTest.titleLabel!.textAlignment = .center
btnTest.titleLabel!.numberOfLines = 0
btnTest.titleLabel!.adjustsFontSizeToFitWidth = true
btnTest.titleLabel!.minimumScaleFactor = 0.5
// below line to add some inset for text to look better
// you can delete or change that
btnTest.contentEdgeInsets = UIEdgeInsets(top: 5, left: 5, bottom: 5, right: 5)
note that DON'T using btnTest.titleLabel!.lineBreakMode = NSLineBreakMode.byWordWrapping or other BreakMode in your code . for enable multiline in button . just using from above code
final result :
Try to call the following method.
button.titleLabel?.baselineAdjustment = UIBaselineAdjustment.AlignCenters
try this:
[myButton setFont:[[myButton font] fontWithSize:--originalButtonFontSize]];
textWidth = [text sizeWithFont:[myButton font]].width;
}
In Storyboard, Go to Link Break in Attributes Inspector, see word wrap..or set according to your choice.
In Xcode->Open storyboard->Go to attributes inspector->Control->Alignment->Choose second in both horizontal and vertical.
or
YourButton.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft;
If you prefer do in storyboard, just click the button, then show attributes inspector, and set the line break to "Word Wrap".

Scale text label by screen size

Is there a way to scale text so that it takes up close to the same screen real estate no matter what the device size is? I've found that the text on an iPad sized device is too small in relation to the screen size when compared to the iPhone. Below is an example of what I'm looking for. Notice the text percentage size is similar in relation to the device screen size.
Example
To set constraints on the label that you have, see this link: How do you make a background image scale to screen size in swift? . I know that you might not be using Swift (I'm using Objective-C), but the first answer shows how to do it in the storyboard. Do the same thing it says, but for the label. Then, see the image below to change the auto shrink options for the label from "Fixed Font Size" to "Minimum Font Scale" (see image below). Hope this helps!
1. Select your label and open attribute inspector for it
2. Click on + sign by Font, select width and height as "Regular", click Add Variation
3. Another Font field will appear, this will represent font for ipad/big screen/ illusion of big screen (scroll view)
4. Select your desired font for ipad
I had my own fix and it's not one click, but I found it well worth it.
Step 1:
Test some font sizes for different screen sizes, recording the font size and the most relevant other dimension.
Here's what I mean...
I'd start with a normal iPad Screen and select a font size that worked.
Since I knew the entire height of the screen would determine what makes a font size comfortable or not, I'd record the font size along with the entire screen height.
So the font size was 50 with a height of 1024.
Then, I switched to the iPhone SE
This was pretty atrocious, so I fixed the font size:
After recording that the font size worked at 25 with a height of 568.
Step 2:
Bust out the calculator app and find a constant that relates the font sizes to the heights (in my case), giving or taking a bit.
Here's what I mean:
I knew 50 works with 1024 and that 25 works with 568.
Dividing 50/1024 gives you .048828125.
Multiplying that by 568 equals 27.734375, which would be a little fat of a font size for the SE.
Repeating that process but using the SE's values to do the division produced .0440140845, and checking that with the iPad's height produces a font size of 45, just a bit small for the mighty iPad screen.
So I split the quotients down the middle, resulting in a number around 0.46.
Step 3:
Connect the labels and change their font sizes programmatically using this number.
First, drag the labels into an outlet collection. I named my connection 'headerLabels' since they're all at the top of the screen.
Then, you can kinda steal my code, but just don't make too much money with it ;)
// Outlet collection of labels
#IBOutlet var headerLabels: [UILabel]!
// Who needs a short variable name when we can have a
// painfully lengthy one to describe the number we calculated?
let relativeFontConstant:CGFloat = 0.046
override func viewDidLoad() {
super.viewDidLoad()
// Should be in the viewDidLoad so it can load the same time as
// the view does.
// apply a font size to all the labels.
for label in headerLabels {
// multiply the height we based it on of the entire view
// to the number we calculated
label.font = label.font.withSize(self.view.frame.height * relativeFontConstant)
}
}
If you've got any questions, comments, compliments, or insults let me know :)
I was having the same issue where I needed the text to be scaled proportionally along with the screen size increase.
Adaptive sizing is quite limited as you can only set the font sizing for size classes. Having the font sizes for two width options, compact and regular was not a solution for me.
I have written a small lib which handles automatic font scaling for UILabel and UITextView for different screen sizes.
You can set the scaling globally or for a specific instance of UILabel and UITextView.
Find it here: AMXFontAutoScale
I had decent behavior with the following code in my viewDidLoad function:
(Note that the screen bounds are in 'points' not 'pixels')
// Set the clock font size according to the height.
// Design was done on iPhone XR with height of 896 points and font size of 98.
if (UIScreen.main.bounds.height != 896). // Only need code if not on original design size.
{
let scaleFactor: Float = Float(UIScreen.main.bounds.height) / 896.0
let fontSize = CGFloat(98.0 * scaleFactor)
self.clock.font = self.clock.font.withSize(fontSize)
}
This was using the "Helvetica Neue" font which is a fixed width font. It wasn't completely scaled up completely for some reason on larger devices, so still a bit smaller than the target on iPads, but close enough.
different screen sizes acoording to
// iphone 5s,SE screen width 320
// iphone 6,6s,7,etc width 375
// iphone 7 plus, 8 plus, xs max,etc width 414
if (self.view.frame.width == 320) {
label.font = UIFont(name: label.font.fontName, size: 16)
} else if (self.view.frame.width == 375) {
label.font = UIFont(name: label.font.fontName, size: 21)
} else if (self.view.frame.width == 414) {
label.font = UIFont(name: label.font.fontName, size: 24)
}
There is an aspect ratio constraint available. Add this to your label. Constraints to left and top margins for anchoring the label in place should silence the compiler warnings.
As #VatsalManot mentioned, learn adaptive sizing for starters. Here's a good link:
http://www.raywenderlich.com/83276/beginning-adaptive-layout-tutorial
Hope this helps! :)
for those who want more than xCode editor has to offer this is my hack and its not future proof,
based on this post we can scale font programmatically
Warning!! SCALE FACTOR is not refined yet.
you will need to work on it.
usage:
titleView.font = UIFont.appFont(ofSize: 24, weight: .bold)
contentView.font = UIFont.appFont(ofSize: 16)
code:
extension UIFont {
class func appFont(
ofSize size : CGFloat = UIFont.systemFontSize,
weight : Weight = .regular,
autoScale : Bool = true
) -> UIFont {
return UIFont.systemFont(ofSize: autoScale ? size.dp : size, weight: weight)
}
}
extension CGFloat {
var dp: CGFloat {
let width = UIScreen.main.bounds.width
let device = UIScreen.main.traitCollection.userInterfaceIdiom
if (device == .phone) {
if (width <= 320) {
// iPod(Gen7)
// iPhone(5s, SEGen1)
return self * 0.75
} else if (width <= 375) {
// iPhone(SEGen2 6, 6s, 7, 8, X, Xs, 11pro, 12mini, 13mini)
return self * 0.95
} else if (width <= 414) {
// iPhone(6+, 6s+, 7+, 8+, XsMax, XR, 11, 11proMax, 12, 12pro, 13, 13pro)
return self
} else if (width <= 744) {
// iPhone(12proMax, 13proMax)
return self * 1.2
}
} else if (device == .pad) {
if (width <= 744) {
// ipad(miniGen6, )
return self * 1.4
} else if (width <= 768) {
// ipad(Gen5, Gen6, Air, Air2, Pro9.7)
return self * 1.45
} else if (width <= 810) {
// ipad(Gen9)
return self * 1.5
} else if (width <= 834) {
// ipad(AirGen3, AirGen5, Pro10.5, Pro11Gen1, Pro11Gen3)
return self * 1.55
} else if (width <= 1024) {
// ipad(Pro12.9Gen1, Pro12.9Gen2, Pro12.9Gen3, Pro12.9Gen5)
return self * 1.85
}
}
return self
}
}

Resources