[ADD] splitflap:fontForFlapAtIndex: method to customize the flap fonts

This commit is contained in:
Yannick Loriot 2015-11-11 18:07:07 +01:00
parent cbf2dcf95f
commit 4551eb28d4
8 changed files with 118 additions and 45 deletions

View File

@ -1,11 +1,15 @@
# Change log
## Version 0.2.0 (Under Developement)
- [ADD] `splitflap:fontForFlapAtIndex:` method to customize the flap fonts
## [Version 0.1.0](https://github.com/yannickl/Splitflap/releases/tag/0.1.0)
Released on 2015-11-11.
- Supports "character" tokens
- Can configure the number of flaps
- Can configure the spacing between flaps
- Can configure the rotation animation duration for each flaps
- `flapSpacing` property to configure the spacing between flaps
- `supportedTokensInSplitflap:` method to define the "characters" used by flaps
- `numberOfFlapsInSplitflap:` to set the number of flaps
- `splitflap(splitflap:rotationDurationForFlapAtIndex:` method to change the rotation duration of each flaps
- Cocoapods support
- Carthage support

View File

@ -20,7 +20,7 @@
CE525D971BF3833F00429200 /* SplitflapDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE525D791BF331D100429200 /* SplitflapDataSource.swift */; };
CE525D981BF3833F00429200 /* SplitflapDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE525D7B1BF336EA00429200 /* SplitflapDelegate.swift */; };
CE525D991BF3833F00429200 /* SplitflapTokens.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE525D7D1BF340BB00429200 /* SplitflapTokens.swift */; };
CE525D9A1BF3833F00429200 /* Tile.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEB139B91BF266FC00DE6BA9 /* Tile.swift */; };
CE525D9A1BF3833F00429200 /* TileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEB139B91BF266FC00DE6BA9 /* TileView.swift */; };
CE525D9B1BF3833F00429200 /* TokenGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE525D7F1BF3425C00429200 /* TokenGenerator.swift */; };
CE525D9C1BF3833F00429200 /* TokenParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE525D811BF3467D00429200 /* TokenParser.swift */; };
CEB139A41BF266DD00DE6BA9 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEB139A31BF266DD00DE6BA9 /* AppDelegate.swift */; };
@ -29,7 +29,7 @@
CEB139AB1BF266DD00DE6BA9 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = CEB139AA1BF266DD00DE6BA9 /* Assets.xcassets */; };
CEB139AE1BF266DD00DE6BA9 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = CEB139AC1BF266DD00DE6BA9 /* LaunchScreen.storyboard */; };
CEB139BD1BF266FC00DE6BA9 /* Splitflap.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEB139B81BF266FC00DE6BA9 /* Splitflap.swift */; };
CEB139BE1BF266FC00DE6BA9 /* Tile.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEB139B91BF266FC00DE6BA9 /* Tile.swift */; };
CEB139BE1BF266FC00DE6BA9 /* TileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEB139B91BF266FC00DE6BA9 /* TileView.swift */; };
CEB139BF1BF266FC00DE6BA9 /* FlapView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEB139BA1BF266FC00DE6BA9 /* FlapView.swift */; };
/* End PBXBuildFile section */
@ -74,7 +74,7 @@
CEB139AD1BF266DD00DE6BA9 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
CEB139AF1BF266DD00DE6BA9 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
CEB139B81BF266FC00DE6BA9 /* Splitflap.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Splitflap.swift; sourceTree = "<group>"; };
CEB139B91BF266FC00DE6BA9 /* Tile.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Tile.swift; sourceTree = "<group>"; };
CEB139B91BF266FC00DE6BA9 /* TileView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TileView.swift; sourceTree = "<group>"; };
CEB139BA1BF266FC00DE6BA9 /* FlapView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FlapView.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
@ -146,7 +146,7 @@
CE525D791BF331D100429200 /* SplitflapDataSource.swift */,
CE525D7B1BF336EA00429200 /* SplitflapDelegate.swift */,
CE525D7D1BF340BB00429200 /* SplitflapTokens.swift */,
CEB139B91BF266FC00DE6BA9 /* Tile.swift */,
CEB139B91BF266FC00DE6BA9 /* TileView.swift */,
CE525D7F1BF3425C00429200 /* TokenGenerator.swift */,
CE525D811BF3467D00429200 /* TokenParser.swift */,
);
@ -271,7 +271,7 @@
CE525D991BF3833F00429200 /* SplitflapTokens.swift in Sources */,
CE525D9C1BF3833F00429200 /* TokenParser.swift in Sources */,
CE525D951BF3833F00429200 /* FlapView.swift in Sources */,
CE525D9A1BF3833F00429200 /* Tile.swift in Sources */,
CE525D9A1BF3833F00429200 /* TileView.swift in Sources */,
CE525D961BF3833F00429200 /* Splitflap.swift in Sources */,
CE525D9B1BF3833F00429200 /* TokenGenerator.swift in Sources */,
CE525D971BF3833F00429200 /* SplitflapDataSource.swift in Sources */,
@ -287,7 +287,7 @@
CE525D7C1BF336EA00429200 /* SplitflapDelegate.swift in Sources */,
CEB139A61BF266DD00DE6BA9 /* ViewController.swift in Sources */,
CEB139A41BF266DD00DE6BA9 /* AppDelegate.swift in Sources */,
CEB139BE1BF266FC00DE6BA9 /* Tile.swift in Sources */,
CEB139BE1BF266FC00DE6BA9 /* TileView.swift in Sources */,
CE525D821BF3467D00429200 /* TokenParser.swift in Sources */,
CE525D7A1BF331D100429200 /* SplitflapDataSource.swift in Sources */,
CE525D801BF3425C00429200 /* TokenGenerator.swift in Sources */,

View File

@ -26,7 +26,7 @@ class ViewController: UIViewController, SplitflapDataSource, SplitflapDelegate {
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
//updateSplitFlapAction(actionButton)
updateSplitFlapAction(actionButton)
}
// MARK: - Action Methods
@ -58,5 +58,9 @@ class ViewController: UIViewController, SplitflapDataSource, SplitflapDelegate {
func splitflap(splitflap: Splitflap, rotationDurationForFlapAtIndex index: Int) -> Double {
return 0.2
}
func splitflap(splitflap: Splitflap, fontForFlapAtIndex index: Int) -> UIFont? {
return UIFont(name: "Courier", size: 50)
}
}

View File

@ -15,7 +15,7 @@ The first example is the simplest way to use the `Splitflap` component. Here how
```swift
import Splitflap
let splitflapView = Splitflap(frame: CGRectMake(0, 0, 200, 80))
let splitflapView = Splitflap(frame: CGRectMake(0, 0, 370, 53))
splitflapView.datasource = self
// Set the text to display by animating the flaps

View File

@ -32,17 +32,13 @@ import QuartzCore
desired character or graphic.
*/
final class FlapView: UIView {
private var topTicTile = Tile(position: .Top)
private var bottomTicTile = Tile(position: .Bottom)
private var topTacTile = Tile(position: .Top)
private var bottomTacTile = Tile(position: .Bottom)
// The tiles used to display and animate the flaps
private var topTicTile = TileView(position: .Top)
private var bottomTicTile = TileView(position: .Bottom)
private var topTacTile = TileView(position: .Top)
private var bottomTacTile = TileView(position: .Bottom)
private enum AnimationTime {
case Tic
case Tac
}
private var animationTime = AnimationTime.Tac
// MARK: - Working With Tokens
var tokens: [String] = [] {
didSet {
@ -52,6 +48,20 @@ final class FlapView: UIView {
private var tokenGenerator = TokenGenerator(tokens: [])
private var targetToken: String?
// MARK: - Configuring the Label of Tiles
/// The font of the flap's text.
var font: UIFont? {
didSet {
topTicTile.font = font
bottomTicTile.font = font
topTacTile.font = font
bottomTacTile.font = font
}
}
// MARK: - Initializing a Flap View
override init(frame: CGRect) {
super.init(frame: frame)
@ -66,6 +76,18 @@ final class FlapView: UIView {
setupAnimations()
}
override func layoutSubviews() {
super.layoutSubviews()
let topLeafFrame = CGRectMake(0, 0, bounds.width, bounds.height / 2)
let bottomLeafFrame = CGRectMake(0, bounds.height / 2, bounds.width, bounds.height / 2)
topTicTile.frame = topLeafFrame
bottomTicTile.frame = bottomLeafFrame
topTacTile.frame = topLeafFrame
bottomTacTile.frame = bottomLeafFrame
}
// MARK: - Initializing the Flap View
private func setupViews() {
@ -84,11 +106,20 @@ final class FlapView: UIView {
// MARK: - Settings the Animations
private let topAnim = CABasicAnimation(keyPath: "transform")
private let bottomAnim = CABasicAnimation(keyPath: "transform")
/// Defines the current time of the animation to know which tile to display.
private enum AnimationTime {
/// Tic time.
case Tic
/// Tac time.
case Tac
}
private var animationTime = AnimationTime.Tac
private let topAnim = CABasicAnimation(keyPath: "transform")
private let bottomAnim = CABasicAnimation(keyPath: "transform")
private func setupAnimations() {
// Set perspective
// Set the perspective
let zDepth: CGFloat = 1000
var skewedIdentityTransform = CATransform3DIdentity
skewedIdentityTransform.m34 = 1 / -zDepth
@ -108,18 +139,14 @@ final class FlapView: UIView {
bottomAnim.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut)
}
override func layoutSubviews() {
super.layoutSubviews()
let topLeafFrame = CGRectMake(0, 0, bounds.width, bounds.height / 2)
let bottomLeafFrame = CGRectMake(0, bounds.height / 2, bounds.width, bounds.height / 2)
topTicTile.frame = topLeafFrame
bottomTicTile.frame = bottomLeafFrame
topTacTile.frame = topLeafFrame
bottomTacTile.frame = bottomLeafFrame
}
// MARK: - Animating the Flap View
/**
Display the given token.
- parameter token: A token string.
- parameter rotationDuration: If upper than 0, it animates the change.
*/
func displayToken(token: String?, rotationDuration: Double) {
let sanitizedToken = token ?? tokenGenerator.firstToken
@ -138,6 +165,10 @@ final class FlapView: UIView {
}
}
/**
Method used in conjunction with the `animationDidStop:finished:` callback in
order to display all the tokens between the current one and the target one.
*/
private func displayNextToken() {
guard tokenGenerator.currentElement != targetToken else {
return
@ -148,11 +179,11 @@ final class FlapView: UIView {
}
}
/// Display the given token. If animated it rotate the flaps.
private func updateWithToken(token: String, animated: Bool) {
let topBack = animationTime == .Tic ? topTicTile : topTacTile
let bottomBack = animationTime == .Tic ? bottomTicTile : bottomTacTile
let topFront = animationTime == .Tic ? topTacTile : topTicTile
let bottomFront = animationTime == .Tic ? bottomTacTile : bottomTicTile
let topBack = animationTime == .Tic ? topTicTile : topTacTile
let bottomBack = animationTime == .Tic ? bottomTicTile : bottomTacTile
let topFront = animationTime == .Tic ? topTacTile : topTicTile
topBack.symbol = token
bottomBack.symbol = token
@ -160,7 +191,6 @@ final class FlapView: UIView {
topBack.layer.removeAllAnimations()
bottomBack.layer.removeAllAnimations()
topFront.layer.removeAllAnimations()
bottomFront.layer.removeAllAnimations()
if animated {
bringSubviewToFront(topFront)

View File

@ -175,11 +175,14 @@ import UIKit
/// Rebuild and layout the split-flap view.
private func updateAndLayoutView() {
let targetDelegate = (delgate ?? self)
var tmp: [FlapView] = []
for _ in 0 ..< numberOfFlaps {
for index in 0 ..< numberOfFlaps {
let flap = FlapView()
flap.tokens = tokens
flap.font = targetDelegate.splitflap(self, fontForFlapAtIndex: index)
tmp.append(flap)
addSubview(flap)
@ -210,6 +213,7 @@ import UIKit
/// Default implementation of SplitflapDataSource
extension Splitflap: SplitflapDataSource {
/// By default the Splitflap object does not have flaps, so returns 0.
public func numberOfFlapsInSplitflap(splitflap: Splitflap) -> Int {
return 0
}

View File

@ -45,12 +45,30 @@ public protocol SplitflapDelegate: class {
- returns: The duration of the flap rotation in seconds.
*/
func splitflap(splitflap: Splitflap, rotationDurationForFlapAtIndex index: Int) -> Double
// MARK: - Configuring the Label of Flaps
/**
Called by the split-flap when it needs to create its flap subviews.
- parameter splitflap: The split-flap view requesting the data.
- parameter index: A zero-indexed number identifying a flap. The index starts
at 0 for the leftmost flap.
- returns: The font of the flap's text. If it returns nil, the flap uses its
internal *Courier* font.
*/
func splitflap(splitflap: Splitflap, fontForFlapAtIndex index: Int) -> UIFont?
}
/// Default implementation of SplitflapDelegate
public extension SplitflapDelegate {
/// Returns by default 0.2 seconds
/// Returns by default 0.2 seconds.
func splitflap(splitflap: Splitflap, rotationDurationForFlapAtIndex index: Int) -> Double {
return 0.2
}
/// Returns nil by default to use the internal flap's font.
func splitflap(splitflap: Splitflap, fontForFlapAtIndex index: Int) -> UIFont? {
return nil
}
}

View File

@ -30,7 +30,7 @@ import UIKit
A Tile is an half view representing the flap's leaf. A tile can represents the
top or the bottom of a leaf.
*/
final class Tile: UIView {
final class TileView: UIView {
private let digitLabel = UILabel()
private let mainLineView = UIView()
private let secondaryLineView = UIView()
@ -54,6 +54,17 @@ final class Tile: UIView {
}
}
// MARK: - Configuring the Label
/// The font of the tile's text.
var font: UIFont? {
didSet {
layoutSubviews()
}
}
// MARK: - Initializing a Flap View
convenience init(position: Position) {
self.init(frame: CGRectZero)
@ -90,6 +101,8 @@ final class Tile: UIView {
addSubview(secondaryLineView)
}
// MARK: - Laying out Subviews
override func layoutSubviews() {
super.layoutSubviews()
@ -126,7 +139,7 @@ final class Tile: UIView {
}
digitLabel.frame = digitLabelFrame
digitLabel.font = UIFont(name: "Courier", size: bounds.width)
digitLabel.font = font ?? UIFont(name: "Courier", size: bounds.width)
mainLineView.frame = mainLineViewFrame
secondaryLineView.frame = secondaryLineViewFrame
}