I have a UITextField positioned in a view next to a button. It has a trailing constraint of 8 to the button (which has a trailing constraint of 8 to the superview) and when I type long text in it, it simply scrolls along, which is I want. However, in order to retain the text typed in the field if the view is switched to another one (it's in a tab controller), I save the text in a holder variable and when it switches back to that view, I set the text in the field to the saved text.
The problem is that this causes the field to expand horizontally if the text is long enough, sometimes pushing the button off-screen, even with the trailing 8 constraint. I have tried to save the original frame of the field in a holder variable, and then after setting the text, set the frame to the saved original frame like so:
fieldFrame = messageField.frame
println(messageField.frame.width)
messageField.text = holderMessage
println(messageField.frame.width)
messageField.frame = fieldFrame
However, the field still expands, and it printed out 502.0 twice. My current thought is that the frame hasn't registered the change in width after the setting of the text in time for the println, but I'm not sure if this is correct.
Also, I've read some similar questions that suggested using a width constraint. If I use a less than or equal to width constraint on the field, will it still expand if on a device that's thinner? That is to say, since I'm currently using an any width and any height storyboard, it's wider than, say, an iPhone 6. So if I set a less than or equal to width constraint on the current width of the field, it seems possible that the field can still expand on a smaller device and not break that constraint.
Is there a better way to do such a width constraint? If not, how else can I keep the field from expanding and pushing the button offscreen?
Here's the problem. The text field has a tendency to size itself horizontally to its contents. The button has a tendency to size itself horizontally to its contents. Thus you have an ambiguity: those tendencies have the same priority, so the runtime doesn't know which one to collapse as the text field's text gets too long.
The solution is to adjust those priorities. You must lower the horizontal compression and hugging priorities for the text field - one point lower should be sufficient. Now the button will have a stronger tendency to match its own size to its contents, and the text field will clip its contents.
You can also lower the Content Compression Resistance programmatically (this also works if you are using a UIViewRepresentable in SwiftUi):
uiTextField.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
For more info on this topic please refer to:
https://medium.com/#dineshk1389/content-hugging-and-compression-resistance-in-ios-35a0e8f19118
Selecting the Text view, then within Size inspector:
1) Set "Layout Margins" to "Fixed".
2) Under "Content Compression Resistance Priority", set the "Horizontal" to be "Low (250)".
Related
I have a UIView with 3 UIViews inside, I need every UIView to define it's one height depending of its contents and at the same time, the parent view should change its size depending of its children. But I'm having this issue:
I need the first UIView from the second column's height to always stick to its content and leave the remaining space in the end of the column. In other words, what I want is the second column's first UIView to shrink.
Here's my IB, the greater than or equal constraints are not working as I expected (I highlighted the relevant constraints):
I've tried changing priorities but nothing's working, hope you can help me out, thanks.
EDIT: I forgot to show what's going on inside each UIView. The image might be present or not so if it's not I remove it and "pass" the UIView's height responsibility to the label, just that:
A greater than or equal constraint will always be as small as possible while satisfying all other constraints. So in this case it is correct...
The reason it is correct is because the label you are using will allow its frame to grow beyond its content.
To fix this you need to change the content hugging priority on the two labels on the right hand side.
To do this, select the label and go to the measurements section in Interface Builder property inspector.
Near the bottom you will see "Content Hugging Priority". Change the vertical value of this to 1000 (a.k.a. required).
This will tell the label to keep its frame as small as possible to fit the content. And so, the greater than constraint will have to be taller to satisfy this.
I got it working by extending the labels to the bottom of each UIView, now the UIViews have no excuse to expand more than the label's height.
I have a table cell, and it has a label (which has the username), and a button (which takes to a location). There are constraints set to be the same on the y axis (center vertically to each other) and 5 px trailing/leading to each other. Works great. What doesnt work great, is if the label text is extremely long. It will push the text off the screen. How do I make the button go down to below the label? Similar to float in css?
^^This is the cell, and as you can see it goes off the screen. I need "San Francisco, CA" to be pushed below the label "#VeryReallyReallyLongUsername". I know you can do dynamic cell resizing using AutomaticDimension...
For that you should manage many thing programmatically. You can take outlet of constraint by ctrl + dragging from constraint to class file.
Then you can manipulate it's constant.
So if you want to let your button goes down when text is large then you can take outlet of top constraint of button and then increase it's constant by some pixels that you want and do same for label take outlet of it's width constraint and increase it's constant by some pixels that you want to increase width.
Second thing if you don't want to manage stuff like mentioned above then you can use multi line label. just set the numberOfLines property of your label to 0.
So if text size will be large then label distributes in two line or three line.
Or you can set Autoshrink property from attribute inspector from IB(story board) to minimum font size and set minimum font size with it. so if text is larger then it reduce font size that it fits exactly to label but not reduce more than that minimum size that we have set.
Hope this will help :)
Not easily. It may be possible to set up constraints which would do this, but I wouldn't know where to start.
I would pick another option such as having the label truncate the long value (it will put ... at the end), or to have the label scale the text smaller, or to have the label grow vertically and wrap the text.
Edit: I found the correct solution and will include it at the bottom of the question
I have a textfield. I constrain the textfield to nearest neighbour, which is in this case the view and when i do this, height for the textfield goes out of my control and matches the size of the text exactly. How can I constrain the text box to nearest neighbour without the text box automatically resizing. Below is an image that shows my constraints and the orange outline of what the textfield wishes to resize to. It also shows that the view will shrink to an expected height of 62 aka the size of the text box.
I would add, that when i checked the constraints in inspector, i found that the text box was not always set to first items. when I made the text box the first item in all constraints the width became editable.
Answer:
1) The secret to adding constraints to a text box it to put content hugging as a low priority eg. 250. Look at the above picture and you can see content hugging vertically is at 750 or high priority while content hugging horizontally is at low priority(=250). Therefore i am able to constrain to nearest neighbor for the width which is at low priority but not for the height which is at high priority.
For more info check this documentation
https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/AutolayoutPG/WorkingwithConstraintsinInterfaceBuidler.html#//apple_ref/doc/uid/TP40010853-CH10-SW2
2) each constraint has a first and second item in the inspector. The second item gets its attributes(eg. size) from the first. double click on the contraints in the size inspector as pictured above to inspect the constraint more closely
The layout engine does not know what size you want the parent view (your view controller in this case), so it takes the intrinsic content size of the textfield.
Do you want the textfield to be exactly as big as it needs to be? Then don't touch it, your textfield will resize to contain the text, using your constraints to place itself.
Do you want your view controller to have a certain size? (e.g. if you want a specific background image to fit) Then you might want to consider adding constraints to your parent view so that it keeps a certain size or aspect ratio.
I will suggest to remove existing constraints, and add vertically and horizontally center constraints.
A rather convoluted problem, looking for an elegant solution:
A UITableViewCell subclass has a UILabel on the left and a UITextView on the right. Both label and text view have dynamic content. I am using the text view rather than another label because I need the address link recognition.
The label has a higher compression resistance than the text view, so that its right edge will expand towards the right as needed (up to a certain limit). The text view's width will in turn be compressed. The text view is set to word wrap to expand downward.
Because a text view is a itself a UIScrollView, the normal automatic dimension mechanism of a dynamically sized table view cells view will not work (i.e. using UITableViewAutomaticDimension). Thus, I tried to implement heightForRowAtIndexPath. To determine the necessary height, I thought just need:
The text in the label, plus its attributes
The text in the text view, plus its attributes
The width of the table view (as it is "plain" rather than "grouped")
With this information, I reckoned, I can use sizeThatFits on helper objects to determine the necessary height.
However, I am reluctant to hard-code the horizontal margins between the label and the text view into my size calculation, so theoretically I additionally need NSLayoutConstraint outlets for
The left margin of the label to its superview
The max allowed width of the label
The horizontal distance between the label and the text field
The right margin of the text field to its superview
This seems really insane! While I was writing the implementation of this, I asked myself if there is not an easier way to accomplish the same. (That's why I did not yet write the code to post here.) After all, the only hard problem here is how to get the available width for the text view, but the way to getting there seems unduly complex.
Any ideas for a concise, elegant solution?
Side notes: This has to be calculated before the cell is rendered to be displayed. Also, setting the text view's scrollingEnabled to false produces weird results when using sizeThatFits.
I have a view setup with 4 UITextFields laid out and constraints setup so that it's looking great on all devices and working as expected. I have an issue where by if I set the text using TextField.text = #" some long text "; or [TextField setText:#"some long text"]; it causes the textfield to expand its width to fit the length of the text and then because of the constraints the other textfields resize too.
How can I stop the textfield resizing ? What I would like is the field to stay the same and the user to be able to just scroll the text If they want to get to the end?
Thanks
You just need to change the constraints. Give the UITextField an explicit internal Width constraint (or use constraints to set or limit its width in some other way) and then things will behave as you desire.
Remember, in general all aspects of a view's size and position must be unambiguously configured by its constraints. Right now, however, your text field has no Width constraint, so its width is based on its text contents (that is, it uses the intrinsic content size to obtain its width). Omitting the Width constraint is not illegal, which is why Interface Builder did not complain when you set up the constraints - but it is legal only because there is an intrinsic content size that supplies a width, and thus it does mean that the width changes if the internal text changes. If that isn't what you want, then don't do that - supply an explicit Width constraint instead.
(Similarly, a text field has an implicit height of 30, which is why you do not have to give it a Height constraint. This height does not change with the text contents, because, unlike a UILabel, a text field does not wrap its text onto multiple lines.)