How to make custom implemented textView supporting AutoLayout? - ios

I create a custom textview (subclass of UIView, draw text with TextKit). I want to make the textview work with AutoLayout, i.e. I set the left and right margin, then it calculate the height itself automatically.
I think I should override the intrinsicContentSize size, but this function have no input. Height cannot be calculated without the constrained width.
I try to override sizeThatFits, which have an constrained size that I want, but it doesn't be called when using AutoLayout.

This intrinsic size must be independent of the content frame, because there’s no way to dynamically communicate a changed width to the layout system based on a changed height, for example.(from documentation)
In your case I'd try to add a height constraint manually inside your custom textView and set its value when you calculate size based on text

Related

Why does intrinsicContentSize for UIView always return (-1.0, -1.0)?

A UIView with proper autolayout is not returning a correct intrinsic content size. I am getting (-1.0, -1.0) even though I can see that it is sized correctly both in the storyboard and when the app runs.
I have tried creating a Playground that creates a UIView (a) and a UILabel.
I add my label as a subview of (a) and set left, right, top, bottom constraints.
I also set some text and verified that my label has a correct intrinsic size (which it does).
My (a) view returns (-1.0, -1.0) for intrinsic content size.
I have also tried creating a new project with a storyboard.
I added a UIView (a) to my view and centered it (X and Y).
I added a UILabel to my (a) view and constrained left, right, top, bottom.
I can see that the (a) view sizes correctly. When I run the app I see that it is still properly sized.
My (a) view returns (-1.0, -1.0) for intrinsic content size.
I find this extremely strange considering that the UIView does layout according to its intrinsic content size (i.e. the subviews and associated constraints).
Assuming that my labels constraints to the edges of my (a) view are all distances of 0, then I would expect my (a) view to have an intrinsic content size equal to that of my label's frame (which is based on the intrinsic size of the label, but that is kinda moot).
I genuinely don't understand how a view can set its frame equivalent to the intrinsic size of its subelements while not having an intrinsic content size.
(The frame of (a) view returns the correct size, but that doesn't really answer my question. Also - it isn't helpful for my particular situation, but that is outside the scope of this question.)
I find this extremely strange considering that the UIView does layout according to its intrinsic content size (i.e. the subviews and associated constraints).
This is just a misunderstanding of what the "intrinsic content size" is. An ordinary UIView has no (meaningful) instrinsic content size.
The intrinsic content size is a property that some interface objects implement (such as UILabel and UIButton) so that you do not have give them height and width constraints. These are special interface objects that contain text (and/or, in the case of a button, an image), and can size themselves to fit that.
Your UIView isn't like that, and basically doesn't implement the intrinsic content size at all. It is possible to write a UIView subclass that does implement the intrinsic content size, behaving like a UILabel or a UIButton; but you have not done so.
You say in a comment:
If I have a UIView with only a Center X and Center Y constraint and given it has some contents, I would like to know what its size is after autolayout operates
Fine, but that is not its intrinsic content size. It is its size. If you are in the view controller, implement didLayoutSubviews and look at this view's bounds.size. That is the size you are after.
I'm curious how I could calculate this
Ah, ok. But then you should have asked that. The answer is: call systemLayoutSizeFittingSize.
iOS intrinsicContentSize
intrinsicContentSize means that view calculates it's size internally (usually in runtime). For example UILabel can find out it's size based on text, font, style...
iOS operates by explicit and implicit constraints. Manually you set explicit constrain(in xib or in code). AutoLayout converts intrinsicContentSize into implicit constraint.
For example basic UIView doesn't have intrinsicContentSize, as an opposite UILable, UIButton has it, that is why it is simple to use them with dynamic content. For this reason you must set width x height explicitly for UIView

How to implements a custom view compatible with AutoLayout that has dynamic height?

I want to implements a customize, and the view's height can be calculated according to it's content and width. The view's width is arbitrary and in IB usually set leading and trailing offset. The question is how can I get the width of this view and do the layout?
A similar view is UILabel, if set numberOfLines property to 0 it can adjust it's height according to content and width. UILabel has a property preferredMaxLayoutWidth, during constraints update, this property is set to correct width and using these do the work. And the same, when and how this property is set?
For example, i have a grid image which look like this
If width is given, then we can calculate the height of the view. And I have pin it to a cell like this
So the cell's height will adjust according by the grid image view's height.
But how did i know the exact width of the grid image view? The actually result is following when the table view first shown.

Adjust UILabel size based on size Swift

I'm trying to adjust my label size based on screen size using swift. I'm using the dynamic layout and cannot figure it out. Essentially what I am looking to do is shrink the text/font size to fit within my frame.
You can do this all in the storyboard.
Add constraints between the leading and trailing edges of the label and its superview. (Something like |-[label]-| in the visual format.) This will adjust the width of the label as the width of its superview changes.
If you want the font size to adjust as well, you can set the Autoshrink item in the storyboard to Minimum Font Scale and set an appropriate value (0.5 is the default).

Autolayout constraints to make UILabel more sensitive to taps

I have 2 tappable labels grouped within a parent view (The attached screenshot shows 3 of these views). Currently the size of each label is set to fit its content. Therefore a tap gesture is only recognized when the tap is directly on the text. I want to be able to tap above or below the text (but within its parent view) so that a tapped label is triggered. What type of constraints would I need to add between the views so that I’m able to do this?
You could add minimum widths and heights to the labels, and that might do what you want (width >= minimum). Alternatively, you could add these labels as the child of a larger UIView that does the gesture recognition. You should add constraints to that UIView so that it expands as the UILabels expand, and again, you'd still need minimum widths and heights.
You would add a height constraint to each label. This is not normally needed, because a label has an intrinsic content size (intrinsicContentSize) that is used to generate its height automatically, through two low-priority constraints that cause the label to resist getting larger than or smaller than the intrinsic height. But if you simply add a height constraint, you can make the label taller, overriding the intrinsic content size; this works because your constraint has a higher priority than the intrinsic height constraints.

Fluid UI layout on iPhone

I have an Android app with a UI like this for viewing emails:
I'm trying to port this to iOS and need it to work with iOS 5.0 and above (so can't use auto-layout in iOS 6.0). Hopefully you can tell how the layout should adjust/flow based on the example.
What would be the best way to handle this type of layout? The From and Re lines need to be variable height as shown (actually the To: line as well). The message body needs to be variable height of course.
My only attempt so far has been trying to use UITableViewController with static cells. I am able to get the variable height that way, by using sizeWithFont inside heightForRowAtIndexPath, to return the required height for each row. Using that method I'm having a heck of a time trying to get the style I want (rounded corners and background only for the top part).
So is there a better way? Maybe something that uses Collection View or Container View? On some other screens I need to port I have similar issues, but they have more levels of nesting (rounded blue section inside a white section inside a rounded blue section). Or would I be better off not using IB and building the entire UI in code from just basic label elements and generic views?
The easiest way I can think of is to manually compute for the label frames inside viewDidLayoutSubviews. Here's some pseudo-code:
On creation:
In IB, put all labels as subviews of the blue area. Check that the autoresizing mask of the container sticks to the top, left, and right, as well as have stretchable width. We'll fix the height and the subview frames in code. The message body can be a label or a textview as a separate view.
In viewDidLoad, set the containing view's layer cornerRadius, borderColor, etc. as appropriate.
In viewDidLayoutSubviews:
Time label: Easy. Just set the width to the superview width minus some padding, set the height with sizeWithFont:
For To:, From:, and Re:; call sizeToFit. Get the max width and hold on to that.
To: label: Set the x to 0 and y to the time label's bottom.
Receiver's name label: Set x to the width you got from (2.) and y to same as (3.). Set width to (container width - (2.)) and height with sizeWithFont:.
Do the same steps from (3.) to (4.) for the From: and Re: rows.
Set the blue view height to the frame bottom of the subject label.
Fill the rest of the frame with the body textview/label.
You have to add paddings on your own because sizeToFit and sizeWithFont: won't do that for you. Also, the body UITextView can scroll on it's own, but if you are expecting long subject titles then you should wrap the whole thing in another UIScrollView (or in IB just set the main view's class to UIScrollView)

Resources