Swift UIScrollView height dependent upon UITextView content - ios

I have a UIScrollView with multiple views inside of it, one of them being a UITextView. I'm trying to set the height of scroll view to match the content. The problem is that the UITextView has a height that varies according to the text it contains. This content is set in the override viewDidLoad() method in the view controller, but for some reason the value I get for the height of the view does not reflect any changes, even after the value of the content has changed. I have a function written to change the height of the content subview by changing it's height constraint constant, which I am calling in the viewDidAppear() method. Relevant code is as follows:
func setPageSize() {
var pageHeight : CGFloat {
var height : CGFloat = 1000 // The height of all content besides the UITextView
let descriptionTextViewHeight = self.descriptionTextView.frame.height
height += descriptionTextViewHeight
return height // Always returns 60, which is the height of view when using the placeholder text from interface builder (doesn't update when text updated).
}
self.pageViewHeightConstraint.constant = pageHeight
}
override func viewDidLoad() {
super.viewDidLoad()
self.descriptionTextView.text = self.description
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(true)
self.setPageSize()
}
Should I call the setPageSize() method somewhere else? Is there a method in UITextView that returns an updated value when new text is entered? I have also tried using the location of the last view in the content view for reference in setting the height, but I'm having the same problem - it's returning a value as if the UITextView's height is always 60, which it's not.

Using
let descriptionTextViewHeight = self.descriptionTextView.frame.height
returns visible height of the textView,
To get height of the scrollable content inside the textView , you can use
let descriptionTextViewHeight = self.descriptionTextView.contentSize.height

OK. I'm not certain why this works, but if I put this line that sets the value of the text:
self.descriptionTextView.text = self.description
in viewDidLayoutSubviews() instead of viewDidLoad() it works. I'm not sure why that is, since viewDidLoad() is called before viewDidLayoutSubviews(), and both are called before viewDidAppear(); but for some reason this returns the proper value of the UITextView's height with:
self.descriptionTextView.frame.height
in the viewDidAppear() method.

Related

iOS TextField view glides out of layout

I'm using TextField in my app. I made it to wrap the content.
The problem is when the user types a long text the TextField edges glides out of the layout and make some of the view invisible.
is there a way to disable it to expend when it reaches to the layout edges?
The best thing to do is to change your UITextField to a UITextView. Here's a function that I like to use quite a lot for this autoresize technique that you'll see in the likes of Apples iMessage:
func containerViewHeight() {
let size = textView.sizeThatFits(CGSize(width: textView.frame.size.width, height: CGFloat.greatestFiniteMagnitude))
containerView.heightAnchor.constraint(equalToConstant: size.height + 24)
self.textView.setContentOffset(.zero, animated: false)
}
You'll want to call this function initially inside of your viewDidLoad:
override func viewDidLoad() {
self.containerViewHeight()
}
As well as that, you'll want to conform to the UITextViewDelegate methods by subclassing it at the top of your file like so:
class YourViewController: UIViewController, UITextViewDelegate
Once you add this AND you have used self.textView.delegate = self inside of your viewDidLoad:
override func viewDidLoad() {
self.textView.delegate = self
}
you'll then be able to use the textViewDidChange method for that textView, so the final thing you'll want to add in your class is this:
func textViewDidChange(_ textView: UITextView) {
self.containerViewHeight()
}
Your textField probably doesn't have a fixed width.
Just put a width constraint to your textField in your storyboard so it will always have the same width, no matter if the text is too long.
Edit : if you want a maximum width, you can add 2 width constraints to your textField. One for minimum width and one for maximum width. This way the width of your textField will vary between 100 and 200, depending on the text it contains.
Minimum width constraint :
Maximum width constraint :

CollectionView takes a while to appear

I am using autolayout anchors to place my collectionView in my view. In my collectionView, I have a list of users. Since the number of cells is not definite, the height is changed based on the height of the content inside the collectionView. This is what have:
override func viewDidAppear(_ animated: Bool) {
let contentViewHeight = collectionView.getContentHeight() // Returns height of the content of the collectionView
collectionView.heightAnchor.constraint(equalToConstant: contentViewHeight).isActive = true
// I create a height constraint based on the contentHeight
self.collectionView.layoutIfNeeded()
}
This code works great, but the only side-effect is that my collectionView loads in a bit late when the view is shown. Everything else in the view is loaded, and the collectionView just pops up after a while. Is there any way I can add my constraint without causing this issue?
I tried moving my code to viewWillAppear and viewWillLayoutSubviews, but the anchor isn't even applied.

Cant get width of table view or table view header until after viewDidAppear

func setTableHeader(){
let headerWidth = Double(tableHeader.frame.width)
// also tried let headerWidth = Double(tableView.frame.width)
print("header width is \(headerWidth)")
}
func viewWillAppear(){ output --> 351
setTableHeader()
//also tried in View Did Appear
}
func viewDidDisappear(){ output --> 296
setTableHeader()
}
The outputs refer to what occurs when run on Iphone SE. I designed my tableview and table header with constraints in Storyboard with the Iphone 6 display. I was able to get the result I wanted by changing setTableHeader to reflect view.frame.width - (left and right constraints from storyboard), however I was wondering why I couldnt get this to work, and what the less hacky work around would be? Also I am in actuality setting the dimensions of table header's subviews inside setTableHeader, but these subviews have no constraints (programatically nor in storyboard), in case that is relevant.
Thank you.
Try this , The solution was to override UIViewController().viewDidLayoutSubviews(), get the proper size of the header view based on it’s constraints, set the frame on the header, and reset it as the table header view
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
// Dynamic sizing for the header view
if let headerView = tableView.tableHeaderView {
let height = headerView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize).height
var headerFrame = headerView.frame
// If we don't have this check, viewDidLayoutSubviews() will get
// repeatedly, causing the app to hang.
if height != headerFrame.size.height {
headerFrame.size.height = height
headerView.frame = headerFrame
tableView.tableHeaderView = headerView
}
}
}
Please try to add this code to viewDidLayoutSubviews method.
All views got their actual sizes only alter layout. Method viewDidLayoutSubviews called before viewDidAppear and after viewWillAppear.

Custom UIView in UITableView cells with UITableViewAutomaticDimension enabled

I have UITableView with UITableViewAutomaticDimension and some estimatedRowHeight. For this table I am using custom UITableViewCell which contains some label and custom UIView with overridden intrinsicContentSize(). Constraints setup is correct and table is able to determine actual height for each row. So far so good.
Now I started to modify internal logic of my custom view to adapt it's appearance based on available width i.e. when table cell size is not wide enough my view can rearrange subviews to fit new limitation and this have impact to resulting height, so I have code like that:
var internalSize: CGSize = ...
override func intrinsicContentSize() -> CGSize {
return internalSize
}
override func layoutSubviews() {
super.layoutSubviews()
fitIntoWidth(frame.size.width)
}
private func fitIntoWidth(width: CGFloat) {
let height = // calculate based on content and width
internalSize = CGSizeMake(width, height)
invalidateIntrinsicContentSize()
}
Now, when I populate table view, intrinsicContentSize() returns some desired value but it is not good fit for current layout, then control goes to layoutSubviews() where size get recalculated and system again calls intrinsicContentSize() and now it returns good value. However, first time table loads data and cell heights calculated based on incorrect intrinsicContentSize() values. If I call reloadData() again all becomes fine and layout is also ok for all upcoming cells in table.
Where is my mistake and how to modify code to make cell sizing work correctly without calling reloadData() twice?

Resize textField Based On Content

How can a textField be resized based on content while using auto-layout in an iOS application written in Swift?
The text field will resize as necessary to fit its content when the view loads as well as while the user is typing.
Ideally, the text field would stop resizing at a certain point, say, 6 lines, and become scrollable.
You have to use an UITextView instead of an UITextField.
Then, you can use the sizeThatFits method.
But first you have to know how high one line will be. You can get that information by using lineHeight:
var amountOfLinesToBeShown: CGFloat = 6
var maxHeight: CGFloat = yourTextview.font.lineHeight * amountOfLinesToBeShown
After that, just call the sizeThatFits method inside your viewDidLoad method and set the maxHeight (line * 6) as your textview height:
yourTextview.sizeThatFits(CGSizeMake(yourTextview.frame.size.width, maxHeight))
Swift 3
var textView : UITextView!
override func viewDidLoad() {
super.viewDidLoad()
textView = UITextView()
textView.sizeThatFits(CGSize(width: textView.frame.size.width, height: textView.frame.size.height))
}

Resources