Core Graphics Tutorial: Shadows and Gloss

Make your UI elements stand out by applying shadows and gloss. Shadows create a feeling of depth, while gloss makes your elements shine. By Ehab Amer.

Leave a rating/review
Download materials
Save for later
Share
Update note: Ehab Amer updated this tutorial for iOS 12, Xcode 10 and Swift 4.2. Ray Wenderlich wrote the original.

Until iOS 6, gloss effects were common across iOS, from buttons and bars to almost any element in the UIKit. With iOS 7, Apple changed its design approach to a flatter interface. That doesn’t mean that it’s wrong or outdated to use gloss effects! It’s still important to know how to create them.

Core Graphics makes it easy.

Getting Started

For this tutorial, you’ll work on a project called Cool Table. The project itself covers many topics about Core Graphics, but for this tutorial, you’ll focus on how to create a shadow and a simple gloss effect on views.

To start, click Download Materials at the top or bottom of this tutorial. Open the starter project in Xcode and run it.

bag of balls

You’ll see a grouped table consisting of two sections, each with a title and three rows. All the work you’re going to do here will be in the title view of the sections, so there’s no need to worry about the rows.

The Drawing Canvas

Right now, the table presents the section title through the tableView(_:titleForHeaderInSection:), which doesn’t allow for much customization in the header. To be able to customize it, you want to set up the header with tableView(_:viewForHeaderInSection:).

It’s worth knowing that there are a couple of ways to create a view for the header:

  • You could create the custom view using code only.
  • You could create the custom view using Interface Builder.

Both are good options, but here, you’ll take the second approach. You’ll create and customize the view in Interface Builder and supply it as the header view.

Creating the Files

In the Project navigator, create a new file using the Cocoa Touch Class template. Name the class CustomHeader. Make sure it’s a subclass of UIView and the language is Swift.

Once you have created the empty UIView subclass, create a .xib file named CustomHeader. This time, instead of choosing Cocoa Touch Class in the template selection, choose View in the User Interface group.

In the new XIB file, you’ll find one view. In the Identity inspector, change its class from UIView to CustomHeader, which is the class you just created.

Now you need the header to show the section name. Follow these three steps:

  1. Open the Library from the View menu and drag a label onto the view inside the XIB file.
  2. Create all four constraints for the label with 4 point spacing from the left and right, a distance of 0 from the top and 10 from the bottom.
  3. In the Attributes inspector, set its text alignment to center.
  4. Select the CustomHeader view again. In the Attributes inspector, change its Size in the Simulated Metrics group from Inferred to Freeform.
  5. In the Size inspector, set the height of the view to 50.

Next, in CustomHeader.swift, add this line for the UILabel outlet. Make sure to connect it in the XIB file.

@IBOutlet public var titleLabel: UILabel!

Loading the View

The next step is to load the view from the interface file and give it to the table view. To accomplish this, add the following method right after the outlet:

class func loadViewFromNib() -> CustomHeader? {
  let bundle = Bundle.main
  let nib = UINib(nibName: "CustomHeader", bundle: bundle)
  guard 
    let view = nib.instantiate(withOwner: CustomHeader())
      .first as? CustomHeader
    else {
      return nil
    }
  return view
}

loadViewFromNib() is a class method that creates and returns CustomHeader for you. Using a standard initializer won’t do the trick here.

Next, in CoolTableViewController.swift, add this method at the end of the class:

override func tableView(
  _ tableView: UITableView,
  viewForHeaderInSection section: Int
  ) -> UIView? {
  guard let customHeaderView = CustomHeader.loadViewFromNib()
    else { return nil }
  customHeaderView.titleLabel.text = self.tableView(
    tableView,
    titleForHeaderInSection: section)
  
  return customHeaderView
}

The code loads the view as explained in the previous step, sets the text of the label, and returns the new view.

Build and run. You’ll see the new header you just created with a white background.

Drawing the Masterpiece

Now that you have a canvas for your header, you’re ready for the fun part. First, consider what you need in the masterpiece you’ll draw.

The header is split into two areas. In the image above, there are three points to pay attention to.

  • A gradient with a gloss on it.
  • A small shadow right under the colored area.
  • A stroke line around the header.

The area of the shadow is 10 points. This is why the bottom constraint under the label is 10. You know the height of the full header is 50. Therefore, the colored area is 40.

Preparing the Header

Why not define those regions visually by giving each a different color? You’ll give the gradient area a red background and make the shadow area green.

In CustomHeader.swift add this line right after the title label declaration:

@IBInspectable var coloredBoxHeight: CGFloat = 40

An @IBInspectable value lets you do as much UI customization as possible directly from Interface Builder.

It’s a good practice to have any numbers declared as constants or properties instead of having many numbers scattered around in your code. Most of the time, you’ll forget the specific number you used when you look at your own code after a couple of days.

Add this method at the end of CustomHeader:

override func draw(_ rect: CGRect) {
    // 1:
    var coloredBoxRect = bounds
    coloredBoxRect.size.height = coloredBoxHeight
    
    var paperRect = bounds
    paperRect.origin.y += coloredBoxHeight
    paperRect.size.height = bounds.height - coloredBoxHeight
    
    // 2:
    let context = UIGraphicsGetCurrentContext()!
    
    context.setFillColor(UIColor.red.cgColor)
    context.fill(coloredBoxRect)
    
    context.setFillColor(UIColor.green.cgColor)
    context.fill(paperRect)
  }

draw(_:) in UIView is where you place any custom drawing code that you want to use to change the look of your custom view. The default draw method does nothing. So, make sure you are overriding the original. There are two steps happening here:

  1. Calculate the two rectangles that you’ll color. The first is the area that will have the gradient. The second is the shadowed area. Both are calculated based on coloredBoxHeight that you defined earlier.
  2. Get the current Core Graphics context and draw the two colored rectangles.

Now, change the text color of the title label in CustomHeader.xib to white directly from the Attributes inspector.

Build and run. You should see your colored headers.