Learn how to export multiple timelines and integrate them into a single iOS project.
If you completed the previous tutorial, you should already have a flow file to work with. Otherwise…
This is a dev-heavy tutorial, so you’ll need to break out Xcode.
The file we’re working with has 4 timelines:
The file looks like this:
To recap, we are creating an effect for a button that animates a checkbox while at the same time animating the background color of that button. Since it is a toggle button, we have animations for both components that go forward and backward – we will trigger two animations at the same time depending on the state of the buttton.
There are a lot of ways to go about structuring and building apps, components, etc. However, for this approach we’ve decided to decouple the background animation and the checkbox animation.
It’s cleaner this way.
Let’s start by exporting our 4 timelines to their own Xcode project.
With the flow file open…
File > Export > Code
or press⬆︎⌘E
You will be presented with an export window that has a few options in it…
Select the
checkMarkForward
andiOS
options
Change the location for saving by tapping the Choose button at the bottom of the panel.
Now, we want to configure the Xcode project a bit…
Click
Configure
next to the iOS option
You should now see the project configuration panel.
Change the project name to
CheckMarkForward
Press
Save
Press
Export
Now, you can open the Xcode project and run it in the iOS simulator. Once the project launches, you can tap the simulator to trigger the animation. You should see:
Follow the same steps above, except when you…
Configure the iOS project name to be
CheckMarkReverse
Follow the same steps above, except when you…
Configure the iOS project name to be
BackgroundForward
Follow the same steps above, except when you…
Configure the iOS project name to be
BackgroundReverse
When Flow exports a Timeline, it names the main class SceneView
.
In each Xcode project you just exported there will be a class called SceneView.swift
. Now, when we go to integrate all these classes into a single project, Xcode will yell at us. Refactoring the classes in each exported project will allow us to drop them into the final project without any complications.
So, let’s get to some renaming.
Open the CheckMarkForward
project and navigate to the SceneView.swift
class.
Select the name of the class, right click and choose
Refactor > Rename...
Rename
SceneView
toCheckMarkView
You should now see…
Hit
Enter
to commit the changes.
Follow the exact same steps as above…
Why that? You wonder...
The reason we want the two projects CheckMarkForward
and CheckMarkReverse
to have the same class names is because they will actually use the same class. When we integrate files we will copy in the CheckMarkView
only one time… so it should be the same for both projects.
Also, we will only bring in the timeline from the CheckMarkReverse project.
Follow the same steps as above, except…
Rename
SceneView
toBackgroundView
Follow the exact same steps you took for BackgroundForward…
You can create a new Xcode project from scratch, or use one we’ve prepped for you.
… to create the project yourself, or you have one you want to try modifying.
Single View App
UIButton
into Main.storyboard
Horizontally in Container
Vertically in Container
Now, we’re going to import a whack of stuff.
To trigger animations created in Flow, your project will require a set of common files that get generated each time you export a timeline.
First,…
In
CheckMark.xcodeproj
create a new group calledFlowCommon
Then from any of the 4 projects we exported earlier, we’re going to move…
Select all the common files and drag them into the group in
CheckMark.xcodeproj
Make sure to add all the files to the project's target
Now, we’ve got our common files ready to go. Next, we integrate the classes and timelines from the other exported projects.
From the CheckMarkForward
project…
Drag
CheckMarkView.swift
andCheckMarkForwardTimeline.swift
intoCheckMark.xcodeproj
From the CheckMarkReverse
project…
Drag
CheckMarkReverseTimeline.swift
intoCheckMark.xcodeproj
See how we didn't need to bring in the CheckMarkView.swift here? That's because it's identical to the one we already imported from the CheckMarkForward project.
From the BackgroundForward
project…
Drag
BackgroundView.swift
andBackgroundForwardTimeline.swift
intoCheckMark.xcodeproj
From the BackgroundReverse
project…
Drag
BackgroundReverseTimeline.swift
intoCheckMark.xcodeproj
To keep your project tidy, you can group the files you just imported.
Select the new files, right-click, choose
New Group From Selection
and rename the group
Don’t forget about the fonts! Your Sketch and Flow files probably used custom fonts for the button label. We’ll want to get access to those, and we’ll want to integrate them properly into our new project.
In your Flow project, create a new timeline and with the start
and end
artboards.
Export the timeline
Remember to configure the output folder name – Layout – while exporting
When Flow exports text it also packs up the fonts and bundles them properly into the Xcode project.
Drag the font file from
Layout.xcodeproj
and drop it intoCheckMark.xcodeproj
Now, the Xcode project needs to know that it’s going to use a custom font file.
Navigate to the
info.plist
file ofLayout.xcodeproj
In this file look for an entry called Fonts provided by application
Select the entry and copy it (
⌘C
)
We want to add this to our new project.
In
CheckMark.xcodeproj
navigate toinfo.plist
and paste (⌘V
) the entry you just copied
Now, we’re good to go for integrating everything into a button.
It’s high time we get to building. Our approach will be to:
Easy, right? Let’s get started
First, we want to subclass UIButton
.
Create a new file called
CheckMarkButton.swift
and give it the following class code:
1
2
3
4
5
import UIKit
class CheckMarkButton: UIButton {
}
There’s some basic setup we need to do to the button.
Create a function called
styleButton
, call itawakeFromNib
and fill it with the following code:
1
2
3
4
5
6
7
8
9
10
11
override func awakeFromNib() {
super.awakeFromNib()
styleButton()
}
func styleButton() {
titleLabel?.font = UIFont(name: "SFUIText-Medium", size: 14)
titleLabel?.textColor = UIColor(red: 0.149, green: 0.149, blue: 0.149, alpha: 1)
contentHorizontalAlignment = .left
titleEdgeInsets = UIEdgeInsetsMake(0.0, 49, 0.0, 0.0)
}
You can get the edge inset position from the Sketch file
This code does three things:
Anchors the button label to the left, then pads it to the right by 49pt
You can find the font and textColor code in the StartView file of Layout.xcodeproj
In Main.storyboard
select the button.
Change the button’s class to
CheckMarkButton
Then…
Change the button’s size to
width: 322
andheight: 42
Then…
Change the button’s type to
Custom
Finally…
Add fixed width and height contraints to the button
Now run the app in the simulator.
You many need to set the button's Text Color directly in the Main.storyboard file.
Back in the CheckMarkButton
class, it’s time to add the CheckMarkView
.
Add a variable
1
var checkMark: CheckMarkView!
Create a new method called
createCheckBox
1
2
3
4
5
func createCheckMark() {
checkMark = CheckMarkView(frame: CGRect(origin: CGPoint(x: 8, y: 8),
size: CheckMarkView.Defaults.size))
addSubview(checkMark)
}
You can get the {x,y} position from the Sketch file
Call that method in
awakeFromNib
Your whole class should now look like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class CheckMarkButton: UIButton {
var checkMark: CheckMarkView!
override func awakeFromNib() {
super.awakeFromNib()
styleButton()
createCheckMark()
}
func styleButton() {
titleLabel?.font = UIFont(name: "SFUIText-Medium", size: 14)
titleLabel?.textColor = UIColor(red: 0.149, green: 0.149, blue: 0.149, alpha: 1)
titleLabel?.textAlignment = .left
contentHorizontalAlignment = .left
titleEdgeInsets = UIEdgeInsetsMake(0.0, 49, 0.0, 0.0)
}
func createCheckMark() {
checkMark = CheckMarkView(frame: CGRect(origin: CGPoint(x: 8, y: 8),
size: CheckMarkView.Defaults.size))
addSubview(checkMark)
}
}
Getting there…
The process for adding the background to our button is identical to the previous steps, with two exceptions
First, the background should be the same size as our button, so we set its frame like so:
1
background = BackgroundView(frame: bounds)
Second, we need to send the view behind all the other views in the button with a call such as:
1
sendSubview(toBack: background)
Your code should now look like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class CheckMarkButton: UIButton {
var checkMark: CheckMarkView!
var background: BackgroundView!
override func awakeFromNib() {
super.awakeFromNib()
styleButton()
createCheckMark()
createBackground()
}
func styleButton() {
titleLabel?.font = UIFont(name: "SFUIText-Medium", size: 14)
titleLabel?.textColor = UIColor(red: 0.149, green: 0.149, blue: 0.149, alpha: 1)
titleLabel?.textAlignment = .left
contentHorizontalAlignment = .left
titleEdgeInsets = UIEdgeInsetsMake(0.0, 49, 0.0, 0.0)
}
func createCheckMark() {
checkMark = CheckMarkView(frame: CGRect(origin: CGPoint(x: 8, y: 8),
size: CheckMarkView.Defaults.size))
addSubview(checkMark)
}
func createBackground() {
background = BackgroundView(frame: bounds)
background.isUserInteractionEnabled = false
addSubview(background)
sendSubview(toBack: background)
}
}
It is important to turn off the interaction behaviour for the background.
Here comes the butter.
Add 4 new variables for the timelines like so:
1
2
3
4
var checkMarkForward: CheckMarkForwardTimeline!
var checkMarkReverse: CheckMarkReverseTimeline!
var backgroundForward: BackgroundForwardTimeline!
var backgroundReverse: BackgroundReverseTimeline!
Add a method for creating the timelines:
1
2
3
4
5
6
func createTimelines() {
checkMarkForward = CheckMarkForwardTimeline(view: checkMark)
checkMarkReverse = CheckMarkReverseTimeline(view: checkMark)
backgroundForward = BackgroundForwardTimeline(view: background)
backgroundReverse = BackgroundReverseTimeline(view: background)
}
When you initialize a Flow timeline, you pass in the view that should handle the animation.
Call that method in
awakeFromNib
We’re going to create a method that checks the button’s isSelected
property, and triggers our animations. We’ll connect this method to the button’s default behaviour by adding it as a target.
Add the following method to your class:
1
2
3
4
5
6
7
8
9
10
@objc func toggle() {
isSelected = !isSelected
if isSelected {
checkBoxForward.animate()
backgroundViewForward.animate()
} else {
checkBoxReverse.animate()
backgroundViewReverse.animate()
}
}
Add the following line to
awakeFromNib
1
addTarget(self, action: #selector(toggle), for: .touchUpInside)
We do this in code because Interface Builder doesn't let you connect the button's action to itself for some reason
Since our button is a subclass of UIButton
we can easily duplicate it in Main.storyboard
.
Copy and paste the button 5 times
Space and align all the buttons
Add constraints to preserve the positions of the buttons
Add numbers to the button titles
Run it.
Wham.
❤︎
result(s) found for “”.