UICollectionView change flow direction on rotation - ios

Hey, I'd like to obtain what you see in the pictures: in Compact Height mode (landscape iphone) both the red and the blue view have to take all screen vertically and half the screen horizontally. In Compact Width mode (portrait iphone)they have to take all the screen horizontally and half the screen vertically. Space between views should be same size in both modes.
I used to think I have to use size classes and auto-layout constraints, but everything I tried failed miserably.
Maybe I have to use a UICollectionView and change flow direction based on orientation (if that is even possible)?

A collection view is probably overkill, because you don't want scrolling and that's the whole point of a collection view--by the time you do the sizing to stop it you'll have done all the work necessary to set a non-scrolling layout.
This is possible with Size Classes in IB. First, In general you will probably find it helpful to name the views in the Document Outline on the left in IB. You will also want to use this outline rather than try to grab the tiny constraint H-lines.
Set up all the constraints except 1) constraints linking the
OrangeView and BlueView to each other, 2) the constraints linking
the OrangeView to the top and left(leading), and 3) The constraints
linking the BlueView to the bottom and right (trailing).
Change the size class setting at the bottom to w-Compact and
h-Any in the funky box system. Now we're designing for a compact width, so views on top of each other.
Create a constraints for vertical space for BlueView.bottom to
OrangeView.Top. Also create constraint for OrangeView to
superview.leading (or ledaing,margin) and BlueView to
superview.trailing.margin. If you select any one of these constraints and look at the Size Inspector on the right (the ruler) you should see an "installed" checkbox not selected, and below that a w-Compact h-Any and another installed box, this one selected.
Now, while keeping the constraint selected just to see what happens, change the sizeClass selector at the bottom to w-Regular h-Any. Notice that in the Document Outline to the left, it should get grayed out.
Now we are designing for regular, so side-to-side. Add constraints linking the views for horizontal space, BlueView.trailing to OrangeView.leading. Also link OrangeView.top to the superview.top or top aligned to BlueView.top, and same for bottoms. You can manually edit the frame first; if not, IB will automatically fill in the wrong values, so edit these after you create them, and verify they are w-Regular and h-Any. With the ViewController selected, select "update frames" and the views should snap to their expected shape for the size class.
Let us know if this works for you or if it was unclear. Good luck!

Related

Swift - Why ScrollView not full screen?

I had inserted a ScrollView into UIViewController and dragged ScrollView to fill the space between the navigation bar and the RAM label below:
But when I run the app, the ScrollView does not fill the space:
Please help me! Thank you very much.
P/S: Sorry for my english is bad.
The scroll view is not covering up the whole thing because you are running the app on a much bigger phone. The simplest solution is to run the app on iPhone 5.
However, if you want to solve the problem on all sizes of iOS device, you need to add constraints.
Constraints are things that tells a view when and how much it should resize and where it should be positioned.
To add a constraint, just select the view you wish to add a constraint to and go to the bottom right corner. You will see 4 buttons:
The leftmost button is used to embedding views in stack views. This is a feature of iOS 9. If your deployment target is lower, just ignore it.
The second button to the left is for adding constraints related to alignment - where the edges of the views are, what its baseline is and where it is positioned in the X and Y axes:
The third button to the left is used to add constraint related to margins, width, height and how the width and height should change when it is asked to resize (keep the aspect ratio, for example):
The rightmost button is used to let Xcode decide what constraints you should add. And I think most of the times its choices are okay. Sometimes though, you still need to do some tweaking before it works.
"So... what constraints should I add?" you asked.
Well, I think I should teach you how to think when you want to add a constraint. This way, you can figure it out yourself in the future.
You should first let Xcode guess what constraints you want. Just click the rightmost button and click "Reset to Suggested Constraints". This can save a lot of work if Xcode can get it right. So remember to always do this first.
Then, run your app on various devices and see if the view's position, size, and alignment are as you expected. If it is not, you might have to add and/or remove some constraints.
For example, if you found that your view is always the same size on different devices, (that could be bad because it means that some content my go out of view on smaller devices) it's probably because Xcode added a width and/or height constraint to the view. You should delete that so that the view's width and/or height is not fixed.
You can find your view's constraints in the view hierarchy:
Just select the constraint and press delete.
Uncheck Adjust subview option and add
scrollview.view.autoresizingMask = [.FlexibleWidth, .FlexibleHeight]
You need to add constraints for your scrollView. Set the leading and trailing constraints to 0. Pin the height of your scrollView and also don't forget to set the top layout constraint. You can either pin the height or add bottom layout constraint to your page control.
Constraints are very important and its even more important to set it correctly. Check the Apple Documentation - Working with constraints in IB
Uncheck constrain to margins and add 0 every one of the four limits of spacing to nearest neighbour.
My guess (from the little information we have) is that you are creating a constraint from your scrollview to the top of your view with a value equal to the height of the navigation bar. Set the value of this constraint to 0.
Just set the 4 constraints to 0 to the area you need and then uncheck the "Content Layout Guides" checkbox in the constraints tab here. It will automatically adjust to the area you have specified.
The checkbox to uncheck

Auto layout space between elements relative to screen height [duplicate]

I understand the old Struts and Springs method of aligning, sizing and distributing views in Interface Builder. However, I cannot seem to figure out how to evenly distribute views using auto layout with Xcode 5. There was a way to do it using Xcode 4, but that option is gone.
I have 7 buttons arranged in a vertical stack. On a 3.5" layout, it looks great. When I preview the screen in the 4" layout, all of the buttons remain tightly packed and there is a large amount of space below the last button.
I want them to stay the same height, but I want the space between them to be able flex so they can spread out across the screen.
I've been able to get the height of the buttons to flex and fill the space, but that is not my desired behavior. I would like to learn how to use Auto Layout to replace my old Springs behavior, but I can't seem to find any way to do it through Interface Builder.
I'm ok with the top button either being a fixed space from the top edge or a proportional space from the top edge, likewise for the bottom button and the bottom edge. Those are less important to me, I'm good with either.
But I really need to figure out how to evenly distribute the extra space between each of the items in the view.
EDIT Note that in iOS 9 this technique will become unnecessary, because a UIStackView will perform distribution automatically. I'll add another answer explaining how that works.
How to Perform Even Distribution Using Autolayout
The simplest way to do this in Interface Builder alone (rather than constructing constraints in code) is to use "spacer" views:
Position the top and bottom buttons absolutely.
Place spacer views between all the buttons. Use constraints to position them horizontally (centering them horizontally is simplest) and to set their widths.
Make constraints between each button and the spacer view above and below it, with a Constant of 0.
Now select all the spacer views and set their heights to be equal.
The first screen shot shows me setting this up in IB:
I have deliberately not corrected for the "misplaced views" because I want you to see what it looks like while I'm designing the constraints. Here's the result on both a 4 inch and a 3.5 inch screen:
I have left the spacer views black, just to show you how this technique works, but of course in real life you would make them transparent and hence invisible! So the user sees just your buttons, evenly distributed on either height of screen.
The reason for the use of this technique is that although the notion of equality performs the distribution of values you are asking for, constraints can apply equality only between aspects of views; thus we need the extra views (the spacer views) so that we have things we can make equal to other things (here, the heights of the spacer views).
Other Approaches
Obviously, a more flexible approach is to assign the constraints in code. This may sound daunting, but there's a lot of third-party code out there to help you, such as this sort of thing.
For example, if we have a (possibly invisible) superview whose height acts as a boundary to dictate maximum vertical distribution of our four buttons, we can pin their tops to the vertical center of that superview with a constant of 0 but a multiplier of 0.000001, 0.666667, 1.33333, and 2.0 respectively (if we have four buttons); now the buttons will stay vertically distributed even as the superview changes size in response to screen height or whatever. [In Xcode 5.1, it will be possible to set that up in Interface Builder, but in earlier versions of Xcode it is not possible.]
In iOS 9 / Xcode 7 this problem will be trivially solved in IB. Simply select the buttons (or whatever it is you want to distribute vertically) and choose Editor > Embed In > Stack View. Then you simply configure the stack view:
Provide constraints that position and size the stack view itself. For example, pin the four edges of the stack view to the four edges of its superview.
Set the stack view's attributes. In this case we want Vertical axis, Fill alignment, Equal Spacing distribution.
That's all! However, you may be curious about how this works, because it is still possible to do the same thing manually in code. A stack view performs distribution, not by inserting spacer views, but by inserting spacer guides. A guide (a UILayoutGuide) is a lightweight object that behaves like a view for purposes of layout constraints, but is not a view and therefore doesn't have to be made invisible and doesn't carry any of the overhead of a view.
To illustrate, I'll do in code what the stack view is doing. Presume we have four views to distribute vertically. We assign them constraints for everything but their distribution:
They all have absolute height constraints
Their left is pinned to the superview's left, and their right is pinned to the superview's right
The top view's top is pinned to the superview's top, and the bottom view's bottom is pinned to the superview's bottom
Now, presume we have references to the four views as views, an array. Then:
let guides = [UILayoutGuide(), UILayoutGuide(), UILayoutGuide()]
for guide in guides {
self.view.addLayoutGuide(guide)
}
NSLayoutConstraint.activateConstraints([
// guide heights are equal
guides[1].heightAnchor.constraintEqualToAnchor(guides[0].heightAnchor),
guides[2].heightAnchor.constraintEqualToAnchor(guides[0].heightAnchor),
// guide widths are arbitrary, let's say 10
guides[0].widthAnchor.constraintEqualToConstant(10),
guides[1].widthAnchor.constraintEqualToConstant(10),
guides[2].widthAnchor.constraintEqualToConstant(10),
// guide left is arbitrary, let's say superview margin
guides[0].leftAnchor.constraintEqualToAnchor(self.view.leftAnchor),
guides[1].leftAnchor.constraintEqualToAnchor(self.view.leftAnchor),
guides[2].leftAnchor.constraintEqualToAnchor(self.view.leftAnchor),
// bottom of each view is top of following guide
views[0].bottomAnchor.constraintEqualToAnchor(guides[0].topAnchor),
views[1].bottomAnchor.constraintEqualToAnchor(guides[1].topAnchor),
views[2].bottomAnchor.constraintEqualToAnchor(guides[2].topAnchor),
// top of each view is bottom of preceding guide
views[1].topAnchor.constraintEqualToAnchor(guides[0].bottomAnchor),
views[2].topAnchor.constraintEqualToAnchor(guides[1].bottomAnchor),
views[3].topAnchor.constraintEqualToAnchor(guides[2].bottomAnchor)
])
(Obviously I could make that code cuter and shorter using loops, but I have deliberately unrolled the loops for clarity, so that you can see the pattern and the technique.)

iOS - Autolayout - Increase/Decrease view size proportionally on orientation change

I went through ray wenderlich's tutorial about autolayouts (link) and then began working on a demo project thinking I've figured it all out but I was wrong. As shown in the screenshot below I have a navigation bar, 2 views and 1 button. The layout on portrait mode looks fine without adding any constraints on views or the button, and understandably in landscape mode views are messed up. I tried adding following constraints on views and those don't seems to work.
View#2: Select view 2 > Editor > Align > Horizontal center in container (hides view completely)
Add Top space to superview. Again view goes away from both landscape and portrait preview.
If I can display view#2 correctly I am planning to add vertical space between view#2 & view#3 and then between view#3 and button#4.
My main concern is to resize the views so that it shows all views and buttons in iPhone 4s landscape mode. Any advise or suggestions are appreciated.
EDIT: Here's the end result that I am trying to get:
The reason your views go away when you add constraints is because a UIView has no intrinsic content size, so its size is {0,0}. The view appeared when you didn't add constraints because the system adds constraints for you, if you don't add them yourself; the system added ones are top, left, width, and height. So, you need to set the size of the views somehow. You can give them explicit size constraints, you can pin them to the edges of the superview, you can give them relative heights based on other views, etc.
Since you want the 2 views to get proportionally smaller in landscape, you should give them heights that are relative to the superview. You do this by selecting the view and the superview, and choosing "Equal Heights" from the pin menu. Edit that constraint to change the multiplier to something like 0.25 for the blue view and 0.2 for the orange one (this assumes that orange or blue view are the first item in the constraint -- if they are the second, then you should use the inverse values of 4 and 5). You should also do the same for the widths, since it seems you want them to get proportionally smaller too.

Auto layout: Xcode 6: Centering UI elements

I'm using Interface Builder in Xcode 6 to make an app and am having trouble getting the text fields and button to centre on the screen for different size screens.
I thought it was a matter of selecting horizontal and vertical centering in container but it doesn't seem to be that when I try it in auto layout. Actually I've tinkered around a bit and I still haven't got it.
I just want to be able to see all of my button and text fields for any size iPhone screen and right now simulator is only showing part of these UI elements like this:
I also want to do this in storyboard and not in code as I'm not at the level of doing this in code yet.
Step 1: Make sure your size class covers all the iPhone screen at least in portrait view. So, change the size class to "wCompact hRegular".
Step 2: After setting the size class properly, add the UITextFields and UIButton to your storyboard. To me, it looks something like-
Step 3:
Before, you start adding constraints, you need to remember two things-
a. Your element(UITextField, UIButton, UIView or any component) needs to know its starting position unambiguously, and
b. Your element needs to know its size meaning, its height and width.
In this case, as you want to centre your elements, I am just assuming that it needs to be centred starting from 10 scale from the left edge and should end 10 scales away from the right edge of your iPhone screen. Now, that means, it's width will be different based on the screen size, but its height will be same.
So, I just add the constrains following way for the 1st text box-
Notice, in the size inspector, I set the text box's starting point, x and width in a way that it is 10 pt away from left edge and 10 pt away from the right edge. Don't worry, it's just simple math.
For the 2nd textField, I add the constrain, the same way-
Lastly, for the button, the constrains are following-
Now, you are good to go. Everything is centered.
By using your size class selector in the bottom of the storyboard window, set you sizes as any width and any height and then follow the below auto layout constrains. It will work for you.
First select the view you want to set the auto layout, and then select the pin option from the right bottom corner of your storyboard and then add the constrains as shown in above picture and click button Add 4 constrains
Repeat the process for all views and set the constrains as Fix the top, bottom, left and right constrains of all views except the last button that should be fix from top,left,right and fixed height.
You need to make use of the size class selector in the bottom of the storyboard window.
So for an iPhone 6 or 6 Plus in portrait you would choose a compact width and regular height like this:
And then you would do whatever auto layout stuff for the given device there

iOS autolayout with size classes does not differenciate landscape for 3"5 and 4"?

here's what I've been struggling with for the past hours.
I have a view controller that contains a few labels, a picture, and a tableview. I'm using autolayout/size classes in order to use only one storyboard for the various devices supported (meaning: all size of iphones and iPad).
Noew everything is working, except one thing : the settings in "width compact x height compact" are used for 3"5 and 4" iPhones in landscape. So either I say that the table view takes 480px and there's a whole lot of space on iPhones 5/5S, either I set it at 480px but then I cannot reliably align items vertically in my cells.
How come there is no way to say "this view should always takes the full width" ?
Any solution, even workaround, would do. Thanks a lot for your time !
There is no distinction in size classes between 3.5", 4" and 4.7" devices. They all have the same size classes deviced for horizontal and vertical, and behave the same way on rotation.
You can define a view to take all the available space. Explicitly set the bottom and top views to be attached to bottom and top edges (respectively) with constraints, and set the middle view (in your case, the table view) to take all the space in the middle by setting constraints on top with the top views, and constraints on bottom with the bottom views. Now, because top view is attached to top and bottom is attached to bottom, you will have the middle view grow and shrink as needed.
There are a couple of ways to get percentage based sizing.
Link the width of your target element to the width of another element.
Pin your element to its Superview and use the Multiplier setting instead of Constant.
Say you want to set a TitleLabel to have a relative width compared to another element.
Example for method 1:
Ctrl + drag from TitleLabel to another Target element (such as its Parent View), then choose Equal Widths, you can then change the Multiplier field in your Constraints settings to 0.5 if you want 50% width.
Example for method 2:
Pin the appropriate edges of TitleLabel to either the Center, Bottom or Trailing edges* of its Superview (Editor -> Pin -> X Space to Superview).
In the Constraints Settings, make sure you have TitleLabel in the First Item field, and the Superview as the Second Item.
*because the Top/Leading edge of the Superview will have a 0 value, a multiplier against these won't work, as 0 x 0 = 0.
I found method #1 doesn't work well in Table Views, so you may have more luck with #2.

Resources