MDC-101 iOS: Material Components (MDC) Basics (Objective-C)

1. Introduction

logo_components_color_2x_web_96dp.png

Material Components (MDC) help developers implement Material Design. Created by a team of engineers and UX designers at Google, MDC features dozens of beautiful and functional UI components and is available for Android, iOS, web and Flutter.material.io/develop

What are Material Design and Material Components for iOS?

Material Design is a system for building bold and beautiful digital products. By uniting style, branding, interaction, and motion under a consistent set of principles and components, product teams can realize their greatest design potential.

Material Components for iOS (MDC iOS) unites design and engineering with a library of components for creating consistency across apps and websites. As the Material Design system evolves, these components are updated to ensure consistent pixel-perfect implementation and adherence to Google's front-end development standards. MDC is also available for the Android, web, and Flutter.

In this codelab, you'll build a login page using several MDC iOS components.

What you'll build

This codelab is the first of 4 codelabs that will guide you through building an app called Shrine, an e-commerce website that sells clothing and home goods. It will demonstrate how you can customize components to reflect any brand or style using MDC-iOS.

In this codelab, you'll build a login page for Shrine that contains:

  • Two text fields, one for entering a username and the other for a password
  • Two buttons, one for "Cancel" and the other for "Next"
  • The name of the app (Shrine)
  • An image of Shrine's logo

7659a88b05c1364c.png

MDC iOS components in this codelab

  • Text field
  • Button

What you'll need

  • Xcode
  • The sample code
  • Basic knowledge of Xcode, the iOS SDK and Objective-C

How would you rate your level of experience building iOS apps?

Novice Intermediate Proficient

2. Set up your development environment

Download the starter codelab app

The starter app is located within the material-components-ios-codelabs-master/MDC-101/ObjectiveC/Starter directory. Be sure to cd into that directory before beginning.

...or clone it from GitHub

To clone this codelab from GitHub, run the following commands:

git clone https://github.com/material-components/material-components-ios-codelabs
cd material-components-ios-codelabs/MDC-101/ObjectiveC/Starter/Shrine

Run the starter app

1. In Xcode open the workspace "Shrine.xcworkspace". If you see the "Welcome to Xcode" window, click "Open another project...", navigate to the file and click open.

OR: Go to File > Open... and navigate to "Shrine.xcworkspace" and click "Open".

OR: At the terminal enter:
open Shrine.xcworkspace

2. Run the app by pressing the play button at the top of the window.Note: Screenshots are generated from building on an iPhone 6s.

Success! The starter code for Shrine's login screen should be running in your simulator. You should see the name "Shrine" and the Shrine logo just below it.

54cecc4fe1a62697.png

3. Add text fields

To begin, we'll add two text fields to our login page, where a user will be able to enter their username and password. We'll use the MDC Text Field component, which displays a floating label by default.

c58c948bb1c11813.png

Add the required header

In LoginViewController.m, add the following header:

//TODO: Add MaterialTextFields #import
#import <MaterialComponents/MaterialTextFields.h>

Add for text field properties

In LoginViewController.m, add the following properties underneath the existing @property(nonatomic) UIImageView *logoImageView in @interface:

// Text Field Properties
//TODO: Add Text Field Properties
@property(nonatomic) MDCTextField *usernameTextField;
@property(nonatomic) MDCTextField *passwordTextField;

You'll also need a Text Input Controller for each text field.

@property(nonatomic) MDCTextInputControllerOutlined *usernameTextFieldController;
@property(nonatomic) MDCTextInputControllerOutlined *passwordTextFieldController;

Initialize the text fields and add them to the view

In LoginViewController.m, add the following code underneath the existing logoImageView initialization in the viewDidLoad: method.

// Text Field Init
//TODO: Instantiate Text Fields
self.usernameTextField = [[MDCTextField alloc] initWithFrame:CGRectZero];
self.usernameTextField.translatesAutoresizingMaskIntoConstraints = NO;
self.usernameTextField.delegate = self;
self.usernameTextField.clearButtonMode = UITextFieldViewModeUnlessEditing;
self.usernameTextField.backgroundColor = self.scrollView.backgroundColor;
[self.scrollView addSubview:self.usernameTextField];

Now initialize and associate the Text Input Controller for Username Text Field.

self.usernameTextFieldController =
    [[MDCTextInputControllerOutlined alloc] initWithTextInput:self.usernameTextField];
self.usernameTextFieldController.placeholderText = @"Username";

Repeat the process for the password text field.

self.passwordTextField = [[MDCTextField alloc] initWithFrame:CGRectZero];
self.passwordTextField.translatesAutoresizingMaskIntoConstraints = NO;
self.passwordTextField.delegate = self;
self.passwordTextField.clearButtonMode = UITextFieldViewModeUnlessEditing;
self.passwordTextField.secureTextEntry = YES;
self.passwordTextField.backgroundColor = self.scrollView.backgroundColor;
[self.scrollView addSubview:self.passwordTextField];

self.passwordTextFieldController =
  [[MDCTextInputControllerOutlined alloc] initWithTextInput:self.passwordTextField];
self.passwordTextFieldController.placeholderText = @"Password";

Position the text fields in the layout

To position the Text Fields, add this code to LoginViewController.m. underneath logoImageView's constraints:

  // Text Field Constraints
  //TODO: Add Text Field Constraints
  NSLayoutConstraint *usernameTopConstraint =
  [NSLayoutConstraint constraintWithItem:self.usernameTextField
                               attribute:NSLayoutAttributeTop
                               relatedBy:NSLayoutRelationEqual
                                  toItem:self.titleLabel
                               attribute:NSLayoutAttributeBottom
                              multiplier:1
                                constant:22];
  [constraints addObject:usernameTopConstraint];

  NSLayoutConstraint *centerUsernameConstraint =
  [NSLayoutConstraint constraintWithItem:self.usernameTextField
                               attribute:NSLayoutAttributeCenterX
                               relatedBy:NSLayoutRelationEqual
                                  toItem:self.scrollView
                               attribute:NSLayoutAttributeCenterX
                              multiplier:1 constant:0];
  [constraints addObject:centerUsernameConstraint];

  NSArray <NSLayoutConstraint *> *horizontalUsernameConstraints =
  [NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[username]-|"
                                          options:0
                                          metrics:nil
                                            views:@{ @"username" : self.usernameTextField }];
  [constraints addObjectsFromArray:horizontalUsernameConstraints];

  NSLayoutConstraint *passwordTopConstraint =
  [NSLayoutConstraint constraintWithItem:self.passwordTextField
                               attribute:NSLayoutAttributeTop
                               relatedBy:NSLayoutRelationEqual
                                  toItem:self.usernameTextField
                               attribute:NSLayoutAttributeBottom
                              multiplier:1
                                constant:8];
  [constraints addObject:passwordTopConstraint];

  NSLayoutConstraint *centerPasswordConstraint =
  [NSLayoutConstraint constraintWithItem:self.passwordTextField
                               attribute:NSLayoutAttributeCenterX
                               relatedBy:NSLayoutRelationEqual
                                  toItem:self.scrollView
                               attribute:NSLayoutAttributeCenterX
                              multiplier:1 constant:0];
  [constraints addObject:centerPasswordConstraint];

  NSArray <NSLayoutConstraint *> *horizontalPasswordConstraints =
  [NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[password]-|"
                                          options:0
                                          metrics:nil
                                            views:@{ @"password" : self.passwordTextField }];
  [constraints addObjectsFromArray:horizontalPasswordConstraints];

Check for a minimum password length

Text fields can indicate if the field input is valid or if it contains an error.

You should:

  • Require the Password text field to have a minimum length of 8.

Find the UITextFieldDelegate and add the following implementation of textFieldShouldReturn:

//TODO: Add Text Field Validation
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
  [textField resignFirstResponder];

  // Text Field Validation
  if (textField == (UITextField *)self.passwordTextField) {
     if (self.passwordTextField.text.length < 8) {
       [self.passwordTextFieldController setErrorText:@"Password is too short" errorAccessibilityValue:nil];
    } else {
       [self.passwordTextFieldController setErrorText:nil errorAccessibilityValue:nil];
    }
  }

  return NO;
}

Build and run

The interface should now show two text fields for Username and Password! Check out the floating label animation when you tap on a text field:

c58c948bb1c11813.png

4. Add buttons

Next, we'll add two buttons to our login page: "Cancel" and "Next." We'll use the MDC Button component to finish building our interface.

7659a88b05c1364c.png

Add the required header

In LoginViewController.m, add the following header:

//TODO: Add MaterialButtons #import
#import <MaterialComponents/MaterialButtons.h>

Create properties for the buttons

In LoginViewController.m, add the following properties to the @interface, underneath the text field properties created earlier:

// Button Properties
//TODO: Add Button Properties
@property(nonatomic) MDCButton *cancelButton;
@property(nonatomic) MDCButton *nextButton;

Initialize the buttons and add them to the view

In LoginViewController.m, add the following code underneath the the text fields in the viewDidLoad: method:

  // Button Init
  //TODO: Instantiate Buttons
  self.cancelButton = [[MDCButton alloc] init];
  self.cancelButton.translatesAutoresizingMaskIntoConstraints = NO;
  [self.cancelButton setTitle:@"CANCEL" forState:UIControlStateNormal];
  [self.cancelButton addTarget:self
                        action:@selector(didTapCancel:)
              forControlEvents:UIControlEventTouchUpInside];
  [self.scrollView addSubview:self.cancelButton];

  self.nextButton = [[MDCButton alloc] initWithFrame:CGRectZero];
  self.nextButton.translatesAutoresizingMaskIntoConstraints = NO;
  [self.nextButton setTitle:@"NEXT" forState:UIControlStateNormal];
  [self.nextButton addTarget:self
                      action:@selector(didTapNext:)
            forControlEvents:UIControlEventTouchUpInside];
  [self.scrollView addSubview:self.nextButton];

Add layout constraints to position the buttons

To position the Text Fields, add code to viewDidLoad: underneath the text field constraints:

  // Button Constraints
  //TODO: Add Button Constraints
  NSLayoutConstraint *cancelTopConstraint =
  [NSLayoutConstraint constraintWithItem:self.cancelButton
                               attribute:NSLayoutAttributeTop
                               relatedBy:NSLayoutRelationEqual
                                  toItem:self.passwordTextField
                               attribute:NSLayoutAttributeBottom
                              multiplier:1
                                constant:8];
  [constraints addObject:cancelTopConstraint];

  NSLayoutConstraint *centerCancelConstraint =
  [NSLayoutConstraint constraintWithItem:self.cancelButton
                               attribute:NSLayoutAttributeCenterX
                               relatedBy:NSLayoutRelationEqual
                                  toItem:self.scrollView
                               attribute:NSLayoutAttributeCenterX
                              multiplier:1 constant:0];
  [constraints addObject:centerCancelConstraint];

  NSLayoutConstraint *centerButtonsConstraint =
  [NSLayoutConstraint constraintWithItem:self.cancelButton
                               attribute:NSLayoutAttributeCenterY
                               relatedBy:NSLayoutRelationEqual
                                  toItem:self.nextButton
                               attribute:NSLayoutAttributeCenterY
                              multiplier:1 constant:0];
  [constraints addObject:centerButtonsConstraint];

  NSArray <NSLayoutConstraint *> *horizontalButtonConstraints =
  [NSLayoutConstraint constraintsWithVisualFormat:@"H:[cancel]-[next]-|"
                                          options:0
                                          metrics:nil
                                            views:@{ @"cancel" : self.cancelButton, @"next" : self.nextButton }];
  [constraints addObjectsFromArray:horizontalButtonConstraints];

  NSLayoutConstraint *scrollContentBottomConstraint =
                                            [NSLayoutConstraint constraintWithItem:self.nextButton
                                                                         attribute:NSLayoutAttributeBottom
                                                                         relatedBy:NSLayoutRelationEqual
                                                                            toItem:self.scrollView.contentLayoutGuide
                                                                         attribute:NSLayoutAttributeBottomMargin
                                                                        multiplier:1
                                                                          constant:-20];
  [constraints addObject:scrollContentBottomConstraint];

Add button actions

Add the button action handlers.

#pragma mark - Action Handling

// Button Action Handlers
//TODO: Add Action Handlers
- (void)didTapNext:(id)sender {
  [self dismissViewControllerAnimated:YES completion:NULL];
}

- (void)didTapCancel:(id)sender {
  [self dismissViewControllerAnimated:YES completion:NULL];
}

Build and run

You should now see a page that contains these two buttons. Try it out 7659a88b05c1364c.png

5. All done

Material Components for iOS has helped you create a beautiful login page that conforms to the Material Design guidelines, which looks and behaves consistently across all devices.

Next steps

Text Field and Button are two core components in MDC iOS, but there are many more! You can explore the rest of the components in MDC iOS.

Alternatively, head over to MDC-102: Material Design Structure and Layout to learn about AppBar and Card Collection. Thanks for trying Material Components. We hope you enjoyed this codelab!

I was able to complete this codelab with a reasonable amount of time and effort

Strongly agree Agree Neutral Disagree Strongly disagree

I would like to continue using Material Components in the future

Strongly agree Agree Neutral Disagree Strongly disagree