IOS Development Tutorial – Adaptive Interface Design with Autolayout: UIStackView and Vary for Traits

In the development of an APP for iOS, there are few occasions in which the same screen has different composition in portrait and landscape orientation. Today we give you this IOS Development Tutorial. Then you will be able to develop an adaptive interface for your apps.

For years there have been different ways to face this problem with Xcode. From re-locating components through code, to creating different layouts for each version.

The Autolayout provided a new way of managing this casuistry. The “Size-Class-Specific Layout” consisted of an option in each xib that allowed to define specific behaviors according to different sizes of screen. Being useful, not only for orientation changes, but also for different screen sizes or changes in the layout container (such as the master-view-controller of a SplitView on iPad).

IOS Development

IOS Development

This tool allowed to establish different behaviors for different screen sizes and configurations without having to appeal to changes by code or to have more of an xib file.

In Xcode 8 was replaced by the option Vary for traits, which has a very similar behavior although it can become somewhat confusing when used for the first time.

Let’s navigate a little by the options offered by this feature and make a sample screen using constraints in AutoLayout, which allows us to reuse the same design applying only changes in the composition of the elements of the interface.

The example will be very simple and in it we will use AutoLayout with different visualization in portrait and landscape (vertical and horizontal).

IOS Development: Creating the ViewController

First of all, we created a new Xcode project. In the wizard, we select Single View Application. The project created consists of its AppDelegate and a ViewController, as well as the Main.storyboard.

In this example we will replace the Main. Storyboard with an xib file that contains only the screen that we want to design.

To do this, we create an xib file called ViewController.xib and add the following code in the ViewController.m class:

-(id)init {ViewController *controller = [super initWithNibName:NSStringFromClass([self class]) bundle:nil]; return controller; }

In the xib it is important to establish in the Custom Class field of the Identity Inspector the name of our class:

IOS Development

And drag in the Connection Inspector the View field to the main view of the controller.

IOS Development

So far we would have created the controller of our view, and linked with its file builder interface. Only it just need to be opened from AppDelegate.m

– (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

// Override point for customization after application launch.

ViewController *viewController = [[ViewController alloc] init];

UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:viewController];

self.window.rootViewController = navigationController;

return YES;

AutoLayout

Now let’s add the fields to our controller. We will set up a Login screen that contains a logo, a couple of text boxes to add the user and the password, and finally a footer.

For this we are going to use AutoLayout and, in the following section, we will be careful that it is not necessary to scroll when we are in an iPhone in Landscape position, since we will dynamically change the location of the components thanks to Vary for traits.

There are different ways to create this screen, probably some of them easier and more intuitive, but we will choose to use the component UIStackView introduced in iOS 9, as it performs some functions automatically and we will be more comfortable to use in the long run.

First, let’s add the footer we want to keep in the bottom area of the screen in both portrait and landscape, so we will just add a label and anchor it in the bottom of the view and the sides. We will leave the height dynamic in case it could have text that occupy more than one line, and we will center the text in the field with Alignment of the Label.

IOS DevelopmentNow, we add to the View a vertical UIStackView that will contain 2 elements: the logo on one side, and the user and password fields on the other. And we will anchor it to the 4 sides of the view.

IOS Development

Now we are going to add 2 UIView that will make them containers, since a UIStackView does not allow us to establish a separate margin for each element. In fact, it is a component that does not allow adding a background or an edge, since it is not a UIView itself, but an extension whose sole purpose is to position the views.

When, adding both UIView to UIStackView, Xcode will complain that we are missing constraints. And it is true, since it is not known how much each view will occupy, it is not possible to assign the remaining space to the other view. By the composition of the screen, we will want the logo to occupy a dominant place at the top of it, leaving the text boxes for the bottom. So we will choose to assign half and half to each view using the Equal Heights constraint.

IOS DevelopmentFor clarity, the view that will contain the logo will be yellow, and the view that will contain the login box will be blue. We should have something like this:

IOS Development

All we have to do is add the logo and the text boxes. Let’s start with the first one, we will place the view inside the yellow view, and we will assign it as unique constraints that are centered vertically and horizontally.

IOS Development

We already have our “logo” (for convenience I used a UILabel but for practical purposes is the same). Now, for the access zone, we will use another vertical UIStackView in which we have 3 views with transparent background (clear color). Two of them with an with an icon and a text box, and the third one with the button of Access.

IOS Development

We add our new vertical UIStackView to the blue UIView, and inside we will add the 3 UIView.

Right now, Xcode does not know how much wide, high each view will occupy, or where the UIStackView itself will be located within what would be our blue UIView. So it is normal to get notices of missing constraints. Let’s ignore them for now.

Next, we’ll add the user and password icons in the first two views, along with a UITextView. This will be the only component in which we will set the width. Since we do not want it to be dynamic and fit the width of the screen (it could occupy too much space and would not be very attractive visually).

The icons have been added to the Assets.xcassets folder with the name pass_ic and user_ic. Regarding the constraints, the icon will be anchored to the left, centered vertically, and will have a fixed size of 30 × 30 (since our icons are in those approximate dimensions).

The text box will have a width of 200, for example, and it will be anchored to the right, up and down of the parent view and the icon by the left constraint. It will also have a height of 44, so that it is comfortable enough.

Having already defined the height of the first UIView (which will be the height of the UITextView, since it has a fixed height and is anchored both above and below), and having the width (which will be the UITextView + the UIImageView + the separation between them , Since all these values are fixed). The UIStackView container can infer the size it will need in width and height once all 3 views are completed.

IOS Development

We will repeat the process for the view of the password and for the access button, which we will anchor to the parent view, and center this UIStackView in the blue view.

It should look something like this:

IOS Development

Text boxes are close together. A more elegant option to separate them, instead of modifying the UITextView contraints, is to add separation to the UIStackView. This can be done from the Identity Inspector by modifying the Spacing field.

Vary for traits

However, our landscape view is not displayed exactly as we would like it to be. The foot does not get to be seen and much space is wasted to the sides, whereas all the important information is located in ⅓ of the screen.

To solve this, we will use the option Vary for traits, which can be accessed from the bottom of the InterfaceBuilder, clicking on View as:

IOS Development
When we click on it, we can decide for which cases we want to add concrete constraints. If they will be constraints that will be applied only when the width changes, or when the height changes, or when they change height and width. At the bottom the cases will graphically show for which this change will apply.

It is important to look at the (wC hC). This gives us a clue of which cases apply. W = width, h = height, and the value on the right may take R = Regular, C = Compat or A = Any.

Several examples would be:

  1. An iPhone in portrait will be wC hR
  2. An iPad will be wR hR
  3. An iPhone 7 or SE in landscape will be wC hC
  4. An iPhone 7 Plus in landscape will be wR hC

IOS Development

Basically Regular refers to large sizes, and compact to small sizes. A constraint that only applies to the cases in which our view is deformed, is a constraint that must be added for hC cases, since an iPad in landscape does not want to change it.

IOS DevelopmentSo in our case, we will select the iPhone 7 in landscape (wC hC), for example, and we will press Vary for Traits for height and width, showing us all the possible variants that have that wC hC.

IOS Development

Por lo que en nuestro caso, seleccionaremos el iPhone 7 en landscape (wC hC), por

At this point, any changes we make to layouts will apply only to these variants. So let’s change the fairness, which is not much thanks to the UIStackView and the orientation parameter, and we will see how it behaves.

First, we will add a constraint that indicates that both the blue and the yellow view have the same width. This was not important with the vertical orientation of UIStackView, but in landscape case it is important to add it:

IOS Development

Notice how from now on, when we click on Done varying, the constraints that we have added in Vary for traits mode will appear in gray when we are visualizing a case in which that constraint does not apply.

The Equal Widths constraint is only used when hC wC, that is, in the iPhone landscape.

IOS Development

Now we are going to use Vary for traits again but it will not be in a constraint, but in the Identity Inspector. We will establish that we want our main UIStackView to be horizontal when we are in the wChC case.

To do this, from the Identity Inspector tab itself, with the UIStackView selected, we click + next to the Axis attribute.

We will see a small menu in which we will indicate for which cases we will want to modify the Axis attribute.

IOS Development

We add the new axis attribute and modify it to be horizontal in the wChC case

IOS Development

The result is the one we wanted to get. A new design that makes better use of space when I’m on an iPhone in Landscape.

IOS Development

And finally: the keyboard

At this point Xcode does not offer any tools (as Android tools do) to prevent the keyboard from hiding a text box.

There are thousands of ways to do it, but none is clean enough. Most of them are manual and involve messing up the code for a behavior that should come in the SDK itself.

In this case, we are going to use the IQKeyboardManager library ( https://github.com/hackiftekhar/IQKeyboardManager) so we will not dedicate code and time to something that is already done, and probably, quite well.

We will create the framework using Carthage (CocoaPods can also be used). We added it to the project and added the following line to the didFinishLaunchingWithOptions del AppDelegate.m

IQKeyboardManager.sharedManager.enable=true;

In summary

With all of the steps of this IOS Development Tutorial, we would already have our login screen perfectly functional. We would only have to finish adding the functionality of the button and the colors, designs, etc.

AutoLayout can become your friend if it is treated with some care. In the latest versions of iOS, with the various improvements to the system of constraints and above all the introduction of UIStackView, making interface designs using code is something that can be avoided in most cases, resulting in a much cleaner and more organized project.

Unfortunately, it is still far from perfect and the Interface Builder continues to show some deficiencies and bugs that make presence in more complex compositions, as well as great problems in the use of version control systems. Hopefully Apple will continue to improve it in successive iterations.

The project code can be downloaded from the following link:
https://www.dropbox.com/s/1m17q68sue9obqb/tutorial-project.zip?dl=0