Recently, an issue came up on a map clustering library that I support. Someone asked how to change the size and color of custom map markers and clusters. After some Googling, this didn’t look like an easy fix. So I gave a cop out answer and said to just use hard-coded PNGs.
The next day, I found out about the new PaintCode Sketch plugin. This was just what I needed — it’s a plugin that automatically generates Swift code for your Sketch illustrations. I tried it out in my app and was making dynamically sized and colored map markers in no time!
The PaintCode Sketch plugin is great because it helps bridge the gap between design and implementation. Designers can now deliver icons as Swift code instead of littering Slack with PNGs. As a bonus, the PaintCode plugin exports colors as UIColor
objects — no more stringly-typed hex values!
In this tutorial, you’ll get hands-on experience with the new PaintCode sketch plugin by exporting a Sketch illustration and using it in an app. You’ll learn how to dynamically size the image and modify its colors and other properties. Let’s dive in!
Getting Started
Here’s what you need to export Swift Core Graphics code from Sketch:
- A copy of Sketch.
- A copy of the PaintCode Sketch plugin.
Note you do not need a copy of PaintCode 2 to do this; the PaintCode Sketch plugin is an independent product that exports the Swift Core Graphics code for Sketch illustrations, without the need for PaintCode 2 itself.
Start by installing Sketch and the PaintCode Sketch plugin.
Then, download this Sketch starter file which you’ll use to generate Swift code. This tutorial does not require any Sketch skills, but check out this Sketch Tutorial for iOS Developers if you want to learn some basics.
Preparing the Sketch File
Open the starter Sketch file and take a look around. Inside the main canvas area, you’ll see an image of a bubble tea cup. Below the image, there’s a color palette with nine colored squares.
On the left, there’s a panel called the Layers List. Each layer has a name like liquid
, bubbles
, or Mango
. PaintCode will convert these names to variables and comments. It’s important to use meaningful names, because this makes the generated code easier to read.
Prep the icon
Before you jump into using the PaintCode plugin, you need to do a little prep work. First, you’ll nest the bubble tea cup image inside an Artboard.
- Hold down the Space key and drag the canvas to pan around until you see the image.
- Click Insert\Artboard from the main menu (or use the shortcut key A).
- Drag your mouse to create a rectangle around the image.
- In the Layers List, rename the Artboard to BubbleTeaCup.
- To center the image within the Artboard, select the blue folder icon and drag the image until it snaps to the red line guides.
By organizing layers inside an Artboard, you’re letting PaintCode know that you want to treat them as a single image. So if you have multiple icons, you should place each one inside its own Artboard.
Prep the color palette
Besides images, you can also make a color palette. Just create an Artboard named Library and drop in some shapes with different colors.
- Type the A key to use the Artboard tool.
- Drag your mouse to enclose all the colored squares.
- In the Layers List, rename the Artboard to Library.
PaintCode converts each of these shapes into an UIColor
object, using the layer name and fill color. The type of shape doesn’t matter. PaintCode ignores the border color and width as well.
- Gradient: Inside the
Fill
picker, chooseLinear Gradient
to produce aCGGradient
. - Shadow: Click the plus sign
+
next toShadows
to produce anNSShadow
. Just make sure you de-select theFill
checkbox so that PaintCode knows your intentions.
Using the Plugin to Generate Swift Code
Now that your layers are properly named and organized into Artboards, it’s time to generate some Swift code. From the Plugins menu, select PaintCode.
Sketch will drop down a plugin sheet with different options.
- PaintCode Plugin: Register a license key or purchase one on the website.
- Choose Drawings: Select which items you wish to export as code.
- Platform & Language: Choose between Swift and Objective-C.
- Settings: Add project meta data to populate the comment block at the top of the generated Swift file. Note that the
Class Name
defaults toStyleKit
. - Change Code Settings…: Configure code formatting options, like indent using 2 spaces.
- Get Code Files: Export the file to the Desktop, or drag the Swift file icon to a Finder window.
For now, make sure Library
and BubbleTeaCup
are checked; otherwise, leave the options as their defaults. Then select Export, and PaintCode will save a file named StyleKit.swift to the Desktop.
Working with the Objective-Tea Starter Project
All of the steps related to Sketch are behind you now. From here on out, it’s just Xcode, baby!
Download the starter project for this tutorial here. The zip file contains an Xcode project for a fake bubble tea app, Objective-Tea. In this tutorial, you’ll work on an order page where thirsty iOS developers can choose drinks of different flavors and sizes.
Launch the ObjectiveTea.xcodeproj
file, then build and run. You should see a two tabs:
- Order: Customize your bubble tea order.
- Stores: This is a map with store locations.
Your job for the rest of this tutorial is to liven up the app with colors and dynamic images.
StyleKit.swift
file to your Desktop.
Import the StyleKit File to Xcode
By default, PaintCode uses the name StyleKit
as the class and file name. Import this file into the ObjectiveTea Xcode project:
- Drag the
StyleKit.swift
file from the Desktop into the Project navigator, right aboveMain.Storyboard
. - Make sure that Copy items if needed and the ObjectiveTea target are selected.
- Click Finish.
You should end up with a StyleKit.swift
file in your Xcode project.
Working with Colors
At the top of the file, you’ll see a Colors section. PaintCode generates a static constant for each colored square inside the Library Artboard.
//MARK: - Colors static let plum = UIColor(hue: 0.126, saturation: 1, brightness: 0.804, alpha: 1) static let strawberry = UIColor(hue: 0.002, saturation: 0.369, brightness: 0.945, alpha: 1) ... |
Set the tab bar color
Let’s test out one of these colors on the tab bar icons. The sample project already has a TabBarController
subclass wired up in the Storyboard.
Within TabBarController.swift, add the following line inside viewDidLoad()
:
tabBar.tintColor = StyleKit.rWGreen |
Build and run. The tab bar icons should be green, just like the tea leaf on the app icon.
Set the flavor colors
All of the flavors are gray, which is not very appetizing. Next, you’re going to integrate the rest of the StyleKit colors into the app.
Within OrderViewController.swift, there’s a flavors
array that generates all the flavor objects. Each of these flavors are using gray as a placeholder color:
let milkTea = UIColor.grayColor() ... let milkTeaFlavor = Flavor(name: "Milk Tea", color: milkTea) |
Replace each flavor’s color with the StyleKit version:
let milkTeaFlavor = Flavor(name: "Milk Tea", color: StyleKit.milkTea) let coffeeFlavor = Flavor(name: "Coffee", color: StyleKit.coffee) let taroFlavor = Flavor(name: "Taro", color: StyleKit.taro) let matchaGreenTeaFlavor = Flavor(name: "Matcha", color: StyleKit.matcha) let mangoFlavor = Flavor(name: "Mango", color: StyleKit.mango) let strawberryFlavor = Flavor(name: "Strawberry", color: StyleKit.strawberry) let plumFlavor = Flavor(name: "Plum", color: StyleKit.plum) let yogurtFlavor = Flavor(name: "Yogurt", color: StyleKit.yogurt) |
Build and run. Each flavor should now have a pretty color.
Canvas Drawings
The next section of the StyleKit file includes canvas drawing methods. This section does all the heavy lifting of drawing vector paths. The code also looks pretty intimidating. Just be glad you didn’t have to write it by hand =].
Open StyleKit.swift and find drawBubbleTeaCup(frame:resizing:)
. You’ll see it looks similar to the following:
//MARK: - Canvas Drawings /// ObjectiveTea class func drawBubbleTeaCup(frame frame: CGRect = CGRect(x: 0, y: 0, width: 161, height: 221), resizing: ResizingBehavior = .AspectFit) { /// BubbleTeaCup do { /// Liquid let liquid = UIBezierPath() ... /// Straw /// Cup /// Bubbles /// Smile } } |
PaintCode adds comments based on the layer names from the Sketch file to help make the code a little more readable.
The drawing method takes two parameters:
- frame: You can set the image size by passing in a
CGRect
. Otherwise, it defaults to the dimensions from the Sketch file. - resizing: You can pass in a content mode such as
Aspect Fit
,Aspect Fill
,Stretch
, orCenter
. PaintCode provides a helperenum
at the bottom of the file to produce these effects.
Create a custom view
The canvas drawing methods are designed to work with custom views.
Open up BubbleTeaCup.swift. This is an empty UIView
subclass that’s already wired up in the Storyboard:
import UIKit class BubbleTeaCup: UIView { } |
Replace the file’s contents with the following code:
import UIKit @IBDesignable // 1 class BubbleTeaCup: UIView { override func drawRect(rect: CGRect) { StyleKit.drawBubbleTeaCup(frame: bounds) // 2 } } |
- IBDesignable: Gives you an image preview in the Storyboard.
- drawBubbleTeaCup(frame:): Draws the vector image into the current graphics context. You pass in the
bounds
so the image takes up the entire view.
UIView
has a method called drawRect(_:)
which provides a graphics context (which I like to think of as a piece of scrap paper). The drawBubbleTeaCup(frame:)
method takes care of drawing the image on this piece of paper, which then ends up on the screen.
Open Main.storyboard and look for the OrderViewController scene. You should see a preview of the bubble tea cup, thanks to @IBDesignable
.
Build and run. The bubble tea cup image should now appear on the order tab.
Canvas Images
Switch back to StyleKit.swift and find the imageOfBubbleTeaCup(frame:resizing:)
method. You’ll see this converts the drawing method to its UIImage
equivalent:
//MARK: - Canvas Images /// ObjectiveTea class func imageOfBubbleTeaCup(size size: CGSize = CGSize(width: 161, height: 221), resizing: ResizingBehavior = .AspectFit) -> UIImage { var image: UIImage UIGraphicsBeginImageContextWithOptions(size, false, 0) StyleKit.drawBubbleTeaCup(frame: CGRect(origin: CGPoint.zero, size: size), resizing: resizing) image = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return image } |
Each canvas image method takes two parameters:
- size: This takes a
CGSize
. Otherwise, it defaults to the dimensions taken from Sketch. - resizing: The content mode is forwarded to the drawing method.
This method can be useful in situations where you just want a UIImage
of the drawing at a particular size.
Set the map marker icon
Let’s test out the canvas image method on the Stores tab.
Open StoresViewController.swift and scroll to the bottom. The map uses a hard-coded PNG for the map marker:
pinView?.image = UIImage(named: reuseId) |
Replace this line with the following:
pinView?.image = StyleKit.imageOfBubbleTeaCup(size: CGSize(width: 32, height: 32)) |
Here you set the MKAnnotationView
image to a 32×32 icon of the bubble tea cup.
Build and run. You should now see little bubble tea cups at each store location.
Set the tab bar icon
You can also use the canvas image methods to set tab bar icons.
Open TabBarController.swift. The viewDidLoad()
method looks like this:
override func viewDidLoad() { tabBar.tintColor = StyleKit.rWGreen } |
Append the following code, right after the tintColor
line.
let tabBarIconSize = CGSize(width: 32, height: 32) // 1 if let firstTabItem = tabBar.items?.first { // 2 let bubbleTeaCupImage = StyleKit.imageOfBubbleTeaCup(size: tabBarIconSize) // 3 firstTabItem.image = bubbleTeaCupImage // 4 firstTabItem.selectedImage = bubbleTeaCupImage // 5 } |
Let’s go over this line by line:
- Creates a
CGSize
of 32×32, which seems like a good size for this particular icon. - Gets a handle to the first tab bar item.
- Generates an
UIImage
of the bubble tea cup. - Sets the tab bar item image.
- Sets the selected image.
Build and run. You should now see the bubble tea cup icon in the tab bar. Since tab bar items use template images, you won’t see the individual bubbles.
Using different image sizes
The app has an order tab where you can customize your beverage. Although there’s a cute cup of bubble tea smiling back at you, it doesn’t do anything when you toggle the options on the form yet.
Draw the image based on beverage size
The form has a segmented control which lets you choose between Regular or Large. Your job is to make the preview icon change size based on the selection.
Open BubbleTeaCup.swift, and replace its contents with the following code:
import UIKit // 1 enum BeverageSize { case Regular, Large } @IBDesignable class BubbleTeaCup: UIView { // 2 var beverageSize: BeverageSize = .Regular { didSet { setNeedsDisplay() } } override func drawRect(rect: CGRect) { // 3 let imageSize: CGRect switch beverageSize { case .Regular: imageSize = bounds.insetBy(dx: bounds.width * 0.1, dy: bounds.height * 0.1) // 4 case .Large: imageSize = bounds // 5 } // 6 StyleKit.drawBubbleTeaCup(frame: imageSize) } } |
Let’s review this section by section:
- Creates an
enum
with beverage size options. This goes outside the class body so the rest of the app has access to it. - Stores the state of the beverage size, which you’ll user later when calculating the image size. It also triggers a re-draw whenever this value changes.
- Calculates the image size based on the beverage size.
- Regular has a slightly smaller width and height.
- Large takes up the full view.
- Draws the image based on the
imageSize
dimensions.
When you set the beverageSize
property, the view refreshes its display to reflect the new beverage size.
Wire up the segmented control
For this to actually work, you need to wire up the order form’s segmented control.
Open OrderViewController.swift and replace the sizeChange
@IBAction method with the following:
@IBAction func sizeChange(sender: UISegmentedControl) { switch sender.selectedSegmentIndex { case 0: bubbleTeaCup.beverageSize = .Regular case 1: bubbleTeaCup.beverageSize = .Large default: break } } |
This sets the beverageSize
property of the custom view based on the selectedSegmentIndex
.
Build and run. Now when you toggle between Regular and Large, the order preview updates accordingly.
Changing the tea color at runtime
PaintCode really shines when you have to change individual image layers at runtime. This would be a nightmare using PNGs.
In this section, you’re going to change the color of the tea based on flavor selection.
Pass in a parameter to change the color
By default, the PaintCode drawing methods expose parameters for size and stretch. In order to support color changes, you’ll need to add your own parameter.
Open StyleKit.swift and replace the following line of code:
class func drawBubbleTeaCup(frame frame: CGRect = CGRect(x: 0, y: 0, width: 161, height: 221), resizing: ResizingBehavior = .AspectFit) { |
With the following:
class func drawBubbleTeaCup(frame frame: CGRect = CGRect(x: 0, y: 0, width: 161, height: 221), teaColor: UIColor = milkTea, resizing: ResizingBehavior = .AspectFit) { |
You’re squeezing in an additional parameter teaColor
into the method signature. You also have it default to milkTea
so it doesn’t break existing code.
Next, scroll to around line 64 where the liquid
layer color fill happens:
... UIColor(hue: 0.078, saturation: 0.447, brightness: 0.843, alpha: 1).setFill() // change this liquid.fill() ... |
Replace the code so it looks like this:
... teaColor.setFill() // now uses the parameter liquid.fill() ... |
Instead of filling the liquid with a hard-coded UIColor
, you use the teaColor
parameter instead.
StyleKit
file from Sketch, remember that your custom changes will be wiped out. If controlling layer behavior with custom parameters is something you’ll be doing often, consider using the full featured PaintCode 2 Mac app which has a neat Variables feature.
Wire up the custom view
The next step is to wire up the BubbleTeaCup class to use this new parameter.
Open BubbleTeaCup.swift, and add the following property above drawRect(_:)
:
var teaColor = StyleKit.milkTea { didSet { setNeedsDisplay() } } |
The teaColor
property keeps track of the liquid
color, and re-draws the view when it changes.
Within the same file, update the following line of code:
StyleKit.drawBubbleTeaCup(frame: imageSize) |
To this:
StyleKit.drawBubbleTeaCup(frame: imageSize, teaColor: teaColor) |
The canvas drawing method supports a new parameter, and you pass it the current teaColor
.
Wire up the collection view
All of the bubble tea flavors are in a collection view at the bottom of the order tab. When a user taps a cell, you want the order preview image to change color too.
Open OrderViewController.swift and append the following line to the end of collectionView(_:didSelectItemAtIndexPath:indexPath:)
:
bubbleTeaCup.teaColor = selectedFlavor.color |
selectedFlavor
represents the Flavor
object that the user tapped (i.e. Mango). Here you set the teaColor
to the flavor’s color.
Build and run. Now when you choose different flavors, the order preview image updates as well.
Hold the bubbles
Bubble tea sure has a lot of carbs! The drinks are sugary, and the bubbles are made of starchy tapioca balls. It’d be nice if there was a no-bubble option.
Pass in a parameter to change the color
In this section, you’re going to parameterize the image again. But this time, you’ll pass a flag to indicate whether or not to draw the bubbles.
Open StyleKit.swift and replace the following line of code:
class func drawBubbleTeaCup(frame frame: CGRect = CGRect(x: 0, y: 0, width: 161, height: 221), teaColor: UIColor = milkTea, resizing: ResizingBehavior = .AspectFit) { |
With the following:
class func drawBubbleTeaCup(frame frame: CGRect = CGRect(x: 0, y: 0, width: 161, height: 221), teaColor: UIColor = milkTea, addBubbles: Bool = true, resizing: ResizingBehavior = .AspectFit) { |
You’re inserting yet another parameter addBubbles
with a default value of true
.
Next, scroll to around line 157 toward the end of the bubble-related code. Replace the following line:
bubbles.fill() |
With this:
if addBubbles { bubbles.fill() } |
You only draw the bubbles if the addBubbles
parameter is true
.
Wire up the custom view
Like before, the next step is to wire up the BubbleTeaCup class to use this new parameter.
Open BubbleTeaCup.swift, and add the following property below teaColor
:
var bubbles = true { didSet { setNeedsDisplay() } } |
This is yet another property that triggers a re-draw when its value changes.
Within the same file, update the following line of code:
StyleKit.drawBubbleTeaCup(frame: imageSize, teaColor: teaColor) |
To this:
StyleKit.drawBubbleTeaCup(frame: imageSize, teaColor: teaColor, addBubbles: bubbles) |
Here, you pass the bubbles
flag to the canvas drawing method.
Wire up the switch
All that’s left is to wire up the UISwitch
in the order form.
Open OrderViewController.swift and replace the toggleBubbles
@IBAction method with the following:
@IBAction func toggleBubbles(sender: UISwitch) { bubbleTeaCup.bubbles = sender.on } |
This updates the bubbles
flag whenever the UISwitch
is toggled.
Build and run. Now when you toggle the switch, the bubbles in the order preview image should disappear.
Pretty cool! Play around a bit and think back on all you’ve accomplished:
- You’ve taken a cool illustration designed in Sketch…
- …used the new PaintCode Sketch plugin to auto-generate Core Graphics code for that illustration…
- …which results in smaller binary size, scalability, and the ability to dynamically customize the image at runtime!
Where To Go From Here?
You can download the final project which includes the Xcode project and Sketch file here.
To learn more about the PaintCode plugin, check out the tutorials on the official site.
If you need to dig deeper into Core Graphics, this tutorial is a good place to start. And if you don’t use Sketch but still like what the PaintCode plugin can do, consider whether PaintCode 2 is right for you.
We hope you enjoyed this tutorial, and if you have any questions or comments, please join the discussion below!
The post PaintCode Sketch Plugin Tutorial appeared first on Ray Wenderlich.