Core Graphics Tutorial: Lines, Rectangles, and Gradients
Learn how to use Core Graphics to draw lines, rectangles, and gradients — starting by beautifying a table view! By Ron Kliffer.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Core Graphics Tutorial: Lines, Rectangles, and Gradients
30 mins
- Getting Started
- Analyzing the Table View Style
- Hello, Core Graphics!
- Drawing Rectangles
- Showing Your New Cell
- Creating New Colors
- Drawing Gradients
- The Graphics State Stack
- Completing the Gradient
- Fixing the Theme
- Stroking Paths
- Outside the Bounds
- Anti-Aliasing
- Adding a Border
- Building a Card Layout
- Drawing Lines
- Where to Go From Here?
Fixing the Theme
Open StarshipsViewController.swift. At the end of viewDidLoad()
, add the following:
tableView.separatorStyle = .none
tableView.backgroundColor = .starwarsSpaceBlue
Then, in tableView(_:cellForRowAt:)
, just before returning the cell, set the color of the text:
cell.textLabel?.textColor = .starwarsStarshipGrey
This removes the cell separators and gives the table some nice starship colors.
Next, open AppDelegate.swift and, in application(_:didFinishLaunchingWithOptions:)
, add the following just before returning:
// Theming
let barAppearance = UINavigationBarAppearance()
barAppearance.configureWithOpaqueBackground()
barAppearance.backgroundColor = .starwarsSpaceBlue
barAppearance.titleTextAttributes = [
.foregroundColor: UIColor.starwarsStarshipGrey
]
UINavigationBar.appearance().tintColor = .starwarsYellow
UINavigationBar.appearance().barStyle = .black
UINavigationBar.appearance().standardAppearance = barAppearance
UINavigationBar.appearance().scrollEdgeAppearance = barAppearance
This sets the navigation bar’s appearance to match the table’s, using the UINavigationBarAppearance
class introduced in iOS 13.
Build and run the app.
That’s better! Your starship’s table view is starting to look space age. :]
Stroking Paths
Stroking in Core Graphics means drawing a line along a path, rather than filling it, as you did before.
When Core Graphics strokes a path, it draws the stroke line on the middle of the exact edge of the path. This can cause a couple of common problems.
Outside the Bounds
First, if you’re drawing around the edge of a rectangle — a border, for example — Core Graphics won’t draw half the stroke path by default.
Why? Because the context set up for a UIView
extends only to the bounds of the view. Imagine stroking with a one-point border around the edge of a view. Because Core Graphics strokes down the middle of the path, the line will be half a point outside the bounds of the view and half a point inside the bounds of the view.
A common solution is to inset the path for the stroke rect half the width of the line in each direction, so it sits inside the view.
The diagram below shows a yellow rectangle with a red stroke one point wide on a gray background, striped at one-point intervals. In the left diagram, the stroke path follows the bounds of the view and has been cropped. You can see this because the red line is half the width of the gray squares. On the right diagram, the stroke path has been inset half a point and now has the correct line width.
Anti-Aliasing
Second, you need to be aware of anti-aliasing effects that can affect the appearance of your border. Anti-aliasing is a technique rendering engines use to avoid the appearance of “jagged” edges and lines when displayed graphics don’t map perfectly to physical pixels on a device.
Take the example of a one-point border around a view from the previous paragraph. If the border follows the bounds of the view, then Core Graphics will attempt to draw a line half a point wide on either side of the rectangle.
On a non-Retina display, one point is equal to one pixel on the device. It’s not possible to light up just half of a pixel, so Core Graphics will use anti-aliasing to draw in both pixels, but in a lighter shade to give the appearance of only a single pixel.
In the following sets of screenshots, the left image is a non-Retina display, the middle image is a Retina display with a scale of two and the third image is a Retina display with a scale of three.
For the first diagram, notice how the 2x image doesn’t show any anti-aliasing, as the half point on either side of the yellow rectangle falls on a pixel boundary. However, in the 1x and 3x images, anti-aliasing occurs.
In this next set of screenshots, the stroke rect has been inset half a point. Thus, the stroke line aligns exactly with point, and thus pixel, boundaries. Notice how there are no aliasing artifacts.
Adding a Border
Back to your app! The cells are starting to look good, but you’re going to add another touch to make them stand out. This time, you’ll draw a bright yellow frame around the edges of the cell.
You already know how to easily fill rectangles. Well, stroking around them is just as easy.
Open StarshipListCellBackground.swift and add the following to the bottom of draw(_:)
:
let strokeRect = bounds.insetBy(dx: 4.5, dy: 4.5)
context.setStrokeColor(UIColor.starwarsYellow.cgColor)
context.setLineWidth(1)
context.stroke(strokeRect)
Here, you create a rectangle for stroking that’s inset from the background rectangle by 4.5 points in both the x and y directions. Then, you set the stroke color to yellow, the line width to one point and, finally, stroke the rectangle. Build and run your project.
Now, your starship list looks like it comes from a galaxy far, far away!
Building a Card Layout
Although StarshipsViewController
is looking fancy, StarshipDetailViewController
still needs some sprucing up!
For this view, you’ll start by drawing a gradient on the table view background, using a custom UITableView
subclass.
Create a new Cocoa Touch Class file, make it a subclass of UITableView
and call it StarshipTableView. Add the following to the new class:
override func draw(_ rect: CGRect) {
guard let context = UIGraphicsGetCurrentContext() else {
return
}
context.drawLinearGradient(
in: bounds,
startingWith: UIColor.starwarsSpaceBlue.cgColor,
finishingWith: UIColor.black.cgColor)
}
This should look familiar by now. In the draw(_:)
method of your new table view subclass, you get the current CGContext
, then draw a gradient in the bounds of the view, starting from blue at the top and heading into black at the bottom. Simple!
Open Main.storyboard and click the TableView in the Starship Detail View Controller scene. In the Identity inspector, set the class to your new StarshipTableView.
Build and run the app, then tap the Y-wing row.
Your detail view now has a nice full-screen gradient running from top to bottom, but the cells in the table view obscure the best parts of the effect. It’s time to fix this and add a bit more flair to the detail cells.
Open StarshipDetailViewController.swift and, at the bottom of tableView(_:cellForRowAt:)
, just before returning the cell for item field
, add the following:
cell.textLabel?.textColor = .starwarsStarshipGrey
cell.detailTextLabel?.textColor = .starwarsYellow
cell.backgroundColor = .clear
This simply sets the cell’s field name and value to more appropriate colors for your Stars Wars theme and sets the background color to clear.
Then, after tableView(_:cellForRowAt:)
, add the following method to style the table view header:
override func tableView(
_ tableView: UITableView,
willDisplayHeaderView view: UIView,
forSection section: Int
) {
view.tintColor = .starwarsYellow
if let header = view as? UITableViewHeaderFooterView {
header.textLabel?.textColor = .starwarsSpaceBlue
}
}
Here, you set the tint color of the table views’ header’s view to the theme yellow, giving it a yellow background, and its text color to the theme blue.