Auto layout: auto update constraints when some view is empty - ios

I have a view like this
_______________________________
| | |
| Label 0 |
| ^ |
| Label 1 |
| ^ |
| Label 2 |
| ^ |
| Label N |
|_______________________________|
^ stands for a constraint that links the top of Label X+1 with the bottom of Label X. Label 0 is constrained to the superview's top with a fixed constant.
What I'm trying to achieve is: if some Label X is empty, then Label X+1 has to take its place.
_______________________________
| | |
| Label 0 (empty) |
| ^ |
| Label 1 |
| ^ |
| Label 2 |
| ^ |
| Label N |
|_______________________________|
|
| The new layout
\ /
_______________________________
| | |
| Label 1 | (here it is also Label 0 but is empty)
| ^ |
| Label 2 |
| ^ |
| Label N |
|_______________________________|
Is this possible with auto layout or do I have to programatically check emptiness to update the constraints manually?
Disclaimer: If this questions sounds very basic excuse me, please, I'm migrating from springs and struts and it is a quite criptic for now.

Does it need to change when content is visible? If not try this: you could do an update in the viewWillAppear method. For each label, call
[firstLabel setNumberOfLines:0];
[firstLabel setText:newProductTitleText];
[firstLabel sizeToFit];
Setting number of lines to 0 lets the label dynamically use an many lines as it needs. If this is an empty label that should actually become 0 lines. This will update the frames and autolayout should take care of the rest.

Related

Keep vertical UIStackView at the minimum width when the alignment is center

If I create a UIStackView with vertical axis and center alignment, how do I keep its width at the minimum possible value, meaning the widest subview?
I can't create a constraint between it and one of the subviews because I don't know which one will be the widest.
This usually happens when I create a leading greater than or equal constraint to the view controller's view with constant 10, and a center horizontally constraint.
In this case, the stack view will have the width equal to the view controller's width - 20, while the subviews will retain their width (e.g. from their intrinsic content sizes if they're labels).
Here is an example:
A--------------+
| B----------+ |
| | C--+ | |
| | +--+ | |
| | D------+ | |
| | | | | |
| | +------+ | |
| +----------+ |
+--------------+
where A is the view controller's view, B is the stack view and C and D are two labels.
So it leaves a gap between the left margins of B and D.
I would instead expect it to look like this:
A--------------+
| B------+ |
| | C--+ | |
| | +--+ | |
| D------+ |
| | | |
| +------+ |
| +------+ |
+--------------+
where the B's width is equal to D's width, because the leading constraint is greater than or equal, and not strictly equal.

Flexible layout using interface builder

I want to achieve this layout that will resize width in landscape mode and will keep all spaces(gaps) as defined. So single column of UITextView in one row there are two which should take half of the width left when left right and in between gaps are deducted.
+---------------------------------------------+
| <y> |
| +-----------------------------------+ |
| | | |
| +-----------------------------------+ |
| +-----------------------------------+ |
| | | |
| +-----------------------------------+ |
| +----------------+ +----------------+ |
|<x> | 50% | | 50% | <x>|
| +----------------+ +----------------+ |
| +-----------------------------------+ |
| | | |
| +-----------------------------------+ |
| |
+---------------------------------------------+
I tried for a long time checking different constrains like width <= 280 and width >= 280 - both with the same results.
Can someone write step by step what to do to have this layout? Does it matter that it is inside the UIScrollView?
The structure is:
View
Scroll View
Content View
Round Style Text Field
Round Style Text Field
View
Round Style Text Field
Round Style Text Field
Round Style Text Field
Add an other view around what you want to center.
+-----------------------------------------------+
| <y> |
| +-------------------------------------+ |
| |+-----------------------------------+| |
| || || |
| |+-----------------------------------+| |
| |+-----------------------------------+| |
| || || |
| |+-----------------------------------+| |
| |+----------------+ +----------------+| |
|<x> || 50% | | 50% || <x>|
| |+----------------+ +----------------+| |
| |+-----------------------------------+| |
| || || |
| |+-----------------------------------+| |
| +-------------------------------------+ |
| |
+-----------------------------------------------+
It's easy to center a view within another view.
View
Scroll View
Content View
View
Round Style Text Field
Round Style Text Field
View
Round Style Text Field
Round Style Text Field
Round Style Text Field

Achieving Grid Layout of Buttons with Autolayout

I am trying to achieve a layout of buttons very similar to Apple's inbuilt Calculator app:
I am using autolayout to position the buttons, and drawing a 0.5px border on each button, aiming for a 1px gap between buttons (like the calc app, closeup showing pixels above, from a retina device).
Border applied by :
btn.layer.borderWidth=0.5f;
btn.layer.borderColor=[[UIColor blackColor] CGColor];
and the layout is set up using Interface Builder.
It almost works; but there is some variability in the gap between buttons - for example the gap between the "1,2,3,thru" row and the "4,5,6,and" row is 2 pixels, but the gap between the "7,8,9" and "cl,0,#" rows is only one pixel.
Constraints are :
Top black area has a fixed height
All buttons same height
In the numeric rows, numeric buttons are set to same width, THRU, AND, ALL OFF and ENTER buttons are set to same fixed width
Top row of buttons pinned to bottom of black area, bottom row pinned to bottom of superview
In IB the buttons are all positioned butting up against each other.
In interface builder and at runtime I don't see any autolayout errors; and a debugger dump of the autolayout info gives me:
po [[UIWindow keyWindow] _autolayoutTrace]
*<UIWindow:0x10908a560> - AMBIGUOUS LAYOUT
| *<UILayoutContainerView:0x109136140>
| | *<UINavigationTransitionView:0x10908ef80>
| | | *<UIViewControllerWrapperView:0x109138870>
| | | | *<UIView:0x1090e6d10>
| | | | | *<UILabel:0x1090d5bb0>
| | | | | *<UIButton:0x1090d27e0>
| | | | | | <UIButtonLabel:0x1091c9190>
| | | | | *<UIButton:0x1090844c0>
| | | | | | <UIButtonLabel:0x1091c7990>
| | | | | *<UIButton:0x109088b60>
| | | | | | <UIButtonLabel:0x1091c6190>
| | | | | *<UIButton:0x1090e2ce0>
| | | | | | <UIButtonLabel:0x1091c4990>
| | | | | *<UIButton:0x1090e4e50>
| | | | | | <UIButtonLabel:0x1091c3190>
| | | | | *<UIButton:0x1090db730>
| | | | | | <UIButtonLabel:0x1091c1990>
| | | | | *<UIButton:0x109068f50>
| | | | | | <UIButtonLabel:0x1091c0190>
| | | | | *<UIButton:0x10906db80>
| | | | | | <UIButtonLabel:0x1091be990>
| | | | | *<UIButton:0x1090d23a0>
| | | | | | <UIButtonLabel:0x1091bd190>
| | | | | *<UIButton:0x1090c8520>
| | | | | | <UIButtonLabel:0x1091bb990>
| | | | | *<UIButton:0x1090c9090>
| | | | | | <UIButtonLabel:0x1091ba190>
| | | | | *<UIButton:0x1090c5680>
| | | | | | <UIButtonLabel:0x1091b8990>
| | | | | *<UIButton:0x1090d7fa0>
| | | | | | <UIButtonLabel:0x1091b7190>
| | | | | *<UIButton:0x1090cc820>
| | | | | | <UIButtonLabel:0x1091b5990>
| | | | | *<UIButton:0x10909dda0>
| | | | | | <UIButtonLabel:0x1091b4190>
| | | | | *<UIButton:0x1090c8090>
| | | | | | <UIButtonLabel:0x1091b2990>
| | | | | *<UIButton:0x1090c8e30>
| | | | | | <UIButtonLabel:0x1091b1190>
| | | | | *<UIButton:0x1090d5390>
| | | | | | <UIButtonLabel:0x1091af990>
| | | | | *<UIButton:0x10d30d260>
| | | | | | <UIButtonLabel:0x1091ae190>
| | | | | *<UIButton:0x10909a6a0>
| | | | | | <UIButtonLabel:0x1091ac990>
| | | | | *<UIButton:0x1090cca40>
| | | | | | <UIButtonLabel:0x109161160>
| | | | | *<UIButton:0x10d30ea90>
| | | | | | <UIButtonLabel:0x109156460>
| | | | | *<UIButton:0x1090da9e0>
| | | | | | <UIButtonLabel:0x109177ad0>
| | | | | *<_UILayoutGuide:0x1090e6dd0> - AMBIGUOUS LAYOUT
| | | | | *<_UILayoutGuide:0x1090ce080> - AMBIGUOUS LAYOUT
| | <UINavigationBar:0x10907da60>
| | | <_UINavigationBarBackground:0x109087240>
| | | | <_UIBackdropView:0x1090899f0>
| | | | | <_UIBackdropEffectView:0x10908b110>
| | | | | <UIView:0x10908bbb0>
| | | | <UIImageView:0x1090877a0>
| | | <UINavigationItemView:0x10916bc30>
| | | | <UILabel:0x10917f810>
| | | <UINavigationItemButtonView:0x1090ee060>
| | | | <UILabel:0x1090ee5d0>
| | | <_UINavigationBarBackIndicatorView:0x109098c80>
Can anyone offer any hints on how to achieve a "pixel perfect" layout like this with Autolayout? Or should I be doing this programatically instead?
Layer borders are drawn inside the views bounds, so you aren't going to see a "gap" between the buttons, but the effect of two borders abutting each other. This isn't going to work in the non-retina simulator or devices, since it can't draw a half-pixel, and Autolayout doesn't align views on half-pixels.
For a layout like the one above I'd be giving a fixed height to the buttons (or to one button, and making the others equal it) and letting the black view take up the remaining space. It's always best to have at least one (ideally, exactly one) variable element in a full-screen layout basically to cover rounding errors.
It sounds at the moment like you have one fixed element (the black view) and the buttons are basically "take the remaining height and divide it between yourselves", which will only work if the remaining height divides perfectly.
I tried to set horizontal/vertical spaces between views equal to 0.5. The color of parent view acts as border color. It looks ugly in IB, it cannot layout views properly but resulting layout on device looks perfect.
In interface builder:
On simulator:
Constraints are a bit tricky. For each row:
set height for the 1st button
set equal height for all buttons
set equal width for all buttons (a bit different for rows with orange buttons)
align top edges of all buttons
align bottom edges of all buttons
set leading and trailing spaces between all buttons (including spaces to superview) to 0.5
set top space (to previous row) of 1st button to 0.5
You can apply constraints to multiple items at once. In order to apply leading/trailing spaces between buttons, place them without intersection in desired order before applying.

Resize superview and all superview's siblings using autolayout

I am trying to manage the views on my screen so that all views are updated in accordance with a text view that is growing depending on the text that is entered.
On my controller's view i have two subviews. Let's call them view1 and view2. View1 has a uitextview subview that is growing and shrinking depending on the text that is entered.
The issue I'm having is that view1 and view2 don't to grow and shrink whilst typing. The constraint constant for vertical spacing from the bottom on the textView has been set to 10px from it's superview. Vertical spacing between view1 and view2 is set to 0. Here is a visual representation of the view:
---------------------
| |
| ----------------- |
| | UITextView | |
| ----------------- |
| view1 |
---------------------
| |
| |
| |
| |
| view2 |
| |
| |
| |
| |
| |
---------------------
The result I'm getting whilst I add text to the textView is:
---------------------
| |
| ----------------- |
| | UITextView | |
| | | |
| | | |
---------------------
| |
| |
| |
| |
| view2 |
| |
| |
| |
| |
| |
---------------------
But the desired behavior would be something along the lines of:
---------------------
| |
| ----------------- |
| | UITextView | |
| | | |
| | | |
| | | |
| | | |
| | | |
| ----------------- |
| view1 |
---------------------
| |
| |
| view2 |
| |
| |
| |
---------------------
The strange thing is, once I've added the text and pressed submit (not added to diagram for simplicity), view1 grows to the correct size, as does view2, but not whilst typing.
Do I need to explicitly call setNeedsUpdateConstraints or updateConstraintsIfNeeded? If so, where? I have tried calling it from the text view delegate textViewDidChange: but nothing has helped.
Any help would be much appreciated.
I managed to figure this out.
For anyone reading this having the same issue, the way I did it was to write my own subclass of UITextView giving it a height constraint and a few delegate methods.
When changing the height of the text view, simply fire the delegate method (e.g. textViewDidChangeHeight:). Then, in the delegate call back implementation, I just called sizeToFit on the view.

Transition between two UICollectionViewCells

I've got a UICollectionView using a flow layout.
Initially it shows small cells, roughly 5 X 5 on the iPad screen.
What I want to do it smoothly change to a nearly full screen cell size, with a cell with a larger, slightly different layout.
----------------
| |---| |---| |
| | | | | |
| |---| |---| |
| |
| |---| |---| |
| | | | | |
| |---| |---| |
----------------
transitions to..
----------------
| |---------| |
| | | |
| | | |
| | | |
| |---------| |
| |
| |---------| |
----------------
At the moment I use use a boolean showLarge which I set to YES when the user clicks (didSelectItemAtIndexPath)
Then I reload the data.
self.showLarge = YES;
[self.collectionView reloadData];
In my cellForItemAtIndexPath I load the appropriate cell according to the boolean:
if (!self.showLarge)
{
SmallCell *smallCell ..
etc
return smallCell;
} else
{
LargeCell *smallCell ..
etc
return largeCell;
}
However, this is an abrupt transition.
How would I smoothly animate to the new custom UICollectionViewCell?
You could use a different UICollectionViewFlowLayout, then change the collection view's layout on the fly. That'll give you a nice animation without the need to reload anything.

Resources