UIView dynamic height depending on Label Height - ios

I have a Label which takes dynamicaly some data from database.
These data are strings which can sometimes be 3-4-5 rows etc.
So this labe is inside a UIView.
--UIView
--Label
How can i make the UIView to take the certain height of the Label dynamicaly??

you can just do it with storyboard this pics
set the label height relation to greater than or equal
and set the view height relation to greater than or equal
it work like a magic

Bellow is working solution of your problem. I used autoLayout. In testView you don't set heightAnchor
let testView: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = UIColor.redColor()
return view
}()
let testLabel: UILabel = {
let label = UILabel()
label.numberOfLines = 0
label.translatesAutoresizingMaskIntoConstraints = false
label.text = "jashfklhaslkfhaslkjdhflksadhflkasdhlkasdhflkadshkfdsjh"
return label
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(testView)
testView.centerXAnchor.constraintEqualToAnchor(view.centerXAnchor).active = true
testView.centerYAnchor.constraintEqualToAnchor(view.centerYAnchor).active = true
testView.widthAnchor.constraintEqualToConstant(100).active = true
testView.addSubview(testLabel)
testLabel.topAnchor.constraintEqualToAnchor(testView.topAnchor, constant: 10).active = true
testLabel.leftAnchor.constraintEqualToAnchor(testView.leftAnchor, constant: 10).active = true
testLabel.bottomAnchor.constraintEqualToAnchor(testView.bottomAnchor, constant: -10).active = true
testLabel.rightAnchor.constraintEqualToAnchor(testView.rightAnchor, constant: -10).active = true
}

I know this is late answer, but it might help someone else.
To make the Dynamic height for UIView follow the simple steps in Storyboard
Add a UIView in UIViewController and set your favourite background colour
Now set the following constraints Leading, Top, Trailing and Height(as of now). We can adjust the Height constraint to achieve dynamic height further.
Update Height Constraints as shown below:
Now probably there storyboard will show you inequality constraint ambiguity. But we are going to fix this now. Just add a label inside UIView as shown
Now Set the Constraints for Label Leading, Trailing, Top and Bottom
Hurrah, Now the UIView height will increase based on the label's height. just make the following changes to label
This technique works with other views inside this UIView. The thing is you must specify bottom constraints for the views present inside this UIView.

first calculate the size of label with the text it contains, using this function
func calculateSizeOfLabel(text:String,labelWidth:CGFloat,labelFont:UIFont)->CGSize{
let constrainedSize = CGSizeMake(labelWidth , 9999)
var attributesDictionary:[String:AnyObject] = [:]
attributesDictionary = [NSFontAttributeName:labelFont] as [String:AnyObject]
let string:NSMutableAttributedString = NSMutableAttributedString(string:text, attributes:attributesDictionary)
var boundingRect = string.boundingRectWithSize(constrainedSize, options:.UsesLineFragmentOrigin, context:nil)
if (boundingRect.size.width > labelWidth) {
boundingRect = CGRectMake(0,0, labelWidth, boundingRect.size.height);
}
return boundingRect.size
}
and then apply the height of returned size to the UIView like this
let labelText = description.text
let labelWidth = description.bounds.width
let labelFont = description.font
let calculatedHeight = calculateSizeOfLabel(labelText,labelWidth:labelWidth,labelFont:labelFont).height
DescView.frame = CGRectMake(DescView.frame.origin.x, DescView.frame.origin.y, DescView.bounds.width,calculatedHeight)

Below code will resolved your issue :
//Adjust View Height
[yourView setFrame:CGRectMake(yourView.frame.origin.x, yourView.frame.origin.y, yourView.frame.size.width, yourLable.frame.size.height + yourLable.frame.origin.y + extraspace)];

- (IBAction)action:(id)sender {
self.label.text = #"UIImage *imageOne = [UIImage imageNamed:#RosePot.jpUIImageJPEGRepresentationg";
NSLog(#"%f",self.label.bounds.size.height);
float height = [self getHeightForText:#"UIImage *imageOne = [UIImage imageNamed:#RosePot.jpUIImageJPEGRepresentationg" withFont:[UIFont fontWithName:#"HelveticaNeue" size:15] andWidth:self.label.bounds.size.width];
NSLog(#"%f",height);
self.constraint.constant = height + self.viewOne.bounds.size.height;
}
-(float) getHeightForText:(NSString*) text withFont:(UIFont*) font andWidth:(float) width{
CGSize constraint = CGSizeMake(width , 20000.0f);
CGSize title_size;
float totalHeight;
SEL selector = #selector(boundingRectWithSize:options:attributes:context:);
if ([text respondsToSelector:selector]) {
title_size = [text boundingRectWithSize:constraint
options:NSStringDrawingUsesLineFragmentOrigin
attributes:#{ NSFontAttributeName : font }
context:nil].size;
totalHeight = ceil(title_size.height);
} else {
title_size = [text sizeWithFont:font
constrainedToSize:constraint
lineBreakMode:NSLineBreakByWordWrapping];
totalHeight = title_size.height ;
}
CGFloat height = MAX(totalHeight, 40.0f);
return height;
}
Give leading ,top, trailing and height constraint for view .
And height outlet of view name as constraint [because i used outlet name constraint]

Related

UITextView doesn't print text on the nextLine?

I put the UITextView inside a UIView. The UIView expands as the user types in the UITextView but the problem is that if the user types on the next line, it doesn't show the text being typed until the user types on the third line, then it shows the text printed on the second line. Same goes with the 3rd line and 4th line, etc.
How can I fix this?
func textViewDidChange(_ textView: UITextView) {
print(textView.text)
let size = CGSize(width: prayerRequest.frame.width, height: .infinity)
let estimatedSize = textView.sizeThatFits(size)
textView.constraints.forEach { (constraints) in
if constraints.firstAttribute == .height {
constraints.constant = estimatedSize.height
}
viewContainer.constraints.forEach({ (constraints) in
if constraints.firstAttribute == .height {
constraints.constant = estimatedSize.height
viewContainer.layoutIfNeeded()
}
})
}
}
If you're using interface builder try setting the number of lines to 0.
Or from code textView.textContainer.maximumNumberOfLines = 10
First of all, you don't need to set height for textView's Parent. Even if you do set the low priority for textView's parent.
See the image, Purple being parent and Yellow is textView.
In viewDidLoad, add the following code:
textView.textContainerInset = UIEdgeInsets.zero
textView.textContainer.lineFragmentPadding = 0
Then, implement textView Delegate method:
extension ViewController : UITextViewDelegate {
func textViewDidChange(_ textView: UITextView) {
let textHeight = textView.attributedText.boundingRect(with:
CGSize.init(width: textView.frame.width, height: .infinity),
options:[.usesLineFragmentOrigin, .usesFontLeading],
context: nil).height
if previousHeight != textHeight {
previousHeight = textHeight
print("Height text: \(textHeight)")
textViewHeight.constant = textHeight
self.view.layoutIfNeeded()
textView.setContentOffset(.zero, animated: false)
}
}
}
textViewHeight is height constraint of textView.
Initialize var previousHeight = CGFloat(0) as instance variable. It will help limit calls to layoutIfNeeded to only when the height is changed.
Bottom constraint of the textView will expand parent view. So you do not need to set Parent's height as well.

iOS UIScrollView's width is different from the width set in constraint

I'm new to iOS auto layout. I want to implement a view pager using UIScrollView with paging enabled. I set the scrollView to (w, h) to (200, 65), but the width of scrollView is always 240 at runtime. As the result, the offset of contents, the label I added into the scrollView, do not match to the scrollView's width. Why are they different? How do I get the right width?
private let contents = ["This is the 1st message",
"This is the 2nd message",
"This is the 3rd message"]
#IBOutlet weak var scrollView: UIScrollView!
override func viewDidLoad() {
super.viewDidLoad()
print("scrollView.bounds = \(scrollView.bounds)")
print("scrollView.frame = \(scrollView.frame)")
var x: CGFloat = 0
for content in contents {
let label = UILabel(frame: CGRectMake(x, 0, 0, 0))
label.text = content
label.sizeToFit()
print("label.frame = \(label.frame)")
scrollView.addSubview(label)
x += scrollView.bounds.width
}
scrollView.contentSize = CGSizeMake(x, scrollView.bounds.height)
}
Output:
scrollView.bounds = (0.0, 0.0, 240.0, 128.0)
scrollView.frame = (80.0, 156.0, 240.0, 128.0)
label.frame = (0.0, 0.0, 178.0, 20.3333333333333)
label.frame = (240.0, 0.0, 185.666666666667, 20.3333333333333)
label.frame = (480.0, 0.0, 182.333333333333, 20.3333333333333)
Your problem here is that you are using a size class wCompact hRegular for this constraints, so if you have another size class (for example different orientations) you don't have these constraints available.
To set the constraints just for iPhone I suggest doing it programmatically:
if (IDevice.currentDevice().userInterfaceIdiom == .Phone) {
// Set constraints or a different UIVIew here
}
And if you have your app available for iPhone Only you can use the class size Any-Any avoiding the programmatically set.

How to adjust font size to fit height and width of UILabel

I have a square UILabel (in yellow color) which contains a single letter.
I have used the following code from this SO answer to adjust the font size such that it fits into the UILabel:
letterLabel.font = UIFont(name: letterLabel.font.fontName, size: 100)
letterLabel.adjustsFontSizeToFitWidth = true
letterLabel.textAlignment = NSTextAlignment.Center
As apparent in the screenshot, the font size is according to the width. But since the text is only one letter, hence we also need to look at the height. How can we adjust the font size such that height is also within the UILabel?
I did not find any simple solution so I made this extension:
extension UILabel {
func setFontSizeToFill() {
let frameSize = self.bounds.size
guard frameSize.height>0 && frameSize.width>0 && self.text != nil else {return}
var fontPoints = self.font.pointSize
var fontSize = self.text!.size(withAttributes: [NSAttributedStringKey.font: self.font.withSize(fontPoints)])
var increment = CGFloat(0)
if fontSize.width > frameSize.width || fontSize.height > frameSize.height {
increment = -1
} else {
increment = 1
}
while true {
fontSize = self.text!.size(withAttributes: [NSAttributedStringKey.font: self.font.withSize(fontPoints+increment)])
if increment < 0 {
if fontSize.width < frameSize.width && fontSize.height < frameSize.height {
fontPoints += increment
break
}
} else {
if fontSize.width > frameSize.width || fontSize.height > frameSize.height {
break
}
}
fontPoints += increment
}
self.font = self.font.withSize(fontPoints)
}
}
I needed only one letter to be shown in label (name initial), so the requirement was clear, it has to scale to fit the height.
Solution:
class AnyView : UIView{
private var nameLabel:UILabel! = nil
override func layoutSubviews() {
super.layoutSubviews()
//Considering the nameLabel has been already created and added as subview with all the constraint set
nameLabel.font = nameLabel.font.withSize(nameLabel.bounds.height * 0.6/*The factor can be adjusted as per need*/)
}
}
I have tested your code works fine for me.
I think cell height is the problem and I haven't give cell height.
Try removing cells height
Try [label sizeToFit] or [label sizeThatFits:(CGSize)]

Adjust UIScrollView's contentSize with an embedded UIView?

I have a UIScroll view which contains only one subview. The subview called contentViewis an UIView. Here is what I did in viewDidLoad():
self.scrollView = UIScrollView()
self.view.addSubview(self.scrollView)
self.scrollView.backgroundColor = UIColor.yellowColor()
// pin all edges to the edges of the superview (self.view)
self.scrollView.snp_makeConstraints { (make) -> Void in
make.edges.equalTo(self.view)
}
// create contentView
self.contentView = UIView()
self.contentView.backgroundColor = UIColor.redColor()
self.scrollView.addSubview(self.contentView)
// pin the edges of the contentView to the scrollView
self.contentView.snp_makeConstraints { (make) -> Void in
make.edges.equalTo(self.scrollView)
}
let myView = UIView(frame: CGRectMake(100, 100, 20, 300))
myView.backgroundColor = UIColor.greenColor()
self.contentView.addSubview(myView)
The result is this:
There is ne red contentView as shown in the previous screen shot.
Next, I tried to adjust the size of the contenView in viewDidLayoutSubviews():
let newSize: CGSize = self.contentView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize)
println("newSize: \(newSize)")
The result is:
newSize: (0.0, 0.0)
How can I either setup the correct size of the contentView or set the correct size of the UIScrollView's contentSize?
have you tried setting the var contentSize: CGSize of the UIScrollView? The documentation says
Discussion The unit of size is points. The default size is CGSizeZero.
If you don't know the size of the content view, then I would recommend setting
scrollView.autoresizingMask = (UIViewAutoresizingFlexibleWidth| UIViewAutoresizingFlexibleHeight);
and
scrollView.autoresizesSubviews = YES;
for good measure.

How do I set adaptive multiline UILabel text?

I have a UILabel named titleLabel in my storyboard nib set to its default height. I want it to programatically expand in height to fit it's content. Here is what I have tried so far:
// just setting content
titleLabel.text = "You don't always know what you are getting with mass-market cloud computing services. But with SimpliCompute, the picture is clear. SimpliCompute gives you powerful virtual servers you can deploy using just your web browser. That’s enterprise grade technology you can deploy and control on-the-fly."
titleLabel.numberOfLines = 0
titleLabel.preferredMaxLayoutWidth = 700
titleLabel.lineBreakMode = NSLineBreakMode.ByWordWrapping
titleLabel.sizeToFit()
None of this works for me in any combination! I always only see one line of text in my UILabel. What am I doing wrong?
I absolutely need the text content to be variable.
I kind of got things working by adding auto layout constraints:
But I am not happy with this. Took a lot of trial and error and couldn't understand why this worked.
Also I had to add to use titleLabel.numberOfLines = 0 in my ViewController
I know it's a bit old but since I recently looked into it :
let l = UILabel()
l.numberOfLines = 0
l.lineBreakMode = .ByWordWrapping
l.text = "BLAH BLAH BLAH BLAH BLAH"
l.frame.size.width = 300
l.sizeToFit()
First set the numberOfLines property to 0 so that the device understands you don't care how many lines it needs.
Then specify your favorite BreakMode
Then the width needs to be set before sizeToFit() method. Then the label knows it must fit in the specified width
This is much better approach if you are looking for multiline dynamic text label which exactly takes the space based on its text.
No sizeToFit, preferredMaxLayoutWidth used
Below is how it will work.
Lets set up the project. Take a Single View application and in Storyboard Add a UILabel and a UIButton. Define constraints to UILabel as below snapshot:
Set the Label properties as below image:
Add the constraints to the UIButton. Make sure that vertical spacing of 100 is between UILabel and UIButton
Now set the priority of the trailing constraint of UILabel as 749
Now set the Horizontal Content Hugging and Horizontal Content Compression properties of UILabel as 750 and 748
Below is my controller class. You have to connect UILabel property and Button action from storyboard to viewcontroller class.
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var textLabel: UILabel!
var count = 0
let items = ["jackson is not any more in this world", "Jonny jonny yes papa eating sugar no papa", "Ab", "What you do is what will happen to you despite of all measures taken to reverse the phenonmenon of the nature"]
#IBAction func updateLabelText(sender: UIButton) {
if count > 3 {
count = 0
}
textLabel.text = items[count]
count = count + 1
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
//self.textLabel.sizeToFit()
//self.textLabel.preferredMaxLayoutWidth = 500
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Thats it. This will automatically resize the UILabel based on its content and also you can see the UIButton is also adjusted accordingly.
It should work. Try this
var label:UILabel = UILabel(frame: CGRectMake(10
,100, 300, 40));
label.textAlignment = NSTextAlignment.Center;
label.numberOfLines = 0;
label.font = UIFont.systemFontOfSize(16.0);
label.text = "First label\nsecond line";
self.view.addSubview(label);
With Graphical User Interface (GUI) in Xcode, you can do the following:
Go to "Attribute Inspector" and set Lines value to 0. By default, it is set to 1.
The Label text can be written in multi-line by hitting option + return.
Now, go to "Size Inspector" and set the width, height, X & Y position of the Label.
That's all.
Programmatically, Swift
label.lineBreakMode = NSLineBreakMode.byWordWrapping
label.titleView.numberOfLines = 2
Programmatically in Swift 5 with Xcode 10.2
Building on top of #La masse's solution, but using autolayout to support rotation
Set anchors for the view's position (left, top, centerY, centerX, etc). You can also set the width anchor or set the frame.width dynamically with the UIScreen extension provided (to support rotation)
label = UILabel()
label.numberOfLines = 0
label.lineBreakMode = .byWordWrapping
self.view.addSubview(label)
// SET AUTOLAYOUT ANCHORS
label.translatesAutoresizingMaskIntoConstraints = false
label.leftAnchor.constraint(equalTo: self.view.leftAnchor, constant: 20).isActive = true
label.rightAnchor.constraint(equalTo: self.view.rightAnchor, constant: -20).isActive = true
label.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 20).isActive = true
// OPTIONALLY, YOU CAN USE THIS INSTEAD OF THE WIDTH ANCHOR (OR LEFT/RIGHT)
// label.frame.size = CGSize(width: UIScreen.absoluteWidth() - 40.0, height: 0)
label.text = "YOUR LONG TEXT GOES HERE"
label.sizeToFit()
If setting frame.width dynamically using UIScreen:
extension UIScreen { // OPTIONAL IF USING A DYNAMIC FRAME WIDTH
class func absoluteWidth() -> CGFloat {
var width: CGFloat
if UIScreen.main.bounds.width > UIScreen.main.bounds.height {
width = self.main.bounds.height // Landscape
} else {
width = self.main.bounds.width // Portrait
}
return width
}
}
extension UILabel {
var textSize: CGSize { text?.size(withAttributes: [.font: font!]) ?? .zero }
func setSizeForText(_ str: String, maxWidth: CGFloat) {
text = str
let dividedByMaxWidth = Int(textSize.width / maxWidth)
if dividedByMaxWidth == 0 {
frame.size = textSize
} else {
numberOfLines = dividedByMaxWidth + 1
frame.size = CGSize(width: maxWidth, height: frame.size.height * CGFloat(numberOfLines))
sizeToFit()
}
}
}
sizeToFit() in the end will shrink the label's width to the widest line after word break
This has worked for me:
Set the numberOfLines property of UILabel to 0
add this line: yourLabel.sizeToFit() after assigning text to the UILabel

Resources