I am trying to change the height of a UITextView based on the size of its content using textViewDidChange delegate call. But text is pushed up a bit while the first line is entered and is corrected to old position when the next line is entered, This keeps on repeating for every alternate line added.
func textViewDidChange(textView: UITextView!) {
var computedHeightDifference = textView.contentSize.height - textView.frame.size.height
if(computedHeightDifference != 0){
textView.frame.size.height = textView.contentSize.height
}
}
I tried using textView.sizeToFit() instead of the complete block but the text view blinks when each line is added(Same behaviour can be noticed in the notes field while adding new contact in the Phone application.
I have uploaded the complete code on GitHub
You're not setting the height large enough. You need to account for the text container's inset.
Do this:
func textViewDidChange(textView: UITextView!) {
var computedHeightDifference = textView.contentSize.height - (textView.frame.size.height + textView.textContainerInset.top + textView.textContainerInset.bottom)
if(computedHeightDifference != 0){
textView.frame.size.height = textView.contentSize.height + textView.textContainerInset.top + textView.textContainerInset.bottom
}
}
Related
I want to have a cell with a header with title and switch. When the switch is on, it should show the content expanding the cell with an animation. The header should be always visible and have a constant height.
The cell consists of a container view with 2 subviews, the header, and a body. The body has a label for the cell's content.
The cell has a delegate that tells the controller to reload the content like this:
tableView.beginUpdates()
tableView.endUpdates()
(Nothing in between. Should I specify something just to be sure?)
I have tried having a height constraint for the header and one for the body and when the switch updates its value, I update the body's constraint to the height of the label (the calculation is being done correctly). I have also tried having a single constraint for the container (constant header height + label height + padding) which also didn't work.
The controller has is the tableView's delegate. I'm not setting a estimated row height (should I?) and for heightForRowAt I'm returning UITableView.automaticDimension.
If you have any suggestion on how it could be done, even for a different view/subviews organization, please comment.
EDIT:
This was actually a silly mistake. I was updating the constraints after I was calling my delegate cell. For future reference I'm posting here the code.
func configure(with reminder: Reminder, delegate: CellUpdateDelegate) {
self.delegate = delegate
self.reminder = reminder
bodyLabel.text = reminder.body
headerTitle.text = reminder.title
let height = bodyLabel.requiredHeight + 16
containerHeight.constant = 48 + (alertSwitch.isOn ? height : 0 )
}
#objc func switching() {
let height = bodyLabel.requiredHeight + 16
containerHeight.constant = 48 + (alertSwitch.isOn ? height : 0 )
delegate?.didSelect(reminder)
}
I am tying to modify the height of a UITextView dynamically (up to a max height) while the user enters text. I am experiencing a very strange behavior when there are an even number of lines in the text view.
I am using autolayout and the text view has a height constraint. I respond to calls to the text view's delegate (textViewDidChange(_:)), where I calculate and adjust the height constraint based on the contentSize.
Here is the code:
func textViewDidChange(_ textView: UITextView) {
let newHeight = textView.contentSize.height
let newConstraintConst = max(MinTextViewHeight, min(MaxTextViewHeight, newHeight))
self.textViewHeightConstraint.constant = newConstraintConst
}
This works well, it resizes the frame up to MaxTextViewHeight and then the text view can scroll. However, when there are an even number of lines in the text view, the text view adds a kind of offset to the bottom of its NSTextContainer, causing the top line to be cut off:
However, when there are odd lines the NSTextContainer is no longer offset:
At first I thought it was somehow being controlled by the text view's textContainerInset but that is only used to pad the space inside the NSTextContainer, as setting it to .zero removes the space inside but does not affect the offset (and incidentally makes it even worse, as the top line almost completely disappears):
I have looked through the UITextView class reference and I don't see any property that would let me manipulate or even get the value of this offset.
As a workaround I am increasing the text container's top inset and removing the bottom inset:
textView.textContainerInset = UIEdgeInsetsMake(10, 0, 0, 0)
This works so far, but I arrived at a value of 10 by trial-and-error, and so far I've only tested it on a single device.
I am not interested in more hacky workarounds that require fragile, fixed values; I am trying to understand how this offset is being set and a proper way to fix it. I'm hoping that someone can provide some insight, thanks!
Just a speculation, but I think the problem is that the text view assumes that the height of itself does not change while calling textViewDidChange, so it scrolls when it thinks it has to, regardless of you changing its frame.
Not sure if you think my solution is too hacky, but this will stop it from scrolling when you don't want it. I simply pin the content offset to the top as long as the wanted content size is smaller than your max size.
Just add this:
func scrollViewDidScroll(scrollView: UIScrollView) {
if textView.contentSize.height <= MaxTextViewHeight && textView.contentOffset.y > 0.0 {
textView.contentOffset.y = 0.0;
}
}
I have multiple uitextfields setup and they are all connected with IBOutlets. I have one textfield that is a password and I have the 'Secure text Entry' selected. when I have this check I get this
Any ideas why this happens? If i deselect the secured entry the textfield rises fine depending on the size of the password with no ellipsis dots.
It does not matter how long the password is. Same thing.
If i don't have the security text selected it works fine
Any idea why? It can be a width issue because it does autosize. But why does the 'secure text entry' cause the issue?
I have faced the same problem. I think it is a bug of UITextfield. It calculates size for the text but not for the secure text(dots). I have the problem when the text includes slim characters like 1, l etc...
As workaround I have subclassed the UITextfield class and overridden intrinsicContentSize function. You might need to adjust letter spacing. I couldn't find how to get it dynamically depending on font.
override var intrinsicContentSize: CGSize {
let size = super.intrinsicContentSize
if !self.isSecureTextEntry {
return size
}
var width = size.width
if let font = self.font,
let charCount = self.text?.count {
width = "•".size(withAttributes: [NSAttributedString.Key.font : font]).width * CGFloat(charCount)
width += (CGFloat(charCount)+1) * 4.5 // this magic number is for letter spacing
}
return CGSize(width: width, height: size.height)
}
I'm new to iOS and autolayout. I'd like to implement a simple UI like iMessage. The structure is simple like,
---------------
TableView (for history content)
---------------
charBarView ( contains inputTextView | sendButton )
---------------
The constraints for inputTextView is TopSpace/BottomSpace/Leading with charBarView, and Trailing with sendButton. Also I set its height equals 45, while charBarView height unset.
The problem is, when I try to handle user input more than one line, to implement the delegate textViewDidChange. The code :
func textViewDidChange(textView: UITextView) {
if textView != inputTextView{
return
}
var textViewFrame = inputTextView.frame
let sizeDiff = inputTextView.contentSize.height - textViewFrame.height
var chatBarViewFrame = chatBarView.frame
chatBarViewFrame.origin.y -= sizeDiff
//chatBarViewFrame.size.height += sizeDiff
chatBarView.frame = chatBarViewFrame
inputTextViewFrame.size = inputTextView.contentSize
inputTextView.frame = inputTextViewFrame
var tableViewFrame = tableView.frame
tableViewFrame.size.height -= sizeDiff
tableView.frame = tableViewFrame
}
When I comment the code
//chatBarViewFrame.size.height += sizeDiff
it looks work fine, but the height of chatBarView is unchanged, so the button is moved up when charBarView changed(I want to keep it at bottom always).
When I put the code
chatBarViewFrame.size.height += sizeDiff
back, then the charBarView origin and height is changed, but the height of inputTextView will never change.
I use xcode 7 with iOS 8/9.
Thanks for any help!
I am trying to make a tableview that is similar to apps like facebook, where it shows the post's texts and if the post is too large, it will cut the size down and have a "Read More" button. I have looked at many different solutions for resizing UITextViews to the height of their text, however none of them are solving my problem. My problem is only existing with large posts. When I have a large post, my function that returns the height of the textView should return the maxHeight of the posts before being expanded. This part works. However, when I click my "Read More" button, I have a massive amount of white space at the bottom of the full textView. Here is my code:
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return heightForTextOfRow(indexPath.row) + PostCell.additionalVertSpaceNeeded
}
func heightForTextOfRow(row: Int) -> CGFloat {
var textView = UITextView(frame: CGRectMake(0, 0, prototypeTextViewWidth, CGFloat.max))
let post = data[row]
textView.text = (post.postText as NSString).stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet())
textView.textContainer.lineFragmentPadding = 0
textView.textContainerInset = UIEdgeInsetsZero
textView.font = PostCell.textViewFont
textView.frame.size = textView.sizeThatFits(CGSizeMake(prototypeTextViewWidth, CGFloat.max))
if textView.frame.size.height > maxHeight && !post.seeMore {
return maxHeight
} else {
return textView.frame.size.height
}
}
Some explanations of the code:
data is an array of Posts (the only thing that is relevant about the class Post in this case is that it has a property postText and seeMore.)
PostCell.textViewFont is a constant in one of my classes that returns the font I am using (System font size 13)
PostCell.additionalVertSpaceNeeded is a constant that returns the height of all other elements in my prototype cell except for the UITextView
prototypeTextViewWidth is defined as tableView.frame.width - 16 (16 is the value of the margins added together)
maxHeight is the max height of a post that does not want to show the full post (currently defined as tableView.frame.height * 0.625 - PostCell.additionalVertSpaceNeeded)
seeMore is a Bool property of Post that tells whether or not to show the full post or whether to show just a portion of the post (true shows full post, false shows a post of height maxHeight)
Any help would be appreciated... I have tried many solutions to resizing the UITextView and all have this same problem. Thanks in advance!