Sign up for a Parse account to implement this tutorial and more!

Sign Up

Icon_signup_ios
Login and Signup Views

Learn how to setup the iOS login and signup view controllers, which provide a set of built-in views ready to be customized and dropped into your app that uses PFUser.

iOS
PFUser
PFLogInViewController
PFSignUpViewController

Download code for this tutorial:

.zip File GitHub

Creating a login and signup flow is a tricky process, yet developers find themselves rewriting this code for every new app. To help make this process easier, we created the PFLogInViewController and the PFSignUpViewController; drop-in solutions that adds a variety of login and sign up options with just a few lines of code.

In this tutorial, we'll explore the full potential of these two view controllers. We'll start in Section 1 by learning how delegates are used to interact with the login and signup controllers. In Section 2, we'll learn how to customize the login options by creating Facebook and Twitter login flow. We'll finish off in Section 3 by looking at the full breadth of available view customization options by creation our own subclasses of PFLogInViewController and PFSignUpViewController.

Before diving into the tutorial, feel free to download the source code from the link above and follow along.

Default PFLoginViewController

1. Basic Login and Sign Up Flow

This first section of the tutorial will be spent in the DefaultSettingsViewController. Here, we've implemented a standard login flow using the default settings provided by the login and signup view controllers.

1.1. Presenting the Login View Controller

In our sample app, we start by modally presenting the new PFLogInViewController in the viewDidAppear method when there is no logged in user. For your own application, you might write this code on the root view controller of you app or in your app delegate.

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    
    if (![PFUser currentUser]) { // No user logged in
        // Create the log in view controller
        PFLogInViewController *logInViewController = [[PFLogInViewController alloc] init];
        [logInViewController setDelegate:self]; // Set ourselves as the delegate
        
        // Create the sign up view controller
        PFSignUpViewController *signUpViewController = [[PFSignUpViewController alloc] init];
        [signUpViewController setDelegate:self]; // Set ourselves as the delegate
        
        // Assign our sign up controller to be displayed from the login controller
        [logInViewController setSignUpController:signUpViewController]; 
        
        // Present the log in view controller
        [self presentViewController:logInViewController animated:YES completion:NULL];
    }
}

You might have noticed that we set the current controller (DefaultSettingsViewController) as the delegate for both the login and signup view controllers. The delegate methods will used to interact with the controllers and obtain event information. The rest of this section will look at the protocol methods available and how they can be used in your app.

1.2. The PFLogInViewControllerDelegate

To implement these methods, we start by adding the protocols in the header file.

// Implement both delegates
@interface DefaultSettingsViewController : 
      UIViewController <PFLogInViewControllerDelegate, PFSignUpViewControllerDelegate>

The PFLoginViewControllerDelegate protocol has four optional methods the delegate can choose to implement. In this tutorial we'll take a look at all four, but your own implementation may vary based on your needs.

First, we have logInViewController: shouldBeginLogInWithUsername: password:. This method allows us to interrupt the login process based on local validation. In our sample app, we use this method to ensure the user completed both the username and the password fields before continuing. If one of the fields is empty we display a UIAlertView and interrupt the login process by returning NO. If the login is interrupted here, no request to the Parse API will be made.

// Sent to the delegate to determine whether the log in request should be submitted to the server.
- (BOOL)logInViewController:(PFLogInViewController *)logInController shouldBeginLogInWithUsername:(NSString *)username password:(NSString *)password {
    // Check if both fields are completed
    if (username && password && username.length != 0 && password.length != 0) {
        return YES; // Begin login process
    }
    
    [[[UIAlertView alloc] initWithTitle:@"Missing Information" 
                          message:@"Make sure you fill out all of the information!" 
                          delegate:nil 
                          cancelButtonTitle:@"ok" 
                          otherButtonTitles:nil] show];
    return NO; // Interrupt login process
}

The second protocol method notifies the delegate when the user is successfully logged in. In this sample app, we simply dismiss the PFLogInViewController that was presented earlier. In your own app, you can use this method to perform additional on-boarding logic, such as downloading a profile picture.

// Sent to the delegate when a PFUser is logged in.
- (void)logInViewController:(PFLogInViewController *)logInController didLogInUser:(PFUser *)user {
    [self dismissViewControllerAnimated:YES completion:NULL];
}

The last two methods of the PFLogInViewControllerDelegate protocol are used to handle a failed login attempt or a user tapping on the top left dismiss button. As you can see from the implementation below, we use the latter to return the user to the sample app's root view controller. We could use display an alert view when the login fails, but since the PFLoginViewController will already display one by default.

// Sent to the delegate when the log in attempt fails.
- (void)logInViewController:(PFLogInViewController *)logInController didFailToLogInWithError:(NSError *)error {
    NSLog(@"Failed to log in...");
}

// Sent to the delegate when the log in screen is dismissed.
- (void)logInViewControllerDidCancelLogIn:(PFLogInViewController *)logInController {
    [self.navigationController popViewControllerAnimated:YES];
}

Feel free to use these however you see fit!

1.3. The PFSignUpViewControllerDelegate

The PFSignUpViewControllerDelegate protocol follows a very similar structure. It has four methods which follow the same pattern. First we have the signUpViewController: shouldBeginSignUp: method which we can use as an interrupt for the sign up process. The key difference with the login delegate equivalent, is that the field information is provided in an NSDictionary where the keys are the names of the fields (username, password, etc). In the case of this sample app, we use this method to ensure that all fields have been completed.

// Sent to the delegate to determine whether the sign up request should be submitted to the server.
- (BOOL)signUpViewController:(PFSignUpViewController *)signUpController shouldBeginSignUp:(NSDictionary *)info {
    BOOL informationComplete = YES;
    
    // loop through all of the submitted data
    for (id key in info) {
        NSString *field = [info objectForKey:key];
        if (!field || field.length == 0) { // check completion
            informationComplete = NO;
            break;
        }
    }
    
    // Display an alert if a field wasn't completed
    if (!informationComplete) {
        [[[UIAlertView alloc] initWithTitle:@"Missing Information" 
                              message:@"Make sure you fill out all of the information!" 
                              delegate:nil 
                              cancelButtonTitle:@"ok" 
                              otherButtonTitles:nil] show];
    }
    
    return informationComplete;
}

The other three methods follow a one-to-one mapping with the PFLogInViewControllerDelegate's. You can use them any way you see fit in you own app.

// Sent to the delegate when a PFUser is signed up.
- (void)signUpViewController:(PFSignUpViewController *)signUpController didSignUpUser:(PFUser *)user {
    [self dismissModalViewControllerAnimated:YES]; // Dismiss the PFSignUpViewController
}

// Sent to the delegate when the sign up attempt fails.
- (void)signUpViewController:(PFSignUpViewController *)signUpController didFailToSignUpWithError:(NSError *)error {
    NSLog(@"Failed to sign up...");
}

// Sent to the delegate when the sign up screen is dismissed.
- (void)signUpViewControllerDidCancelSignUp:(PFSignUpViewController *)signUpController {
    NSLog(@"User dismissed the signUpViewController");
}

2. Simple Customization and Social Integration

PFLoginViewController customized with social login options

In this section, we'll spend our time in the sample app's PropertyConfigViewController which demonstrates the customization of the login view controllers through properties. With only a few lines of code, we'll create the entire login work flow needed for users to join our application using their Twitter or Facebook accounts.

Before starting, take a look at the Facebook and Twitter setup guides to create the necessary accounts on these social networks. You can compare the steps described in these sections with the AppDelegate of this tutorial to see how we've properly configured the app for use with both social networks.

While Twitter and Facebook make the setup process arduous at times, we've ensured that the integration process with Parse is dead simple. Like in Section 1.1, we present the PFLoginViewController in the viewDidAppear method of our controller. However, this time we set the fields property to support our social network logins. Notice that since we are not using the username login, we don't need to create a signup view controller. We also set the facebookPermissions property to the a list of the permissions we want to be granted by the user. A full list of these permissions can be found on the Facebook developer website.

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    
    if (![PFUser currentUser]) {        
        // Customize the Log In View Controller
        PFLogInViewController *logInViewController = [[PFLogInViewController alloc] init];
        [logInViewController setDelegate:self];
        [logInViewController setFacebookPermissions:[NSArray arrayWithObjects:@"friends_about_me", nil]];
        [logInViewController setFields: PFLogInFieldsTwitter | PFLogInFieldsFacebook | PFLogInFieldsDismissButton];
                
        // Present Log In View Controller
        [self presentViewController:logInViewController animated:YES completion:NULL];
    }
}

And it's that simple! You can use the same delegate methods we saw in Section 1.1 to handle successful, failed and dismissed logins. Note that the logInViewController: shouldBeginLogInWithUsername: password: delegate method is not called when the Twitter or Facebook login is used.

As we'll see in the next section, social network login is not mutually exclusive with the username and password option. Both can coexist and give you users the variety they crave!

3. Subclassing for Full Customization

To give the login and signup view controllers your own look and feel, you can subclass them and modify the logInView and signUpView properties. In this last section of the tutorial, we'll see how this can be done by looking at the sample app's MyLogInViewController, MySignUpViewController and SubclassConfigViewController.

Custom PFLogInViewController subclass Custom PFSignUpViewController subclass

The view customization is done by overriding two methods in the subclass: viewDidLoad and viewDidLayoutSubviews. As with most view controllers, we use the former to add and modify views. The only task that must be performed in the latter, is setting the frame of view elements. Changing the frame in the viewDidLoad method will not have the desired effect.

3.1. Subclassing PFLogInViewController

Let's start with the viewDidLoad method of our custom login view controller subclass, MyLogInViewController. You'll notice most changes are done by accessing the logInView property of the view controller. This view is of type PFLogInView and contains all of the elements displayed by the PFLogInViewController.

Take a look at the implementation below and we'll discuss what's going on afterwards.

- (void)viewDidLoad
{
    [super viewDidLoad];

    [self.logInView setBackgroundColor:[UIColor colorWithPatternImage:[UIImage imageNamed:@"main_background.png"]]];
    [self.logInView setLogo:[[UIImageView alloc] initWithImage:[UIImage imageNamed:@"logo.png"]]];

    // Set buttons appearance
    [self.logInView.dismissButton setImage:[UIImage imageNamed:@"exit.png"] forState:UIControlStateNormal];
    [self.logInView.dismissButton setImage:[UIImage imageNamed:@"exit_down.png"] forState:UIControlStateHighlighted];
    
    [self.logInView.facebookButton setImage:nil forState:UIControlStateNormal];
    [self.logInView.facebookButton setImage:nil forState:UIControlStateHighlighted];
    [self.logInView.facebookButton setBackgroundImage:[UIImage imageNamed:@"facebook_down.png"] forState:UIControlStateHighlighted];
    [self.logInView.facebookButton setBackgroundImage:[UIImage imageNamed:@"facebook.png"] forState:UIControlStateNormal];
    [self.logInView.facebookButton setTitle:@"" forState:UIControlStateNormal];
    [self.logInView.facebookButton setTitle:@"" forState:UIControlStateHighlighted];

    [self.logInView.twitterButton setImage:nil forState:UIControlStateNormal];
    [self.logInView.twitterButton setImage:nil forState:UIControlStateHighlighted];
    [self.logInView.twitterButton setBackgroundImage:[UIImage imageNamed:@"twitter.png"] forState:UIControlStateNormal];
    [self.logInView.twitterButton setBackgroundImage:[UIImage imageNamed:@"twitter_down.png"] forState:UIControlStateHighlighted];
    [self.logInView.twitterButton setTitle:@"" forState:UIControlStateNormal];
    [self.logInView.twitterButton setTitle:@"" forState:UIControlStateHighlighted];
    
    [self.logInView.signUpButton setBackgroundImage:[UIImage imageNamed:@"signup.png"] forState:UIControlStateNormal];
    [self.logInView.signUpButton setBackgroundImage:[UIImage imageNamed:@"signup_down.png"] forState:UIControlStateHighlighted];
    [self.logInView.signUpButton setTitle:@"" forState:UIControlStateNormal];
    [self.logInView.signUpButton setTitle:@"" forState:UIControlStateHighlighted];
    
    // Add login field background
    fieldsBackground = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"background.png"]];
    [self.logInView insertSubview:fieldsBackground atIndex:1];
    
    // Remove text shadow
    CALayer *layer = self.logInView.usernameField.layer;
    layer.shadowOpacity = 0.0;
    layer = self.logInView.passwordField.layer;
    layer.shadowOpacity = 0.0;
    
    // Set field text color
    [self.logInView.usernameField setTextColor:[UIColor colorWithRed:135.0f/255.0f green:118.0f/255.0f blue:92.0f/255.0f alpha:1.0]];
    [self.logInView.passwordField setTextColor:[UIColor colorWithRed:135.0f/255.0f green:118.0f/255.0f blue:92.0f/255.0f alpha:1.0]];

}

We start by changing the background to our own image and setting the logo field to be our app's logo instead of "Parse". Note that changing the background will also remove the table-like styling seen behind the username and password text fields, so we'll have to add our own later.

Next, we change all of the buttons to use custom images. The astute reader might notice that we use both setImage and setBackgroundImage in some cases. The image property of a button is typically used for an icon that is displayed with the text, while the backgroundImage is meant to be used as the background. In case of the Facebook and Twitter buttons, we want to remove the icon images so we the image property to nil.

We then add an image view for the table-like text field background since the default was removed along with the background. We also modify the text field's text color and shadow by modifying the layer and textColor properties. If you want to change a view's layer, you will need to import QuartzCore by adding the following line to your .m file.

#import <QuartzCore/QuartzCore.h>

Since our logo and button images are not the same size as the default ones, we need to change the frame of these views. As mentioned earlier, we do this modification in the viewDidLayoutSubviews method.

- (void)viewDidLayoutSubviews {
    [super viewDidLayoutSubviews];
    
    // Set frame for elements
    [self.logInView.dismissButton setFrame:CGRectMake(10.0f, 10.0f, 87.5f, 45.5f)];
    [self.logInView.logo setFrame:CGRectMake(66.5f, 70.0f, 187.0f, 58.5f)];
    [self.logInView.facebookButton setFrame:CGRectMake(35.0f, 287.0f, 120.0f, 40.0f)];
    [self.logInView.twitterButton setFrame:CGRectMake(35.0f+130.0f, 287.0f, 120.0f, 40.0f)];
    [self.logInView.signUpButton setFrame:CGRectMake(35.0f, 385.0f, 250.0f, 40.0f)];
    [self.logInView.usernameField setFrame:CGRectMake(35.0f, 145.0f, 250.0f, 50.0f)];
    [self.logInView.passwordField setFrame:CGRectMake(35.0f, 195.0f, 250.0f, 50.0f)];
    [self.fieldsBackground setFrame:CGRectMake(35.0f, 145.0f, 250.0f, 100.0f)];
}

And that's it! We now have a fully customized PFLogInViewController. Of course, if we want to use the sign up view controller, we'll have to customize it to match our new style.

3.2. Subclassing PFSignUpViewController

As with the delegates we saw in Section 1.2 and Section 1.3, there are many parallels between the view customization of login and signup view controllers. We use the same two methods to add our modifications, and access the view elements through the signUpView property.

We won't go through the entire code for these two methods since they are so similar to the implementations we saw in Section 3.1, but you can check out the full source code if you're interested.

In the viewDidLoad method, there is one interesting modification that is worth looking at. If we decide to add the additional text field in the sign up form, it is likely we don't want it to say "Additional". It is very simple to customize this text.

- (void)viewDidLoad {
    ...
    // Change the placeholder text "Additional" to "Phone number"
    [self.signUpView.additionalField setPlaceholder:@"Phone number"];
}

We can even perform additional validation to ensure the entered text is a phone number by using the delegate methods seen in Section 1.3.

In this implementation of viewDidLayoutSubviews we have a few more frames to changes. Apart from the buttons and the logo, we also want to move the entire form down since our logo looks a little crammed on some devices. To do this, we simply change the frame of each text field element to be a little lower. We also take the opportunity to shorten the text fields a bit since our background is slightly smaller than the default.

- (void)viewDidLayoutSubviews {
    [super viewDidLayoutSubviews];
    
    // Move all fields down on smaller screen sizes
    float yOffset = [UIScreen mainScreen].bounds.size.height <= 480.0f ? 30.0f : 0.0f;

    CGRect fieldFrame = self.signUpView.usernameField.frame;

    [self.signUpView.dismissButton setFrame:CGRectMake(10.0f, 10.0f, 87.5f, 45.5f)];
    [self.signUpView.logo setFrame:CGRectMake(66.5f, 70.0f, 187.0f, 58.5f)];
    [self.signUpView.signUpButton setFrame:CGRectMake(35.0f, 385.0f, 250.0f, 40.0f)];
    [self.fieldsBackground setFrame:CGRectMake(35.0f, fieldFrame.origin.y + yOffset, 250.0f, 174.0f)];
    
    [self.signUpView.usernameField setFrame:CGRectMake(fieldFrame.origin.x + 5.0f,
                                                       fieldFrame.origin.y + yOffset,
                                                       fieldFrame.size.width - 10.0f,
                                                       fieldFrame.size.height)];
    yOffset += fieldFrame.size.height;
    
    [self.signUpView.passwordField setFrame:CGRectMake(fieldFrame.origin.x + 5.0f,
                                                       fieldFrame.origin.y + yOffset,
                                                       fieldFrame.size.width - 10.0f,
                                                       fieldFrame.size.height)];
    yOffset += fieldFrame.size.height;
    
    [self.signUpView.emailField setFrame:CGRectMake(fieldFrame.origin.x + 5.0f,
                                                    fieldFrame.origin.y + yOffset,
                                                    fieldFrame.size.width - 10.0f,
                                                    fieldFrame.size.height)];
    yOffset += fieldFrame.size.height;
    
    [self.signUpView.additionalField setFrame:CGRectMake(fieldFrame.origin.x + 5.0f,
                                                         fieldFrame.origin.y + yOffset,
                                                         fieldFrame.size.width - 10.0f,
                                                         fieldFrame.size.height)];
}

3.3. Using our Subclasses

Armed with our fully customized login and signup controllers, we can now display them just like we did in Section 1 and Section 2. In the viewDidAppear: method of the SubclassConfigViewController we present our new controllers. Remember to import these view controllers at the top of your .m file.

#import "MyLogInViewController.h"
#import "MySignUpViewController.h"
.
.
.
- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
 
    // Check if user is logged in
    if (![PFUser currentUser]) {        
        // Instantiate our custom log in view controller
        MyLogInViewController *logInViewController = [[MyLogInViewController alloc] init];
        [logInViewController setDelegate:self];
        [logInViewController setFacebookPermissions:[NSArray arrayWithObjects:@"friends_about_me", nil]];
        [logInViewController setFields:PFLogInFieldsUsernameAndPassword 
                                     | PFLogInFieldsTwitter 
                                     | PFLogInFieldsFacebook 
                                     | PFLogInFieldsSignUpButton 
                                     | PFLogInFieldsDismissButton];
        
        // Instantiate our custom sign up view controller
        MySignUpViewController *signUpViewController = [[MySignUpViewController alloc] init];
        [signUpViewController setDelegate:self];
        [signUpViewController setFields:PFSignUpFieldsDefault | PFSignUpFieldsAdditional];
        
        // Link the sign up view controller
        [logInViewController setSignUpController:signUpViewController];
        
        // Present log in view controller
        [self presentViewController:logInViewController animated:YES completion:NULL];
    }
}

You now have a complete login flow with your very own look and feel!

4. Wrapping Up

In this tutorial, we saw how easy it is to create a complete and robust login and signup flow using Parse. In Section 1 we discussed the basics of the PFLogInViewController and the PFSignUpViewController as well as their associated delegate protocols. We then looked at the different field options available in Section 2 and made a login flow using Twitter and Facebook. To finish it off, in Section 3 we discovered how to fully customize the look and feel through subclassing.

If you have any more questions about the PFLogInViewController and the PFSignUpViewController, check out the iOS Guide, the API docs or sent us an email at feedback@parse.com.