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

In codelab MDC-101, you used two Material Components (MDC) to build a login page: text fields and buttons with ink ripples. Now let's expand upon this foundation by adding navigation, structure, and data.

What you'll build

In this codelab, you'll build a home screen for an app called Shrine, an e-commerce app that sells clothing and home goods. It will contain:

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 within the material-components-ios-codelabs-master/MDC-102/Swift/Starter directory. If using the terminal, 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-102/Swift/Starter

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 the file 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.

Success! You should see the Shrine login page from the MDC-101 codelab running in your simulator.

Now that the login screen is looking good, let's populate it with some products.

The home screen is revealed when the login page is dismissed, with a screen that says "You did it!". That's great! But now our user needs to have actions that they can take, as well as a sense of where they are in the app. To help with that, let's add navigation.

Material Design offers navigation patterns that ensure a high degree of usability. One of the most visible components is the top app bar.

Let's give users access to key actions by adding a top app bar.

Add an app bar property

In HomeViewController.swift, add the following property:

//TODO: Add an appBar property
var appBar = MDCAppBar()

Initialize and configure the app bar

While still in HomeViewController.swift, add the following to the viewDidLoad method:

// AppBar Setup
//TODO: Add the appBar controller and views
self.addChildViewController(appBar.headerViewController)
self.appBar.headerViewController.headerView.trackingScrollView = self.collectionView
appBar.addSubviewsToParent()

Setup the scrollview delegate methods

While still in HomeViewController.swift, add the following implementation for UIScrollViewDelegate:

//MARK: - UIScrollViewDelegate

// The following four methods must be forwarded to the tracking scroll view in order to implement
// the Flexible Header's behavior.

//TODO: Send the scrollView delegate messages to our appBar's headerView
extension HomeViewController {

  override func scrollViewDidScroll(_ scrollView: UIScrollView) {
    if (scrollView == self.appBar.headerViewController.headerView.trackingScrollView) {
      self.appBar.headerViewController.headerView.trackingScrollDidScroll()
    }
  }

  override func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
    if (scrollView == self.appBar.headerViewController.headerView.trackingScrollView) {
      self.appBar.headerViewController.headerView.trackingScrollDidEndDecelerating()
    }
  }

  override func scrollViewDidEndDragging(_ scrollView: UIScrollView,
                                         willDecelerate decelerate: Bool) {
    let headerView = self.appBar.headerViewController.headerView
    if (scrollView == headerView.trackingScrollView) {
      headerView.trackingScrollDidEndDraggingWillDecelerate(decelerate)
    }
  }

  override func scrollViewWillEndDragging(_ scrollView: UIScrollView,
                                          withVelocity velocity: CGPoint,
                                          targetContentOffset: UnsafeMutablePointer<CGPoint>) {
    let headerView = self.appBar.headerViewController.headerView
    if (scrollView == headerView.trackingScrollView) {
      headerView.trackingScrollWillEndDragging(withVelocity: velocity,
                                               targetContentOffset: targetContentOffset)
    }
  }

}

Add navigation items

App bars display navigation items attached to their parent view controller. Let's add some leading and trailing navigation items in viewDidLoad.

// Setup Navigation Items
//TODO: Create the items and set them on the view controller's navigationItems properties
let menuItemImage = UIImage(named: "MenuItem")
let templatedMenuItemImage = menuItemImage?.withRenderingMode(.alwaysTemplate)
let menuItem = UIBarButtonItem(image: templatedMenuItemImage,
                               style: .plain,
                               target: self,
                               action: #selector(menuItemTapped(sender:)))
self.navigationItem.leftBarButtonItem = menuItem

let searchItemImage = UIImage(named: "SearchItem")
let templatedSearchItemImage = searchItemImage?.withRenderingMode(.alwaysTemplate)
let searchItem = UIBarButtonItem(image: templatedSearchItemImage,
                               style: .plain,
                               target: nil,
                               action: nil)
let tuneItemImage = UIImage(named: "TuneItem")
let templatedTuneItemImage = tuneItemImage?.withRenderingMode(.alwaysTemplate)
let tuneItem = UIBarButtonItem(image: templatedTuneItemImage,
                                 style: .plain,
                                 target: nil,
                                 action: nil)
self.navigationItem.rightBarButtonItems = [ tuneItem, searchItem ]

Build and run

You app should now have an app bar with associated navigation items. (Click Next on the login screen to see it.)

Now the app has a leading button, a title, and two actions on the trailing side.

Now that our app has some structure, let's connect our collection view into our catalog of products.

Add a cell

Let's start by adding a single cell underneath the top app bar. This cell should have a region for an image, title, and label (for secondary text).

To make the cell visible, in HomeViewController.swift, update the collectionView(numberOfItemsInSection:) method. Eventually we will return the number of items in our catalog, but to test the cell rendering, let's return 1 for now.

override func collectionView(_ collectionView: UICollectionView,
                             numberOfItemsInSection section: Int) -> Int {
  //TODO: Set the number of cells to be equal to the number of products in the catalog
  return 1
}

Build and run

In this preview, you can see the basic cell. It includes two labels and a space for an image of the product.

Populate cells with our model data

To return the number of products in our catalog, in HomeViewController.swift, update the collectionView(numberOfItemsInSection:) delegate method.

  override func collectionView(_ collectionView: UICollectionView,
                               numberOfItemsInSection section: Int) -> Int {
    //TODO: Set the number of cells to be equal to the number of products in the catalog
    return Catalog.count
  }

Now that we have created the correct number of cells for our catalog, populate each cell with the appropriate product by updating collectionView(cellForItemAt:)

  override func collectionView(_ collectionView: UICollectionView,
                               cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell = self.collectionView?.dequeueReusableCell(withReuseIdentifier: "ProductCell",
                                                        for: indexPath) as! ProductCell
    //TODO: Set the properties of the cell to reflect to product from the model
    let product = Catalog.productAtIndex(index: indexPath.row)
    cell.imageView.image = UIImage(named: product.imageName)
    cell.nameLabel.text = product.productName
    cell.priceLabel.text = product.price

    return cell
  }

Build and run

You did it! We can now scroll through the entire catalog of Shrine products.

Now that our product collection is being displayed, let's change our existing collection view cells into Material cards. MDC-iOS provides a card embedded in the UICollectionViewCell that can easily be integrated into your existing UI.

Convert the UICollectionViewCell to an MDCCardCollectionViewCell

Cards provide additional functionality and customization options, including support for ink, shadows, and borders. Let's take advantage of those built-in features.

In ProductCell.swift change the superclass from UICollectionViewCell to MDCCardCollectionCell.

class ProductCell: MDCCardCollectionCell

In ProductCell.swift, configure our new properties in the awakeFromNib() method.

override func awakeFromNib() {
  super.awakeFromNib()

  //TODO: Configure the cell properties
  self.backgroundColor = .white

  //TODO: Configure the MDCCardCollectionCell specific properties
  self.cornerRadius = 4.0;
  self.setBorderWidth(1.0, for:.normal)
  self.setBorderColor(.lightGray, for: .normal)
}

Build and run

The products are now be displayed within cards, which support an ink ripple animation. Try it out!

Our app flow takes the user from the login screen to a home screen, where products can be viewed. In just a few lines of code, we added a top app bar (with a title and three buttons) and displayed our app content in cards. Our home screen is now simple and functional, with a basic structure and actionable content.

Next steps

With the app bar, cards, text fields, and buttons, we've now used four Material Design core components from the MDC-iOS library. Great job! You can explore even more components by visiting the MDC Catalog for iOS.

While it's fully functioning, our app doesn't yet express any particular brand or point of view. In MDC-103: Material Design Theming with Color, Shape, Elevation and Type, we'll customize the style of these components to express a vibrant, modern brand.

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