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/Swift/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/Swift/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 it 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 Class field, type MDCTextField
or select it from the dropdown menu. Do this for all 5 text fields.
Set the text fields' background color to white
In order for the text field to have the elegant 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. (You may need to scroll down to find this section.)
Import MDC in the view controller
At the top of ViewController.swift
, add the following under import UIKit
:
import MaterialComponents
Change the text field class in the view controller
In ViewController.swift
, change each UITextField
to MDCTextField
.
@IBOutlet weak var name: MDCTextField!
@IBOutlet weak var address: MDCTextField!
@IBOutlet weak var city: MDCTextField!
@IBOutlet weak var state: MDCTextField!
@IBOutlet weak var zip: MDCTextField!
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.swift
, add the following under @IBOutlet weak var saveButton: MDCRaisedButton!
:
// MARK: Properties
var nameController: MDCTextInputControllerOutlined?
var addressController: MDCTextInputControllerOutlined?
var cityController: MDCTextInputControllerOutlined?
var stateController: MDCTextInputControllerOutlined?
var zipController: MDCTextInputControllerOutlined?
Now we initialize them in viewDidLoad
.
In ViewController.swift
, add the following to viewDidLoad
under super.viewDidLoad()
:
// TODO: Instantiate controllers
nameController = MDCTextInputControllerOutlined(textInput: name)
addressController = MDCTextInputControllerOutlined(textInput: address)
cityController = MDCTextInputControllerOutlined(textInput: city)
stateController = MDCTextInputControllerOutlined(textInput: state)
zipController = MDCTextInputControllerOutlined(textInput: zip)
Build and run. Click on the Name field to see its colored highlight.
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 Main.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:
Great! Now let's refine the layout even more. Change the Safe Area's leading constraint constant of the Name text field to also be 20 instead of 32.
Build and run. Notice that the side margins are narrower.
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.swift
, add the following at the end of viewDidLoad
:
zip.delegate = self
Also in ViewController.swift
, add the following method under viewDidLoad
:
// MARK: UITextFieldDelegate methods
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
guard let text = textField.text,
let range = Range(range, in: text),
textField == zip else {
return true
}
let finishedString = text.replacingCharacters(in: range, with: string)
if finishedString.rangeOfCharacter(from: CharacterSet.letters) != nil {
zipController?.setErrorText("Error: Zip can only contain numbers", errorAccessibilityValue: nil)
} else {
zipController?.setErrorText(nil, errorAccessibilityValue: nil)
}
return true
}
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.
Change button class
In ViewController.swift
, in the properties section, change the class of the @IBOutlet weak var saveButton: UIButton!
to MDCRaisedButton:
@IBOutlet weak var saveButton: MDCButton!
In Main.storyboard
, select the save button and open the Identity 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, select MDCButton
from the dropdown menu. (Notice all other classes you could choose!):
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.swift
, add the following below @IBOutlet weak var saveButton: MDCButton! let buttonScheme = MDCButtonScheme()
let buttonScheme = MDCButtonScheme()
Theme the button
In ViewController.swift
, add the following to viewDidLoad
under super.viewDidLoad()
MDCContainedButtonThemer.applyScheme(buttonScheme, to: saveButton)
Make the button text uppe****rcase
Material Design recommends you make the button text uppercase.
In ViewController.swift
, add the following to viewDidLoad
under super.viewDidLoad()
saveButton.setTitle("Save", for: .normal)
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 Add 1 Constraint:
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. We'll enlarge the tappable area by adding hit area insets to the button.
Add hit area insets
In ViewController.swift, update the following method to account for hitAreaInsets
:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
// TODO: Update `hitAreaInsets`.
let verticalInset = min(0, (self.saveButton.bounds.height - 48) / 2)
saveButton.hitAreaInsets = UIEdgeInsets(top: verticalInset,
left: 0,
bottom:verticalInset,
right: 0)
}
Build and run and tap the save button. There's no warning in the console! 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 the UIAlertController is limited in the amount of text it can show on an iPad.
Replace alert controller class
Change the class of the alert controller shown when the form isn't filled out completely.
In ViewController.swift
, in saveDidTouch
:, replace the UIAlertController
variable with an MDCAlertController.
(Notice MDCAlertController
's init
has fewer parameters):
let alert = MDCAlertController(title: "Do you accept these terms?", message: kLorem)
Replace alert action class
In ViewController.swift
, in saveDidTouch
:, replace the UIAlertAction
variables with MDCAlertActions
(Notice MDCAlertAction
's init
has fewer parameters):
alert.addAction(MDCAlertAction(title: "Yes", handler: { (_) in
self.name.text = nil
self.address.text = nil
self.city.text = nil
self.state.text = nil
self.zip.text = nil
}))
alert.addAction(MDCAlertAction(title: "Cancel", handler: nil))
Theme the alert
In ViewController.swift
, add the following to saveDidTouch
just before present(alert, animated: true, completion: nil)
MDCAlertControllerThemer.applyScheme(MDCAlertScheme(), to: alert)
Switch the simulator target to an iPad, then build and run. 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 whole 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.