Back to Blog

iOS: Repositioning UIViews – A Step By Step Guide

March 7, 2013

By Jon Nalewajek
4 Comments

iOS

While developing one of our iPad applications, we came across a scenario where we needed a way to simply reposition UIView objects on the screen.

The Problem

You have a View that has a big logo in the center towards the top, and two UITextFields right below it: one to enter your user name, another to enter your password.

While in portrait mode, this makes perfect sense. When you tap on the user name UITextField, the keyboard comes up and you can still clearly see the other UITextField where you need to enter your password in next. However, in landscape mode, because we have less vertical space, if we leave both UITextFields in the middle of the view (right below the logo), when we select the user name UITextField, the virtual keyboard will popup and will cover up the password box. In other words, because there is less vertical space in landscape orientation, the keyboard will come up and cover approximately half the screen’s height, which means the password text box will be under the keyboard.

Looks good in portrait mode

Looks good in portrait mode

 

Where did our password box go? Oh, it's under the keyboard...

Where did our password box go? Oh, it’s under the keyboard…

This seems like a pretty straightforward problem. We want to be able to see both UITextFields when the virtual keyboard is on the screen. We could go about creating a separate landscape view, and in some cases, especially where the view is complicated, this might be the better approach. However, it seems like if we just are concerned about the positioning of the UITextFields, instead of created an entirely new landscape view, we should just be able to easily move them when the screen orientation changes.

Repositioning

What would be the best way to reposition the UITextFields? Should I simply just write all of the code out in my ViewController? I didn’t think this was the appropriate place to put this logic. Suppose there are other ViewControllers that need to reposition items. Would all of these view controllers need to contain the repositioning methods? What if we have compound views, and the view itself needs to reposition a subview?

If we take this approach, we are going to have the repositioning logic all throughout our application. As a developer, having the same code block in several files is bad news bears. Isn’t there a way we can do put most of the logic in one place to use throughout the app?

Solution

We can achieve this by creating a new category on UIView which will contain all of our repositioning logic.

In Xcode, go to File > New and select “File…”. Under Cocoa Touch, select “Objective-C category”.

On the next screen, name your Category Positioning, and in the “Category on” text box, enter UIView. This will add the category on ALL classes that have UIView as one of their parent classes. Click “Next” and save the files. This process will create two files “UIView+Positioning.h” and “UIView+Positioning.m”.

In UIView+Positioning.h, add the following methods

- (void)shiftVertically:(int)points;
- (void)shiftHorizontally:(int)points;

In UIView+Positioning.m add the following code

- (void)shiftVertically:(int)points
{
     CGRect frame = self.frame;
     frame.origin.y = self.frame.origin.y + points;
     [self setFrame:frame];
}

- (void)shiftHorizontally:(int)points
{
     CGRect frame = self.frame;
     frame.origin.x = self.frame.origin.x + points;
     [self setFrame:frame];
}

It is that simple. Now that we have this category complete, we can easily shift any UIView vertically or horizontally on the screen.

For example, for the problem I described above, you might have something like the following in your ViewController class:

/*
 * At the top of our class where we are adding our logic,
 * we need to import the category.
*/
#import "UIView+Positioning.h"

#define VERTICAL_SHIFT 50
#define HORIZONTAL_SHIFT 20
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
    if (UIInterfaceOrientationIsLandscape(toInterfaceOrientation)) {
        // shift the login and password box upwards by 50 points
        [_userNameBox shiftVertically:-VERTICAL_SHIFT];
        [_passwordBox shiftVertically:-VERTICAL_SHIFT];
        // shift both boxes to the right by 20 pixels
        [_userNameBox shiftHorizontally:HORIZONTAL_SHIFT];
        [_passwordBox shiftHorizontally:HORIZONTAL_SHIFT];
    } else {
        // shift the login and password box downward by 50 points
        [_userNameBox shiftVertically:VERTICAL_SHIFT];
        [_passwordBox shiftVertically:VERTICAL_SHIFT];
        // shift both boxes to the left by 20 pixels
        [_userNameBox shiftHorizontally:-HORIZONTAL_SHIFT];
        [_passwordBox shiftHorizontally:-HORIZONTAL_SHIFT];
    }
}

An important thing to note here is that if we want to shift a UIView upward, vertically, we need to shift the view by negative pixels. The y-coordinate 0 is at the top of the screen. Likewise, if we want to shift a UIView to the left, horizontally, we need to shift the view by negative pixels. The x-coordinate 0 is at the left edge of the screen.

We did it!

By creating this objective-c category on UIView, we can easily shift UI elements around the screen in ANY of our ViewControllers. All of the logic to move the elements is located in one central file instead of in each separate ViewController.

Jon Nalewajek

Jon Nalewajek

Not only is Jon largely responsible for mobile application development at Cypress North, but he also plays a role in developing custom ASP.NET and PHP solutions. Whether it is creating web services, custom web components for your website, or helping you build your next big mobile app, Jon has you covered.

See Jon's Most Recent Posts

Share this post

4 comments

Bas van Kuijck March 9, 2013Reply

That’s one way to do it. I for one like to use constraints or even the oldskool ‘autoResizingMask’ for positioning elements without individually addressing them. Just resize (animated) its view container and you’re done.

Jonathan Nalewajek March 11, 2013Reply

Bas van Kuijck,

I use the auto-resizing mask on every other screen, but in this one case, we wanted our view to look different in landscape mode and had to move UI elements around on the screen.

In our case, we didn’t only move the UITextFields, but we also moved the position of the logo. There was so much more horizontal space in landscape mode, that we thought we could utilize this space better if UI elements were rearranged.

But you bring up an excellent point: the auto-resizing mask feature can be very useful!

Robert Lis February 24, 2014Reply

Can you not simply embed it inside a UIScrollView and adjust bottom edge inset to the height of a keyboard ?

Jonathan Nalewajek February 24, 2014Reply

Robert,

You can definitely do that in the case I mention above. However, suppose you have a background that is a static, non-repeating image, and adjusting the offset messes up the background. In my case, due to the static image, when the view was offset, the key components of the background would either be covered up by UI elements, or the background would shift off of the screen. In the app I was originally writing that inspired this post, it didn’t make any sense to wrap the view in a scroll view.

This is an optional way for anyone who finds themselves in a similar situation. They can just use these categories to shift elements around on the screen.

It all comes down to what problem you are trying to solve, and which solution is acceptable for the situation you are in.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Search our blog

Start A Project

Categories

What's next?

Well...you might like one of these

Article

[SOLVED] Using an in-memory repository....

.NET Core Data Protection One of the main benefits of...

Read article

Article

New Website Launch: HHL Architects

While the 2016 calendar winds down, we've been hard at work...

Read article

Article

New Site Launch: D.V. Brown &...

With more than 30 years of experience  in the business (and...

Read article