Text in textview is not vertically centered - swift

I'm trying to create a text view which height follows number of line in it. I've worked on constraint but here I've a curious issue :
At the beginning all is centered as I want
When there is a break, text is not vertically centered anymore and goes in top left corner
When you delete characters and then come back to previous line, the text is yet vertically centered
Here is the code
func textViewDidChange(textView: UITextView) { //Handle the text changes here
if(textView.text != ""){
self.animateSendButton(true)
}else{
self.animateSendButton(false)
}//the textView parameter is the textView where text was changed
heightTextfieldConstraint.constant = self.textField.contentSize.height + 2
textField.contentInset.top = 1
}
I don't understand why there are these 3 different cases, do you have any idea to solve it ?

You can try and add an inset to the top, bot, left and right, that should keep the text centered even after a second line appears.
textView.textContainerInset = UIEdgeInsetsMake(5, 5, 5, 5)

You might be better off keeping the text view itself vertically centered while ensuring that the text view is always sized to fit the content. You can do this with Auto Layout.
Set the text view to be vertically centered in its container and also add a height constraint to the text view with a low constant value (about the height of 1 line of text). Then set the text view's Content Compression Resistant Priority to a value that is higher than the text view's height constraint (see screenshots).
I've done it in "Main.storyboard" of this demo project of mine if you want to see a working example:
https://github.com/patricklynch/Walkthrough

Perhaps you could call view.layoutIfNeeded() after text has been entered.

Related

Random table view cell height is not working perfectly

I am designing a chat app in which I am using the UITableViewAutomaticDimension property of table view when I am setting the label lines to 6 then all cells height is according to the text which label contains but when I increase the number of lines for label then some cells have extra height. Even when scrolling it then cell height again changed.You can check the image so that you can understand it easily thanks.
You need to give label constraints from top and bottom, rather than center vertically.
Give vertical content hugging priority

Expanding UITextView inside a Stack View with scrolling enabled

I am trying to achieve something with Auto Layout and Stackview. I have a Vertical Stackview with a UIView, UITextView, and UIView in it as shown below.
I have checked out the previous answers here but couldn't find a clean solution to achieve this.
The UITextView is editable and must expand as the user types in it - for this I have disabled the scroll for the UITextView in the IB. I also have a height constraint set on the UITextView set to "Greater than or equal to" 10 and number of lines set to 0, so that the UITextView takes the intrinsic height at run time while the user types. I also want the expansion of the UITextView to continue until the UIStackView bottom edge reaches the Keypad accessory's top edge. I have been somewhat able to achieve the expanding UITextView part with my stack view pinned to the top, trailing, and leading edges of the Superview but I keep getting an error that the stackview needs Y position or height which is understandable but if I give it a fixed height or bottom constraint then the UITextView simply won't expand.
I also am not sure how to stop the expansion of the UITextView when it reaches the top edge of the Keyboard accessory view.
The code that I have so far is
let allowedHeight = self.view.frame.height - (self.keyboardHeight! + self.accessory.frame.height)
Basically find the allowed height in the view frame by subtracting the keyboardheight+Accessory view height. This calculation happens correctly. Next I do this (inside textViewDidChange) in hopes that just enabling/disabling the scroll on the UITextView would work but it clearly doens't since the whole text view then weirdly jumps up and down with every key stroke.
func textViewDidChange(_ textView: UITextView) {
if stackView.frame.height >= allowedHeight{
textView.isScrollEnabled = true
}
if stackView.frame.height < allowedHeight{
textView.isScrollEnabled = false
}
}
What is the best way to achieve what I am looking to do?
Couple notes...
I think you'll be better off setting the Max Height of your UITextView rather than of your UIStackView. The Text View will expand / contract based on its content, so you can set a <= height constraint.
Calculate the "elementsHeight" - the vertical size used by your top and bottom views, plus any spacing - and set the "max text view height" to the view height minus elementsHeight ... and subtract keyboard height when visible.
Update the text view's height constraint constant when "max text view height" changes.
Then set up an Observer for the text view's .contentSize ... and enable / disable scrolling based on .contentSize.height compared to "max text view height".
Needs a bit of hoop-jumping, to make sure you update sizes when subviews are laid-out, and to make sure you don't get in a recursion loop.
This is how I set it up:
The initial height constraint on the text view is <= 40 - but that doesn't really matter, as it will be changed via code every time the views layout differently.
I put up an example on GitHub -- it works, but is really just a starting-point. Take a look if you're interested, and pick it apart. https://github.com/DonMag/ExpandingTextView

Auto layout constraints not taking effect on child controls until parent container is resized (swift)

I have a program with an NSSplitView (containing two panes) filling the window. The left pane is an NSStackView and it contains three NSBoxes. The top two each contain an NSTextField for displaying a string of weather information that can change length every time it is updated by code in the background. I have added the relevant constraints to the top two NSBoxes (and their child NSTextFields) to make them expand or shrink to fit the size of the text fields inside them as the text changes length. The third NSBox fills up the remaining space in the stack view.
The problem is that when the window loads, the NSBoxes (as expected) display the correct size as designed in interface builder to fit the default string in the text fields. However, when the update code kicks in and changes the text of the text fields to the downloaded data, which is longer than the default string, the NSBoxes do not adjust their height to fit the larger text. They only change their height when the splitter of the split view is moved. This is annoying because I have to move the splitter every time the number of lines in the text fields changes. Why is this happening and how can I make the boxes update their height to fit the text when it is longer or shorter than before?
It is much like this question (the only source of information I found on my problem): NSScrollView with auto layout not resizing until first manual window resize, however the solution did not work for me.
Below is an image of the interface (again, the top two boxes should resize to fit their text but this only happens when the splitter is moved). It shows longer text than the boxes can display and they haven't resized:
One option is to create a subclass of NSBox and override the intrinsicContentSize property. In your implementation you'd measure the height of the text contained in the box's text field, take account of any vertical padding that exists between the vertical edges of the text field and the corresponding edges of the box, and return the result of your calculation as the height part of the NSSize return value. Then, any time you want the boxes to resize, you can call invalidateIntrinsicContentSize.
I created a sample app to see if I could get this to work which I've posted here. There are a couple of tricky parts which you should be aware of:
Calculating Text Height
The most involved bit of coding in this approach is calculating the height of the text. Fortunately, Apple has documentation that tells you exactly how to do it; here's how that looks in Swift:
// Properties of your NSBox subclass
var layoutManager: NSLayoutManager {
return textStorage.layoutManagers.first! as! NSLayoutManager
}
var textContainer: NSTextContainer {
return layoutManager.textContainers.first! as! NSTextContainer
}
var typesetter: NSTypesetter {
return layoutManager.typesetter
}
// The textStorage object lies at the heart of the text stack. Create this
// object first, the rest follows.
lazy var textStorage: NSTextStorage! = {
var initialString: String = ""
var ts = NSTextStorage(attributedString: self.textField.attributedStringValue)
var lm = NSLayoutManager()
lm.typesetterBehavior = NSTypesetterBehavior.Behavior_10_2_WithCompatibility
var tc = NSTextContainer()
lm.addTextContainer(tc)
ts.addLayoutManager(lm)
return ts
}()
Setting the hugging-resisting priorities for your NSBox subclass
To get the demo working correctly I found that I needed to set the vertical hugging priority and vertical compression resistance values of the NSBox objects to 900.

Left-aligned input text starting at fixed location

I'm writing an app in Swift 2.2 targeting iOS 8 and using Eureka Forms. For rows of type TextRow, I would like to have all of the textFields be left aligned starting at a particular distance from the edge of the screen, roughly equal to the width of the longest title. For example:
Prompt1 textField
A longer prompt textField
Prompt2 textField
I have found that I can get left alignment with the following code:
TextRow.defaultCellUpdate = { cell, row in
cell.textField.textAlignment = .Left
}
For setting the left position, I found that I could use textFieldPercentage but it doesn't quite have the behavior I want since the app works in both portrait and landscape. Is there a way to set either the textField or title label width to a fixed value?

UILabel not wrapping correctly after text change

I'm working via the IB and have a UILabel that's stretched almost to the end of the layout. I have it set with Lines=2, because the max amount of lines it should take is 2. However if it's only 1 line long, I would like it to have a vertical justification of top.
Label settings in IB:
Lines:2
Line breaks: Word wrap
In code, in the viewDidLoad method I set the text of the UILabel. However as part of the functionality at a point the text must change. This is my code:
[_main_lbl1 setText:[myUI MAIN_TITLE]]; //Always only 1 line
[_main_lblsub1 setText:[myUI SUB_TITLE]]; //May be 1 or 2 lines
[_main_lblsub1 sizeToFit]; //Causes vertical alignment (I believe)
Whenever I change the text and rerun sizeToFit, the text wrapping becomes totally messed up. Instead of reaching almost the end of the UILabel as set up in the IB, in some cases the text will wrap at little more than half the distance, in some cases it doesn't wrap at all.
Image of layout in IB:
Image of resulting label in simulator:
In the first label it seems to be working ok, the second label doesn't wrap at all.
Is there anything I have to do to keep the text wrapping when changing the UILabel text? Anything else I'm missing?
Note: Updated question to include more detail and pics.
Thanks
The issue is that you're using sizeToFit. Which stretches the label out to fit the text. If you need to change the size you can use:
CGSize maxSize = CGSizeMake(320, 9999); // 999 means it can be as tall as you like
CGSize textSize = [label.text sizeWithFont:label.font
constrainedToSize:maxSize];
label.size = textSize;
You shouldn't have to do anything special. It will automatically wrap the text to fit when you change it. Otherwise the text would run out of the text labels bounds (which is not what you want). Your problem is that sizeToFit permanently changes the frame of the label. It makes it as small as possible while still showing the text. You are having it resize its frame to the original text and then you are changing the text so it is no longer sized properly. You should reset the frame back to it's original, change the text, and finally call size to fit again.
In viewDidLoad:
self.originalFrame = self.mainLabelSub1.frame;
Then in viewWillAppear:
self.mainLabel1.frame = self.originalFrame;
self.mainLabel1.text = #"New Text";
Note:
A good way to see the borders of the text label to get an idea for the wrapping potential is to temporary set the background of the label to something like magentaColor that stands out.