1. Introduction
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:
- Text fields
- Buttons
- Alerts
How would you rate your level of experience building iOS apps?
2. Set up your development environment
Download the starter codelab 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: | ||
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! You should see the app and its form.
3. Update the storyboard and view controller
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.
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];
Import MDC text field themers in the view controller
At the top of ViewController.m
, add the following above @interface ViewController () <UITextFieldDelegate>
:
#import "MaterialTextFields+ColorThemer.h"
#import "MaterialTextFields+TypographyThemer.h"
Apply the MDC text field themers
At the top of ViewController.m
, add the following to viewDidLoad
underneath the MDCTextInputController initializations:
MDCSemanticColorScheme *colorScheme = [[MDCSemanticColorScheme alloc] init];
[MDCTextFieldColorThemer applySemanticColorScheme:colorScheme
toTextInputController:self.nameController];
[MDCTextFieldColorThemer applySemanticColorScheme:colorScheme
toTextInputController:self.addressController];
[MDCTextFieldColorThemer applySemanticColorScheme:colorScheme
toTextInputController:self.cityController];
[MDCTextFieldColorThemer applySemanticColorScheme:colorScheme
toTextInputController:self.stateController];
[MDCTextFieldColorThemer applySemanticColorScheme:colorScheme toTextInputController:self.zipController];
MDCTypographyScheme *typographyScheme = [[MDCTypographyScheme alloc] init];
[MDCTextFieldTypographyThemer applyTypographyScheme:typographyScheme
toTextInputController:self.nameController];
[MDCTextFieldTypographyThemer applyTypographyScheme:typographyScheme
toTextInputController:self.addressController];
[MDCTextFieldTypographyThemer applyTypographyScheme:typographyScheme
toTextInputController:self.cityController];
[MDCTextFieldTypographyThemer applyTypographyScheme:typographyScheme
toTextInputController:self.stateController];
[MDCTextFieldTypographyThemer applyTypographyScheme:typographyScheme
toTextInputController:self.zipController];
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.
4. Update the button
MDC has buttons with:
- Ink ripples
- Rounded corners
- Theming support
- Beautiful shadows
- Pixel-perfect layout and typography
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 MDCButton *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 MDCButton
:
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:
Import the button themer
At the top of ViewController.m
, add the following below #import "MaterialButtons.h"
#import "MaterialButtons+ButtonThemer.h"
Theme the button
In ViewController.m
, add the following to viewDidLoad
under [super viewDidLoad]
:
[MDCContainedButtonThemer applyScheme:[[MDCButtonScheme alloc] init] toButton:self.saveButton];
Make the button text uppercase
Material Design recommends you make the button text uppercase.
In ViewController.m
, add the following to viewDidLoad
under [super viewDidLoad]
:
[self.saveButton setTitle:@"Save" forState:UIControlStateNormal];
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
, update the following method to account for hitAreaInsets
:
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
// TODO: Update @c hitAreaInsets.
CGFloat verticalInset = MIN(0, (CGRectGetHeight(self.saveButton.bounds) - 48) / 2);
self.saveButton.hitAreaInsets = UIEdgeInsetsMake(verticalInset, 0, verticalInset, 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.
5. Update alerts
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) {
}]];
Import the dialogs themer
At the top of ViewController.m
, add the following below #import "MaterialDialogs.h"
#import "MaterialDialogs+DialogThemer.h"
Theme the alert
In ViewController.m
, add the following to saveDidTouch
just before [self presentViewController:saveAlert animated:YES completion:nil];
MDCAlertScheme *alertScheme = [[MDCAlertScheme alloc] init];
[MDCAlertControllerThemer applyScheme:alertScheme toAlertController:saveAlert];
Build and run on an iPad. Press SAVE:
The alert is big enough to handle the long text.
6. Recap
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.