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

Material Components (MDC) isn't a new coding system that requires a paradigm shift in your app. MDC is built on the same classes and APIs you already know in iOS. MDC can be added as needed to your existing app. Some components can make an immediate design improvement to your app.

What you'll build

In this codelab, you'll replace some existing components in a form with new ones by MDC.

MDC-iOS components in this codelab:

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

Novice Intermediate Proficient

Download the starter codelab app

Download starter app

The starter app is located in the material-components-ios-codelabs-master/MDC-111/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-111/ObjectiveC/Starter

Run the starter app

1. In Xcode open the workspace "MDC-111.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 the file and click "Open".

OR: At the terminal enter:
open MDC-111.xcworkspace

2. Run the app by pressing the play button at the top of the window.

Success! You should see the app and its form.

Material Design text fields have a major usability gain over plain text fields. By defining the hit zone with an outline or a background fill, users are more likely to interact with your form or identify text fields within more complicated content.

Remove dividers

The file Main.storyboard has been setup to mimic a common iOS form design. You might recognize if from email clients or as part of tables you'd find in Settings. The first thing we need to change is removing the dividers.

In Main.storyboard, navigate to the Shipping Address Scene and expand all the arrows until you see the dividers and the textfields.

Delete each of the dividers.

Change the text field class in the storyboard

Select each text field. Open the Identify Inspector on the right. (If you can't find the Identity Inspector, you can access it in View > Utilities > Show Identity Inspector.)

In the Custom Class field, type MDCTextField. Do this for all 5 text fields.

Set the text fields' background color to white. In order for the text field to have the slick effect of the placeholder label cutting through the border, we need to set its background color to white.

In the storyboard, select all the text fields and open the Attributes Inspector. (You can also open the Attributes Inspector in View > Utilities > Show Attributes Inspector.) In the View section, change Background to be White Color.

Import MDC text fields in the view controller

At the top of ViewController.m, add the following above @interface ViewController () <UITextFieldDelegate>:

#import "MaterialTextFields.h"

Change the text field class in the view controller

In ViewController.m, change each UITextField to MDCTextField.

@property (weak, nonatomic) IBOutlet MDCTextField *name;
@property (weak, nonatomic) IBOutlet MDCTextField *address;
@property (weak, nonatomic) IBOutlet MDCTextField *city;
@property (weak, nonatomic) IBOutlet MDCTextField *state;
@property (weak, nonatomic) IBOutlet MDCTextField *zip;

Add text input controllers

MDCTextField is subclassed from UITextField. It has a lot of capabilities but requires a controller to style it. Those controllers have to stay in memory so let's add them as properties of the view controller.

In ViewController.m, add the following below @interface ViewController () <UITextFieldDelegate>:

@property (nonatomic) MDCTextInputControllerOutlined *nameController;
@property (nonatomic) MDCTextInputControllerOutlined *addressController;
@property (nonatomic) MDCTextInputControllerOutlined *cityController;
@property (nonatomic) MDCTextInputControllerOutlined *stateController;
@property (nonatomic) MDCTextInputControllerOutlined *zipController;

Now we initialize them in viewDidLoad.

In ViewController.m, add the following to viewDidLoad under [super viewDidLoad]:

self.nameController = [[MDCTextInputControllerOutlined alloc] initWithTextInput:self.name];
self.addressController = [[MDCTextInputControllerOutlined alloc] initWithTextInput:self.address];
self.cityController = [[MDCTextInputControllerOutlined alloc] initWithTextInput:self.city];
self.stateController = [[MDCTextInputControllerOutlined alloc] initWithTextInput:self.state];
self.zipController = [[MDCTextInputControllerOutlined alloc] initWithTextInput:self.zip];

Build and run:

The text fields are all updated to use the newer designs in MDC. Let's change the trailing constraint to be something more appropriate for this design.

Update trailing constraint constant

In the storyboard, click on the Safe Area in the view hierarchy and open the Size Inspector. (If you can't find the Size Inspector, you can open it in View > Utilities > Show Size Inspector.)

Double click or press Edit on the constraint called "Align Trailing to Name". Change the constant value to be 20 instead of 0.

Build and run:

Let's refine the layout even more. Change the Safe Area's leading constraint constant to the Name text field to also be 20 instead of 32.

Build and run:

The layout is perfect!

Add an error

MDC text fields have built in error presentation. MDC adds red text beneath your text field and updates decorations to be red too.

In ViewController.m, add the following anywhere in viewDidLoad:

self.zip.delegate = self;

Then, also in ViewController.m, add the following method under viewDidLoad:

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
  NSString *finishedString =
    [textField.text stringByReplacingCharactersInRange:range withString:string];

  if (textField == self.zip) {
    if ([finishedString rangeOfCharacterFromSet:[NSCharacterSet letterCharacterSet]].length > 0) {
      [self.zipController setErrorText:@"Error: Zip can only contain numbers"
               errorAccessibilityValue:nil];
    } else {
      [self.zipController setErrorText:nil errorAccessibilityValue:nil];
    }
  }
  return YES;
}

Build and run. Enter a letter in the zip field:

The zip text field is red and has error text beneath it. To get rid of the error, just replace the letter with a number.

They are built on UIButton (the standard iOS button class), so you already know how to use them in your code.

Import MDC buttons in the view controller

At the top of ViewController.m, add the following above @interface ViewController () <UITextFieldDelegate>:

#import "MaterialButtons.h"

Change button class

In ViewController.m, in the properties section, change the class of the UIButton *saveButton IBOutlet :

@property (weak, nonatomic) IBOutlet MDCRaisedButton *saveButton;

In the storyboard, select the save button and open the Identify Inspector on the right. (If you can't find the Identity Inspector, you can access it in View > Utilities > Show Identity Inspector.)

In the Custom Class field, type MDCRaisedButton:

Still in the storyboard, open the Attributes Inspector. (If you can't find the Attributes Inspector, you can access it in View > Utilities > Show Attributes Inspector.)

Set the button type to custom:

Theme the button's colors

Material Design allows for expressive branding choices. Let's make a couple to see how much those choices can change an app.

In the storyboard, select the save button and open the Attributes Inspector. (If you can't find the Attributes Inspector, you can access it in View > Utilities > Show Attributes Inspector.)

Set the text color to be white:

Then set the button's background color to hex color #0078FF:

Make the button text uppercase

Material Design recommends you make the button text uppercase.

In the storyboard, double click on the save button and make the title SAVE:

Make the button wide

Material Design recommends buttons be 88 points wide or more.

In the storyboard, selected the button. We're going to make a width constraint. To do this, select the save button and then press the add new constraints button in the bottom right corner of the canvas. It looks like a little tie fighter:

Select the checkbox for width and set the constant to 88 and then click away from the dialog to close it:

Since we want it to be at least 88, we need to make this a greater-than-or-equal-to constraint. Select the save button and open the Size Inspector. (If you can't find the Size Inspector, you can open it in View > Utilities > Show Size Inspector.)

Select the width constraint and click edit. Change the = to >=:

Click away from the dialog. Build and run:

The button is fresher and fits in with the style of your text fields. But if you tap it, you may get a warning in the console, "Button touch target does not meet minimum size guidelines of (48, 48)." That's a great point. We do want to make sure our buttons are tappable by all finger sizes and by people with motor disabilities.

Add hit area insets

In ViewController.m, add the following method:

- (void)viewDidLayoutSubviews {
  [super viewDidLayoutSubviews];

  self.saveButton.hitAreaInsets = UIEdgeInsetsMake((48 - self.saveButton.bounds.size.height) / -2, 0, (48 - self.saveButton.bounds.size.height) / -2, 0);
}

Build and run and tap the save button. There's no warning in the console! And you can tap a little outside the button's frame and still trigger a touch.

Alerts are a familiar way to present errors, mandatory choices, and one-line forms. But UIAlertController is limited in the amount of text it can show on an iPad.

Import MDC alerts

At the top of ViewController.m, add the following above @interface ViewController () <UITextFieldDelegate>:

#import "MaterialDialogs.h"

Replace alert controller class

Change the class of the alert controller shown when the form isn't filled out completely.

In ViewController.m, in saveDidTouch:, replace the UIAlertController variable with an MDCAlertController:

MDCAlertController *saveAlert = [MDCAlertController alertControllerWithTitle:@"Do you accept these terms?" message:kLorem];

Replace alert action class

In ViewController.m, in saveDidTouch:, replace the UIAlertAction variables with MDCAlertActions:

[saveAlert addAction:[MDCAlertAction actionWithTitle:@"Yes" handler:^(MDCAlertAction * _Nonnull action) {
    self.name.text = nil;
    self.address.text = nil;
    self.city.text = nil;
    self.state.text = nil;
    self.zip.text = nil;
}]];
[saveAlert addAction:[MDCAlertAction actionWithTitle:@"Cancel" handler:^(MDCAlertAction * _Nonnull action) {
}]];

Build and run on an iPad. Press SAVE:

The alert is big enough to handle the long text.

You've replaced some common components to show immediate value: text fields, buttons, alerts or menus and you didn't have to do a wholesale redesign of your app. Other components can make a big difference too, such as the top app bar and typography.

Next steps

You can explore even more components in MDC-iOS by visiting the MDC-iOS catalog.

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