36 |
37 |
38 |
--------------------------------------------------------------------------------
/Example/SplitflapExample/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 4.1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 | LSRequiresIPhoneOS
24 |
25 | UILaunchStoryboardName
26 | LaunchScreen
27 | UIMainStoryboardFile
28 | Main
29 | UIRequiredDeviceCapabilities
30 |
31 | armv7
32 |
33 | UISupportedInterfaceOrientations
34 |
35 | UIInterfaceOrientationPortrait
36 | UIInterfaceOrientationLandscapeLeft
37 | UIInterfaceOrientationLandscapeRight
38 |
39 | UISupportedInterfaceOrientations~ipad
40 |
41 | UIInterfaceOrientationPortrait
42 | UIInterfaceOrientationPortraitUpsideDown
43 | UIInterfaceOrientationLandscapeLeft
44 | UIInterfaceOrientationLandscapeRight
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/Example/SplitflapExample/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
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 |
--------------------------------------------------------------------------------
/Example/SplitflapExample.xcodeproj/xcshareddata/xcschemes/SplitflapTests.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
16 |
18 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
40 |
41 |
42 |
43 |
49 |
50 |
52 |
53 |
56 |
57 |
58 |
--------------------------------------------------------------------------------
/Example/SplitflapExample/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // SplitflapExample
4 | //
5 | // Created by Yannick LORIOT on 10/11/15.
6 | // Copyright © 2015 Yannick LORIOT. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | @UIApplicationMain
12 | class AppDelegate: UIResponder, UIApplicationDelegate {
13 |
14 | var window: UIWindow?
15 |
16 |
17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
18 | // Override point for customization after application launch.
19 | return true
20 | }
21 |
22 | func applicationWillResignActive(_ application: UIApplication) {
23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
24 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
25 | }
26 |
27 | func applicationDidEnterBackground(_ application: UIApplication) {
28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
30 | }
31 |
32 | func applicationWillEnterForeground(_ application: UIApplication) {
33 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
34 | }
35 |
36 | func applicationDidBecomeActive(_ application: UIApplication) {
37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
38 | }
39 |
40 | func applicationWillTerminate(_ application: UIApplication) {
41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
42 | }
43 |
44 |
45 | }
46 |
47 |
--------------------------------------------------------------------------------
/Example/tvOSExample/ViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // tvOSExample
4 | //
5 | // Created by Yannick LORIOT on 24/11/15.
6 | // Copyright © 2015 Yannick LORIOT. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class ViewController: UIViewController, SplitflapDataSource, SplitflapDelegate {
12 | @IBOutlet weak var splitflap: Splitflap!
13 | @IBOutlet weak var actionButton: UIButton!
14 |
15 | private let words = ["Hey you", "Bonsoir", "12h15", "Arrival"]
16 | private var currentIndex = 0
17 |
18 | override func viewDidLoad() {
19 | super.viewDidLoad()
20 |
21 | splitflap.datasource = self
22 | splitflap.delegate = self
23 | splitflap.reload()
24 | }
25 |
26 | override func viewDidAppear(_ animated: Bool) {
27 | super.viewDidAppear(animated)
28 |
29 | updateSplitFlapAction(actionButton)
30 | }
31 |
32 | // MARK: - Action Methods
33 |
34 | @IBAction func updateSplitFlapAction(_ sender: AnyObject) {
35 | splitflap.setText(words[currentIndex], animated: true, completionBlock: {
36 | print("Display finished!")
37 | })
38 |
39 | currentIndex = (currentIndex + 1) % words.count
40 |
41 | updateButtonWithTitle(words[currentIndex])
42 | }
43 |
44 | private func updateButtonWithTitle(_ title: String) {
45 | actionButton.setTitle("Say \(words[currentIndex])!", for: .normal)
46 | }
47 |
48 | // MARK: - Splitflap DataSource Methods
49 |
50 | func numberOfFlapsInSplitflap(_ splitflap: Splitflap) -> Int {
51 | return 7
52 | }
53 |
54 | func tokensInSplitflap(_ splitflap: Splitflap, flap: Int) -> [String] {
55 | return SplitflapTokens.AlphanumericAndSpace
56 | }
57 |
58 | // MARK: - Splitflap Delegate Methods
59 |
60 | func splitflap(splitflap: Splitflap, rotationDurationForFlapAtIndex index: Int) -> Double {
61 | return 0.2
62 | }
63 |
64 | func splitflap(splitflap: Splitflap, builderForFlapAtIndex index: Int) -> FlapViewBuilder {
65 | return FlapViewBuilder { builder in
66 | builder.backgroundColor = UIColor.black
67 | builder.cornerRadius = 5
68 | builder.font = UIFont(name: "Courier", size: 80)
69 | builder.textAlignment = .center
70 | builder.textColor = UIColor.white
71 | builder.lineColor = UIColor.darkGray
72 | }
73 | }
74 | }
75 |
76 |
--------------------------------------------------------------------------------
/Example/SplitflapExample/ViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // SplitflapExample
4 | //
5 | // Created by Yannick LORIOT on 10/11/15.
6 | // Copyright © 2015 Yannick LORIOT. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class ViewController: UIViewController, SplitflapDataSource, SplitflapDelegate {
12 | @IBOutlet weak var splitflap: Splitflap!
13 | @IBOutlet weak var actionButton: UIButton!
14 |
15 | fileprivate let words = ["Hey you", "Bonsoir", "12h15", "Arrival"]
16 | fileprivate var currentIndex = 0
17 |
18 | override func viewDidLoad() {
19 | super.viewDidLoad()
20 |
21 | splitflap.datasource = self
22 | splitflap.delegate = self
23 | splitflap.reload()
24 | }
25 |
26 | override func viewDidAppear(_ animated: Bool) {
27 | super.viewDidAppear(animated)
28 |
29 | updateSplitFlapAction(actionButton)
30 | }
31 |
32 | // MARK: - Action Methods
33 |
34 | @IBAction func updateSplitFlapAction(_ sender: AnyObject) {
35 | splitflap.setText(words[currentIndex], animated: true, completionBlock: {
36 | print("Display finished!")
37 | })
38 |
39 | currentIndex = (currentIndex + 1) % words.count
40 |
41 | updateButtonWithTitle(words[currentIndex])
42 | }
43 |
44 | fileprivate func updateButtonWithTitle(_ title: String) {
45 | actionButton.setTitle("Say \(words[currentIndex])!", for: UIControl.State())
46 | }
47 |
48 | // MARK: - Splitflap DataSource Methods
49 |
50 | func numberOfFlapsInSplitflap(_ splitflap: Splitflap) -> Int {
51 | return 7
52 | }
53 |
54 | func tokensInSplitflap(_ splitflap: Splitflap, flap: Int) -> [String] {
55 | if flap == 0 {
56 | return SplitflapTokens.Alphanumeric
57 | }
58 | return SplitflapTokens.AlphanumericAndSpace
59 | }
60 |
61 | // MARK: - Splitflap Delegate Methods
62 |
63 | func splitflap(_ splitflap: Splitflap, rotationDurationForFlapAtIndex index: Int) -> Double {
64 | return 0.2
65 | }
66 |
67 | func splitflap(_ splitflap: Splitflap, builderForFlapAtIndex index: Int) -> FlapViewBuilder {
68 | return FlapViewBuilder { builder in
69 | builder.backgroundColor = .black
70 | builder.cornerRadius = 5
71 | builder.font = UIFont(name: "Courier", size: 50)
72 | builder.textAlignment = .center
73 | builder.textColor = .white
74 | builder.lineColor = .darkGray
75 | }
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/Tests/SplitflapDataSourceDatasourceTests.swift:
--------------------------------------------------------------------------------
1 | /*
2 | * Splitflap
3 | *
4 | * Copyright 2015-present Yannick Loriot.
5 | * http://yannickloriot.com
6 | *
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy
8 | * of this software and associated documentation files (the "Software"), to deal
9 | * in the Software without restriction, including without limitation the rights
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | * copies of the Software, and to permit persons to whom the Software is
12 | * furnished to do so, subject to the following conditions:
13 | *
14 | * The above copyright notice and this permission notice shall be included in
15 | * all copies or substantial portions of the Software.
16 | *
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | * THE SOFTWARE.
24 | *
25 | */
26 |
27 | import UIKit
28 | import XCTest
29 |
30 | class SplitflapDataSourceDatasourceTests: XCTTestCaseTemplate {
31 | func testDefaultImplementation() {
32 | class DataSourceMock: SplitflapDataSource {
33 | func numberOfFlapsInSplitflap(_ splitflap: Splitflap) -> Int {
34 | return 0
35 | }
36 | }
37 |
38 | let datasourceMock = DataSourceMock()
39 | let splitflap = Splitflap()
40 |
41 | XCTAssertEqual(datasourceMock.numberOfFlapsInSplitflap(splitflap), 0)
42 | XCTAssertEqual(datasourceMock.tokensInSplitflap(splitflap, flap:0), SplitflapTokens.Alphanumeric)
43 | }
44 |
45 | func testCustomImplementation() {
46 | class DataSourceMock: SplitflapDataSource {
47 | func numberOfFlapsInSplitflap(_ splitflap: Splitflap) -> Int {
48 | return 5
49 | }
50 |
51 | func tokensInSplitflap(_ splitflap: Splitflap, flap: Int) -> [String] {
52 | return SplitflapTokens.Numeric
53 | }
54 | }
55 |
56 | let datasourceMock = DataSourceMock()
57 | let splitflap = Splitflap()
58 |
59 | XCTAssertEqual(datasourceMock.numberOfFlapsInSplitflap(splitflap), 5)
60 | XCTAssertEqual(datasourceMock.tokensInSplitflap(splitflap, flap:0), SplitflapTokens.Numeric)
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/Sources/TokenParser.swift:
--------------------------------------------------------------------------------
1 | /*
2 | * Splitflap
3 | *
4 | * Copyright 2015-present Yannick Loriot.
5 | * http://yannickloriot.com
6 | *
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy
8 | * of this software and associated documentation files (the "Software"), to deal
9 | * in the Software without restriction, including without limitation the rights
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | * copies of the Software, and to permit persons to whom the Software is
12 | * furnished to do so, subject to the following conditions:
13 | *
14 | * The above copyright notice and this permission notice shall be included in
15 | * all copies or substantial portions of the Software.
16 | *
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | * THE SOFTWARE.
24 | *
25 | */
26 |
27 | import Foundation
28 |
29 | /**
30 | The TokenParser parses a given string into a token list.
31 | */
32 | final class TokenParser {
33 | let tokens: [String: Bool]
34 |
35 | required init(tokens: [String]) {
36 | // Transforms a list into a dictionary to improve the search
37 | self.tokens = tokens.reduce([:]) { (dict, elem) in
38 | var dict = dict
39 | dict[elem] = true
40 |
41 | return dict
42 | }
43 | }
44 |
45 | // MARK: - Parsing Inputs
46 |
47 | /**
48 | Parse a given string to find tokens.
49 |
50 | - parameter string: A string to parse.
51 | - returns: A list of token. An empty list if the given string does not
52 | contains token.
53 | */
54 | func parseString(_ string: String) -> [String] {
55 | var tokensFound: [String] = []
56 |
57 | var word: String = ""
58 |
59 | for character in string {
60 | word += String(character)
61 |
62 | if isToken(word) {
63 | tokensFound.append(word)
64 |
65 | word = ""
66 | }
67 | }
68 |
69 | return tokensFound
70 | }
71 |
72 | // MARK: - Checking Token Validity
73 |
74 | /**
75 | Checks whether the given word is a token and returns true if it the case.
76 |
77 | - parameter word: A word as String.
78 | */
79 | fileprivate func isToken(_ word: String) -> Bool {
80 | return tokens[word] != nil
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/Sources/SplitflapTokens.swift:
--------------------------------------------------------------------------------
1 | /*
2 | * Splitflap
3 | *
4 | * Copyright 2015-present Yannick Loriot.
5 | * http://yannickloriot.com
6 | *
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy
8 | * of this software and associated documentation files (the "Software"), to deal
9 | * in the Software without restriction, including without limitation the rights
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | * copies of the Software, and to permit persons to whom the Software is
12 | * furnished to do so, subject to the following conditions:
13 | *
14 | * The above copyright notice and this permission notice shall be included in
15 | * all copies or substantial portions of the Software.
16 | *
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | * THE SOFTWARE.
24 | *
25 | */
26 |
27 | import Foundation
28 |
29 | /**
30 | The SplitflapTokens defines a collection of token string ready to use for
31 | split-flap view.
32 |
33 | A token is a character, a symbol or a text that is displays by the flap view. A
34 | flap view manages a stack a token in order to display them in the good order when
35 | it needs to animate its token change.
36 | */
37 | open class SplitflapTokens {
38 | /// Numeric characters.
39 | public static let Numeric = (0 ... 9).map { String($0) }
40 |
41 | /// Alphabetic characters (lower and upper cases).
42 | public static let Alphabetic = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".map { String($0) }
43 |
44 | /// Combination of alphabetic (lower and upper cases) and numeric characters.
45 | public static let Alphanumeric = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".map { String($0) }
46 |
47 | /// Combination of alphabetic (lower and upper cases) and numeric characters plus the space.
48 | public static let AlphanumericAndSpace = " abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".map { String($0) }
49 |
50 | /// The 12-hour clock characters (from 1 to 12).
51 | public static let TwelveHourClock = (1 ... 12).map { String($0) }
52 |
53 | /// The 24-hour clock characters (from 00 to 23).
54 | public static let TwentyFourHourClock = (0 ... 23).map { String(format:"%02d", $0) }
55 |
56 | /// The minute/second characters (from 00 to 59).
57 | public static let MinuteAndSecond = (0 ... 59).map { String(format:"%02d", $0) }
58 | }
59 |
--------------------------------------------------------------------------------
/Sources/TokenGenerator.swift:
--------------------------------------------------------------------------------
1 | /*
2 | * Splitflap
3 | *
4 | * Copyright 2015-present Yannick Loriot.
5 | * http://yannickloriot.com
6 | *
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy
8 | * of this software and associated documentation files (the "Software"), to deal
9 | * in the Software without restriction, including without limitation the rights
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | * copies of the Software, and to permit persons to whom the Software is
12 | * furnished to do so, subject to the following conditions:
13 | *
14 | * The above copyright notice and this permission notice shall be included in
15 | * all copies or substantial portions of the Software.
16 | *
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | * THE SOFTWARE.
24 | *
25 | */
26 |
27 | import Foundation
28 |
29 | /**
30 | A TokenGenerator helps the flap view by choosing the right token rescpecting the
31 | initial order.
32 | */
33 | final class TokenGenerator: IteratorProtocol {
34 | typealias Element = String
35 |
36 | let tokens: [String]
37 |
38 | required init(tokens: [String]) {
39 | self.tokens = tokens
40 | }
41 |
42 | // MARK: - Implementing GeneratorType
43 |
44 | /// Current element index
45 | fileprivate var currentIndex = 0
46 |
47 | /// Returns the current element of the generator, nil otherwise.
48 | var currentElement: Element? {
49 | get {
50 | if currentIndex < tokens.count {
51 | return tokens[currentIndex]
52 | }
53 |
54 | return nil
55 | }
56 | set(newValue) {
57 | if let value = newValue {
58 | currentIndex = tokens.firstIndex(of: value) ?? currentIndex
59 | }
60 | else {
61 | currentIndex = 0
62 | }
63 | }
64 | }
65 |
66 | /// Advance to the next element and return it, or `nil` if no next.
67 | /// element exists.
68 | @discardableResult
69 | func next() -> Element? {
70 | let tokenCount = tokens.count
71 |
72 | guard tokenCount > 0 else {
73 | return nil
74 | }
75 |
76 | currentIndex = (currentIndex + 1) % tokens.count
77 |
78 | return tokens[currentIndex]
79 | }
80 |
81 | // MARK: - Convenience Methods
82 |
83 | /// Returns the first token, or `nil` if no token.
84 | var firstToken: String? {
85 | get {
86 | return tokens.first
87 | }
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/Sources/SplitflapDataSource.swift:
--------------------------------------------------------------------------------
1 | /*
2 | * Splitflap
3 | *
4 | * Copyright 2015-present Yannick Loriot.
5 | * http://yannickloriot.com
6 | *
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy
8 | * of this software and associated documentation files (the "Software"), to deal
9 | * in the Software without restriction, including without limitation the rights
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | * copies of the Software, and to permit persons to whom the Software is
12 | * furnished to do so, subject to the following conditions:
13 | *
14 | * The above copyright notice and this permission notice shall be included in
15 | * all copies or substantial portions of the Software.
16 | *
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | * THE SOFTWARE.
24 | *
25 | */
26 |
27 | import UIKit
28 |
29 | /**
30 | The SplitflapDataSource protocol must be adopted by an object that mediates
31 | between a Splitflap object and your application’s data model for that split-flap
32 | view. The data source provides the split-flap view with the number of flaps for
33 | displaying the split-flap view data.
34 | */
35 | public protocol SplitflapDataSource: class {
36 | // MARK: - Providing Counts for the Splitflap View
37 |
38 | /**
39 | Called by the split-flap view when it needs the number of flaps.
40 | - parameter splitflap: The split-flap view requesting the data.
41 | - returns: The number of flaps.
42 | */
43 | func numberOfFlapsInSplitflap(_ splitflap: Splitflap) -> Int
44 |
45 | // MARK: - Managing Supported Tokens for the Splitflap Components
46 |
47 | /**
48 | Called by the split-flap view when it needs to update the token strings that
49 | each flaps must display.
50 |
51 | If you don't implement this method the split-flap view will use the
52 | `Alphanumeric` token list.
53 | - parameter splitflap: The split-flap view requesting the data.
54 | - returns: A list of token string used by each flaps to manage their stack of
55 | token.
56 | */
57 | func tokensInSplitflap(_ splitflap: Splitflap, flap: Int) -> [String]
58 | }
59 |
60 | /// Default implementation of SplitflapDataSource
61 | public extension SplitflapDataSource {
62 | /// Returns by default the Alphanumeric token list.
63 | func tokensInSplitflap(_ splitflap: Splitflap, flap: Int) -> [String] {
64 | return SplitflapTokens.Alphanumeric
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/Tests/StoryboardTests.storyboard:
--------------------------------------------------------------------------------
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 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Change log
2 |
3 | ## [Version 4.1.0](https://github.com/yannickl/Splitflap/releases/tag/4.1.0)
4 | *Released on 2018-11-24.*
5 |
6 | - [FIX] Don't show lines when color is set to nil
7 |
8 | ## [Version 4.0.0](https://github.com/yannickl/Splitflap/releases/tag/4.0.0)
9 | *Released on 2018-09-25.*
10 |
11 | **Swift 4.2 Supports**
12 |
13 | ## [Version 3.0.1](https://github.com/yannickl/Splitflap/releases/tag/3.0.1)
14 | *Released on 2017-12-10.*
15 |
16 | - [FIX] Updating to last swift syntax.
17 |
18 | ## [Version 3.0.0](https://github.com/yannickl/Splitflap/releases/tag/3.0.0)
19 | *Released on 2017-09-22.*
20 |
21 | **Swift 4 Supports**
22 |
23 | ## [Version 2.1.0](https://github.com/yannickl/Splitflap/releases/tag/2.1.0)
24 | *Released on 2017-04-01.*
25 |
26 | **Swift 3.1 Supports**
27 |
28 | ## [Version 2.0.0](https://github.com/yannickl/Splitflap/releases/tag/2.0.0)
29 | *Released on 2016-09-15.*
30 |
31 | **Swift 3 Supports**
32 |
33 | - [ADD] Swift Package Manager supports
34 |
35 | ## [Version 1.1.1](https://github.com/yannickl/Splitflap/releases/tag/1.1.1)
36 | Released on 2015-12-14.
37 |
38 | - [FIX] flap animation continue when new text is set [#2](https://github.com/yannickl/Splitflap/issues/2)
39 |
40 | ## [Version 1.1.0](https://github.com/yannickl/Splitflap/releases/tag/1.1.0)
41 | Released on 2015-11-27.
42 |
43 | - [ADD] `TwelveHourClock`, `TwentyFourHourClock` and `MinuteAndSecond` tokens
44 | - [ADD] tvOS supports
45 | - [FIX] Keep text displayed when reload data
46 |
47 | ## [Version 1.0.1](https://github.com/yannickl/Splitflap/releases/tag/1.0.1)
48 | Released on 2015-11-17.
49 |
50 | - [FIX] Builder properties access control ([#1](https://github.com/yannickl/Splitflap/issues/1))
51 |
52 | ## [Version 1.0.0](https://github.com/yannickl/Splitflap/releases/tag/1.0.0)
53 | Released on 2015-11-13.
54 |
55 | - [UPDATE] Rename `supportedTokensInSplitflap:` method to `tokensInSplitflap:`
56 | - [ADD] `setText:animated:completionBlock:` method to know when an animation finished
57 | - [ADD] Test cases
58 |
59 | ## [Version 0.2.0](https://github.com/yannickl/Splitflap/releases/tag/0.2.0)
60 | Released on 2015-11-12.
61 |
62 | - [FIX] Parse characters and words
63 | - [ADD] `FlapViewBuilder` to make flap configuration easier
64 | - [ADD] `splitflap:builderForFlapAtIndex:` method to customize the each flap individually:
65 | - `backgroundColor`, `cornerRadius`, `font`, `textAlignment`, `textColor`, `lineColor`
66 |
67 | ## [Version 0.1.0](https://github.com/yannickl/Splitflap/releases/tag/0.1.0)
68 | Released on 2015-11-11.
69 |
70 | - `flapSpacing` property to configure the spacing between flaps
71 | - `supportedTokensInSplitflap:` method to define the "characters" used by flaps
72 | - `numberOfFlapsInSplitflap:` to set the number of flaps
73 | - `splitflap(splitflap:rotationDurationForFlapAtIndex:` method to change the rotation duration of each flaps
74 | - Cocoapods support
75 | - Carthage support
76 |
--------------------------------------------------------------------------------
/Tests/SplitflapDelegateTests.swift:
--------------------------------------------------------------------------------
1 | /*
2 | * Splitflap
3 | *
4 | * Copyright 2015-present Yannick Loriot.
5 | * http://yannickloriot.com
6 | *
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy
8 | * of this software and associated documentation files (the "Software"), to deal
9 | * in the Software without restriction, including without limitation the rights
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | * copies of the Software, and to permit persons to whom the Software is
12 | * furnished to do so, subject to the following conditions:
13 | *
14 | * The above copyright notice and this permission notice shall be included in
15 | * all copies or substantial portions of the Software.
16 | *
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | * THE SOFTWARE.
24 | *
25 | */
26 |
27 | import UIKit
28 | import XCTest
29 |
30 | class SplitflapDelegateTests: XCTTestCaseTemplate {
31 | func testDefaultImplementation() {
32 | class DelegateMock: SplitflapDelegate {}
33 |
34 | let delegateMock = DelegateMock()
35 | let splitflap = Splitflap()
36 |
37 | XCTAssertEqual(delegateMock.splitflap(splitflap, rotationDurationForFlapAtIndex: 0), 0.2)
38 | }
39 |
40 | func testCustomImplementation() {
41 | class DelegateMock: SplitflapDelegate {
42 | func splitflap(_ splitflap: Splitflap, rotationDurationForFlapAtIndex index: Int) -> Double {
43 | return 1
44 | }
45 |
46 | func splitflap(_ splitflap: Splitflap, builderForFlapAtIndex index: Int) -> FlapViewBuilder {
47 | return FlapViewBuilder { builder in
48 | builder.backgroundColor = UIColor.red
49 | builder.cornerRadius = 0
50 | builder.font = UIFont(name: "HelveticaNeue", size: 50)
51 | builder.textAlignment = .left
52 | builder.textColor = UIColor.yellow
53 | builder.lineColor = UIColor.green
54 | }
55 | }
56 | }
57 |
58 | let delegateMock = DelegateMock()
59 | let splitflap = Splitflap()
60 |
61 | XCTAssertEqual(delegateMock.splitflap(splitflap, rotationDurationForFlapAtIndex: 0), 1)
62 |
63 | let builder = delegateMock.splitflap(splitflap, builderForFlapAtIndex: 0)
64 | XCTAssertEqual(builder.backgroundColor, UIColor.red)
65 | XCTAssertEqual(builder.cornerRadius, 0)
66 | XCTAssertEqual(builder.font, UIFont(name: "HelveticaNeue", size: 50))
67 | XCTAssertEqual(builder.textAlignment, NSTextAlignment.left)
68 | XCTAssertEqual(builder.textColor, UIColor.yellow)
69 | XCTAssertEqual(builder.lineColor, UIColor.green)
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/Sources/SplitflapDelegate.swift:
--------------------------------------------------------------------------------
1 | /*
2 | * Splitflap
3 | *
4 | * Copyright 2015-present Yannick Loriot.
5 | * http://yannickloriot.com
6 | *
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy
8 | * of this software and associated documentation files (the "Software"), to deal
9 | * in the Software without restriction, including without limitation the rights
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | * copies of the Software, and to permit persons to whom the Software is
12 | * furnished to do so, subject to the following conditions:
13 | *
14 | * The above copyright notice and this permission notice shall be included in
15 | * all copies or substantial portions of the Software.
16 | *
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | * THE SOFTWARE.
24 | *
25 | */
26 |
27 | import UIKit
28 |
29 | /**
30 | The delegate of a Splitflap object should adopt this protocol and implement at
31 | least some of its methods to provide the split-flap view with the data it needs
32 | to construct itself.
33 | */
34 | public protocol SplitflapDelegate: class {
35 | // MARK: - Setting the Rotation Duration of Flaps
36 |
37 | /**
38 | Called by the split-flap when it needs to rotate a flap at a given index.
39 |
40 | The default value for each flap is 0.2 seconds.
41 |
42 | - parameter splitflap: The split-flap view requesting the data.
43 | - parameter index: A zero-indexed number identifying a flap. The index starts
44 | at 0 for the leftmost flap.
45 | - returns: The duration of the flap rotation in seconds.
46 | */
47 | func splitflap(_ splitflap: Splitflap, rotationDurationForFlapAtIndex index: Int) -> Double
48 |
49 | // MARK: - Configuring the Label of Flaps
50 |
51 | /**
52 | Called by the split-flap when it needs to create its flap subviews.
53 |
54 | - parameter splitflap: The split-flap view requesting the data.
55 | - parameter index: A zero-indexed number identifying a flap. The index starts
56 | at 0 for the leftmost flap.
57 | - returns: A FlapView builder object to create custom flaps.
58 | */
59 | func splitflap(_ splitflap: Splitflap, builderForFlapAtIndex index: Int) -> FlapViewBuilder
60 | }
61 |
62 | /// Default implementation of SplitflapDelegate
63 | public extension SplitflapDelegate {
64 | /// Returns by default 0.2 seconds.
65 | func splitflap(_ splitflap: Splitflap, rotationDurationForFlapAtIndex index: Int) -> Double {
66 | return 0.2
67 | }
68 |
69 | /// Returns the default FlapViewBuilder configuration by default.
70 | func splitflap(_ splitflap: Splitflap, builderForFlapAtIndex index: Int) -> FlapViewBuilder {
71 | return FlapViewBuilder()
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/Example/SplitflapExample.xcodeproj/xcshareddata/xcschemes/Splitflap.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
34 |
35 |
45 |
46 |
52 |
53 |
54 |
55 |
56 |
57 |
63 |
64 |
70 |
71 |
72 |
73 |
75 |
76 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/Tests/TokenGeneratorTests.swift:
--------------------------------------------------------------------------------
1 | /*
2 | * Splitflap
3 | *
4 | * Copyright 2015-present Yannick Loriot.
5 | * http://yannickloriot.com
6 | *
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy
8 | * of this software and associated documentation files (the "Software"), to deal
9 | * in the Software without restriction, including without limitation the rights
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | * copies of the Software, and to permit persons to whom the Software is
12 | * furnished to do so, subject to the following conditions:
13 | *
14 | * The above copyright notice and this permission notice shall be included in
15 | * all copies or substantial portions of the Software.
16 | *
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | * THE SOFTWARE.
24 | *
25 | */
26 |
27 | import UIKit
28 | import XCTest
29 |
30 | class TokenGeneratorTests: XCTTestCaseTemplate {
31 | func testCurrentElement() {
32 | let emptyGenerator = TokenGenerator(tokens: [])
33 | XCTAssertNil(emptyGenerator.currentElement)
34 |
35 | let oneElementGenerator = TokenGenerator(tokens: ["a"])
36 | XCTAssertEqual(oneElementGenerator.currentElement, "a")
37 |
38 | let nElementsGenerator = TokenGenerator(tokens: ["a", "b", "c", "d"])
39 | XCTAssertEqual(nElementsGenerator.currentElement, "a")
40 |
41 | // Set current element
42 | nElementsGenerator.currentElement = "c"
43 | XCTAssertEqual(nElementsGenerator.currentElement, "c")
44 |
45 | // Set invalid element
46 | nElementsGenerator.currentElement = "l"
47 | XCTAssertEqual(nElementsGenerator.currentElement, "c")
48 |
49 | // Set no element
50 | nElementsGenerator.currentElement = nil
51 | XCTAssertEqual(nElementsGenerator.currentElement, "a")
52 | }
53 |
54 | func testNext() {
55 | let emptyGenerator = TokenGenerator(tokens: [])
56 | XCTAssertNil(emptyGenerator.currentElement)
57 |
58 | let nilElement = emptyGenerator.next()
59 | XCTAssertNil(nilElement)
60 | XCTAssertNil(emptyGenerator.currentElement)
61 |
62 | let oneElementGenerator = TokenGenerator(tokens: ["a"])
63 | let aElement = oneElementGenerator.next()
64 | XCTAssertEqual(aElement, "a")
65 | XCTAssertEqual(oneElementGenerator.currentElement, "a")
66 |
67 | let twoElementsGenerator = TokenGenerator(tokens: ["a", "b"])
68 | var twoElement = twoElementsGenerator.next()
69 | XCTAssertEqual(twoElement, "b")
70 | XCTAssertEqual(twoElementsGenerator.currentElement, "b")
71 |
72 | twoElement = twoElementsGenerator.next()
73 | XCTAssertEqual(twoElement, "a")
74 | XCTAssertEqual(twoElementsGenerator.currentElement, "a")
75 | }
76 |
77 | func testFirstToken() {
78 | let emptyGenerator = TokenGenerator(tokens: [])
79 | XCTAssertNil(emptyGenerator.firstToken)
80 |
81 | let oneElementGenerator = TokenGenerator(tokens: ["a"])
82 | XCTAssertEqual(oneElementGenerator.firstToken, "a")
83 |
84 | let nElementsGenerator = TokenGenerator(tokens: ["a", "b", "c", "d"])
85 | nElementsGenerator.next()
86 | XCTAssertEqual(nElementsGenerator.firstToken, "a")
87 | }
88 | }
--------------------------------------------------------------------------------
/Tests/TokenParserTests.swift:
--------------------------------------------------------------------------------
1 | /*
2 | * Splitflap
3 | *
4 | * Copyright 2015-present Yannick Loriot.
5 | * http://yannickloriot.com
6 | *
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy
8 | * of this software and associated documentation files (the "Software"), to deal
9 | * in the Software without restriction, including without limitation the rights
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | * copies of the Software, and to permit persons to whom the Software is
12 | * furnished to do so, subject to the following conditions:
13 | *
14 | * The above copyright notice and this permission notice shall be included in
15 | * all copies or substantial portions of the Software.
16 | *
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | * THE SOFTWARE.
24 | *
25 | */
26 |
27 | import UIKit
28 | import XCTest
29 |
30 | class TokenParserTests: XCTTestCaseTemplate {
31 | func testParseEmptyString() {
32 | let parser = TokenParser(tokens: [])
33 |
34 | XCTAssertEqual(parser.parseString(""), [])
35 | XCTAssertEqual(parser.parseString(" "), [])
36 | XCTAssertEqual(parser.parseString("hello"), [])
37 | }
38 |
39 | func testParseOneSimpleTokenString() {
40 | let parser = TokenParser(tokens: ["a"])
41 |
42 | XCTAssertEqual(parser.parseString(""), [])
43 | XCTAssertEqual(parser.parseString("hello"), [])
44 | XCTAssertEqual(parser.parseString("a"), ["a"])
45 | XCTAssertEqual(parser.parseString("aaaaa"), ["a", "a", "a", "a", "a"])
46 | XCTAssertEqual(parser.parseString("abracadabra"), ["a"])
47 | XCTAssertEqual(parser.parseString("ba_alababa"), [])
48 | }
49 |
50 | func testParseAlphabeticTokenString() {
51 | let parser = TokenParser(tokens: SplitflapTokens.Alphabetic)
52 |
53 | XCTAssertEqual(parser.parseString(""), [])
54 | XCTAssertEqual(parser.parseString("hello"), ["h", "e", "l", "l", "o"])
55 | XCTAssertEqual(parser.parseString("Hello"), ["H", "e", "l", "l", "o"])
56 | XCTAssertEqual(parser.parseString("h0ello"), ["h"])
57 | XCTAssertEqual(parser.parseString(" hello"), [])
58 | }
59 |
60 | func testParseOneComplexTokenString() {
61 | let parser = TokenParser(tokens: ["foo"])
62 |
63 | XCTAssertEqual(parser.parseString(""), [])
64 | XCTAssertEqual(parser.parseString("f"), [])
65 | XCTAssertEqual(parser.parseString("fo"), [])
66 | XCTAssertEqual(parser.parseString("foo"), ["foo"])
67 | XCTAssertEqual(parser.parseString("foof"), ["foo"])
68 | XCTAssertEqual(parser.parseString("foofo"), ["foo"])
69 | XCTAssertEqual(parser.parseString("foofoo"), ["foo", "foo"])
70 | XCTAssertEqual(parser.parseString("footfoo"), ["foo"])
71 | XCTAssertEqual(parser.parseString("playfoot"), [])
72 | }
73 |
74 | func testParseComplexTokenString() {
75 | let parser = TokenParser(tokens: ["foo", "bar", "pop"])
76 |
77 | XCTAssertEqual(parser.parseString(""), [])
78 | XCTAssertEqual(parser.parseString("foo"), ["foo"])
79 | XCTAssertEqual(parser.parseString("foopop"), ["foo", "pop"])
80 | XCTAssertEqual(parser.parseString("popbarfoopop"), ["pop", "bar", "foo", "pop"])
81 | XCTAssertEqual(parser.parseString("afoo"), [])
82 | }
83 | }
--------------------------------------------------------------------------------
/Sources/FlapViewBuilder.swift:
--------------------------------------------------------------------------------
1 | /*
2 | * Splitflap
3 | *
4 | * Copyright 2015-present Yannick Loriot.
5 | * http://yannickloriot.com
6 | *
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy
8 | * of this software and associated documentation files (the "Software"), to deal
9 | * in the Software without restriction, including without limitation the rights
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | * copies of the Software, and to permit persons to whom the Software is
12 | * furnished to do so, subject to the following conditions:
13 | *
14 | * The above copyright notice and this permission notice shall be included in
15 | * all copies or substantial portions of the Software.
16 | *
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | * THE SOFTWARE.
24 | *
25 | */
26 |
27 | import UIKit
28 |
29 | /**
30 | The FlapViewBuilder aims to create a simple configuration object for the Flap
31 | view. It allows you to define the *backgroundColor*, the *font*, the
32 | *cornerRadius*, etc. It is based on the builder pattern (for more information
33 | take a look at https://github.com/ochococo/Design-Patterns-In-Swift#-builder)
34 | */
35 | public final class FlapViewBuilder {
36 | // MARK: - Customizing Flaps
37 |
38 | /**
39 | The builder block.
40 |
41 | The block gives a reference of builder you can configure.
42 | */
43 | public typealias FlapViewBuilderBlock = (_ builder: FlapViewBuilder) -> Void
44 |
45 | /**
46 | The flap's background color.
47 |
48 | If the value is nil, it results in a transparent background color. The
49 | default value is black.
50 | */
51 | public var backgroundColor: UIColor? = .black
52 |
53 | /**
54 | The radius to use when drawing rounded corners for the flap’s background.
55 |
56 | Setting the radius to a value greater than 0.0 causes the flap to begin
57 | drawing rounded corners on its background.
58 |
59 | The default value of this property is 5.0.
60 | */
61 | public var cornerRadius: CGFloat = 5
62 |
63 | /**
64 | The font of the flap.
65 |
66 | If the font is nil, the flap uses its internal default *Courier* font.
67 |
68 | The default value of this property is nil.
69 | */
70 | public var font: UIFont?
71 |
72 | /**
73 | The technique to use for aligning the text.
74 |
75 | The default value of this property is NSTextAlignment.Center.
76 | */
77 | public var textAlignment: NSTextAlignment = .center
78 |
79 | /**
80 | The color of the text.
81 |
82 | Uses the white color by default.
83 | */
84 | public var textColor: UIColor = UIColor.white
85 |
86 | /**
87 | The flap's middle line color.
88 |
89 | If the value is nil, it results in a transparent line color. The default
90 | value is dark gray.
91 | */
92 | public var lineColor: UIColor? = UIColor.darkGray
93 |
94 | /**
95 | The flap's middle line height relative to the default. The default value
96 | id 1.0.
97 | */
98 | public var flipPointHeightFactor: CGFloat = 1.0
99 |
100 | /**
101 | The on/off toggle for a label's property "adjustsFontSizeToFitWidth" inside every flap
102 | By defaul the value is false, but it could be changed while initing a builder
103 | */
104 | public var adjustsFontSizeToFitWidth: Bool = false
105 | // MARK: - Initializing a Flap View
106 |
107 | /**
108 | Initialize a FlapView builder with default values.
109 | */
110 | public init() {}
111 |
112 | /**
113 | Initialize a FlapView builder with default values.
114 |
115 | - parameter buildBlock: A FlapView builder block to configure itself.
116 | */
117 | public init(buildBlock: FlapViewBuilderBlock) {
118 | buildBlock(self)
119 | }
120 | }
121 |
--------------------------------------------------------------------------------
/Example/SplitflapExample/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
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 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
--------------------------------------------------------------------------------
/Example/tvOSExample/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
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 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/Tests/SplitflapTests.swift:
--------------------------------------------------------------------------------
1 | /*
2 | * Splitflap
3 | *
4 | * Copyright 2015-present Yannick Loriot.
5 | * http://yannickloriot.com
6 | *
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy
8 | * of this software and associated documentation files (the "Software"), to deal
9 | * in the Software without restriction, including without limitation the rights
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | * copies of the Software, and to permit persons to whom the Software is
12 | * furnished to do so, subject to the following conditions:
13 | *
14 | * The above copyright notice and this permission notice shall be included in
15 | * all copies or substantial portions of the Software.
16 | *
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | * THE SOFTWARE.
24 | *
25 | */
26 |
27 | import UIKit
28 | import XCTest
29 |
30 | class SplitflapTests: XCTTestCaseTemplate {
31 | func testDefaultSplitflap() {
32 | let splitflap = Splitflap()
33 |
34 | XCTAssertNil(splitflap.datasource)
35 | XCTAssertNil(splitflap.delegate)
36 | XCTAssertEqual(splitflap.numberOfFlaps, 0)
37 | XCTAssertEqual(splitflap.tokens, [])
38 | XCTAssertEqual(splitflap.flapSpacing, 2)
39 | XCTAssertNil(splitflap.text)
40 |
41 | // 'didMoveToWindow' calls the 'reload' method
42 | splitflap.didMoveToWindow()
43 |
44 | XCTAssertNil(splitflap.datasource)
45 | XCTAssertNil(splitflap.delegate)
46 | XCTAssertEqual(splitflap.numberOfFlaps, 0)
47 | XCTAssertEqual(splitflap.tokens, SplitflapTokens.Alphanumeric)
48 | XCTAssertEqual(splitflap.flapSpacing, 2)
49 | XCTAssertNil(splitflap.text)
50 | }
51 |
52 | func testText() {
53 | class DataSourceMock: SplitflapDataSource {
54 | func numberOfFlapsInSplitflap(_ splitflap: Splitflap) -> Int {
55 | return 5
56 | }
57 | }
58 |
59 | // By default, length is 0
60 | let splitflap = Splitflap()
61 | XCTAssertNil(splitflap.text)
62 |
63 | splitflap.text = "Alongtext"
64 | XCTAssertNil(splitflap.text)
65 |
66 | // String with length 5
67 | let datasourceMock = DataSourceMock()
68 | splitflap.datasource = datasourceMock
69 | splitflap.reload()
70 |
71 | XCTAssertNil(splitflap.text)
72 |
73 | splitflap.text = "hello"
74 | XCTAssertEqual(splitflap.text, "hello")
75 |
76 | splitflap.text = "helloworld"
77 | XCTAssertEqual(splitflap.text, "hello")
78 |
79 | splitflap.text = "$invalid!"
80 | XCTAssertNil(splitflap.text)
81 | }
82 |
83 | func testSetText() {
84 | class DataSourceMock: SplitflapDataSource {
85 | func numberOfFlapsInSplitflap(_ splitflap: Splitflap) -> Int {
86 | return 9
87 | }
88 | }
89 |
90 | class DelegateMock: SplitflapDelegate {
91 | private func splitflap(splitflap: Splitflap, rotationDurationForFlapAtIndex index: Int) -> Double {
92 | return 0.01
93 | }
94 | }
95 |
96 | // By default, length is 0
97 | let splitflap = Splitflap()
98 | XCTAssertNil(splitflap.text)
99 |
100 | splitflap.setText("Alongtext", animated: true)
101 | XCTAssertNil(splitflap.text)
102 |
103 | // String with length 9
104 | let datasourceMock = DataSourceMock()
105 | let delegateMock = DelegateMock()
106 | splitflap.datasource = datasourceMock
107 | splitflap.delegate = delegateMock
108 | splitflap.reload()
109 |
110 | var expect = expectation(description: "Block completed immediatly when no animation")
111 | splitflap.setText("Alongtext", animated: false, completionBlock: {
112 | expect.fulfill()
113 | })
114 | waitForExpectations(timeout: 0.1, handler:nil)
115 |
116 | expect = expectation(description: "Block animation completed")
117 | splitflap.setText("Alongtext", animated: true, completionBlock: {
118 | expect.fulfill()
119 | })
120 | XCTAssertEqual(splitflap.text, "Alongtext")
121 | waitForExpectations(timeout: 2.0, handler:nil)
122 |
123 | expect = expectation(description: "Block animation completed even with invalid text")
124 | splitflap.setText("$invalid!", animated: true, completionBlock: {
125 | expect.fulfill()
126 | })
127 | XCTAssertNil(splitflap.text)
128 | waitForExpectations(timeout: 2.0, handler:nil)
129 | }
130 |
131 | func testReload() {
132 | class DataSourceMock: SplitflapDataSource {
133 | func numberOfFlapsInSplitflap(_ splitflap: Splitflap) -> Int {
134 | return 2
135 | }
136 | }
137 |
138 | let datasourceMock = DataSourceMock()
139 | let splitflap = Splitflap()
140 | splitflap.datasource = datasourceMock
141 |
142 | splitflap.reload()
143 | XCTAssertEqual(splitflap.numberOfFlaps, 2)
144 |
145 | splitflap.reload()
146 | XCTAssertEqual(splitflap.numberOfFlaps, 2)
147 | }
148 | }
149 |
--------------------------------------------------------------------------------
/Sources/TileView.swift:
--------------------------------------------------------------------------------
1 | /*
2 | * Splitflap
3 | *
4 | * Copyright 2015-present Yannick Loriot.
5 | * http://yannickloriot.com
6 | *
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy
8 | * of this software and associated documentation files (the "Software"), to deal
9 | * in the Software without restriction, including without limitation the rights
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | * copies of the Software, and to permit persons to whom the Software is
12 | * furnished to do so, subject to the following conditions:
13 | *
14 | * The above copyright notice and this permission notice shall be included in
15 | * all copies or substantial portions of the Software.
16 | *
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | * THE SOFTWARE.
24 | *
25 | */
26 |
27 | import UIKit
28 |
29 | /**
30 | A Tile is an half view representing the flap's leaf. A tile can represents the
31 | top or the bottom of a leaf.
32 | */
33 | final class TileView: UIView {
34 | fileprivate let digitLabel = UILabel()
35 | fileprivate let mainLineView = UIView()
36 | fileprivate let secondaryLineView = UIView()
37 |
38 | /// Defines the position and by the same time appearance of the tiles.
39 | enum Position {
40 | /// Tile positioned as a top leaf.
41 | case top
42 | /// Tile positioned as a bottom leaf.
43 | case bottom
44 | }
45 |
46 | let position: Position
47 |
48 | // MARK: - Setting Symbols
49 |
50 | /**
51 | Set the given symbol as text.
52 |
53 | - parameter symbol: An optional symbol string.
54 | */
55 | func setSymbol(_ symbol: String?) {
56 | digitLabel.text = symbol
57 | }
58 |
59 | // MARK: - Configuring the Label
60 |
61 | /// The font of the tile's text.
62 | fileprivate var font: UIFont?
63 |
64 | /**
65 | The radii size to use when drawing rounded corners.
66 | */
67 | fileprivate let cornerRadii: CGSize
68 |
69 | // MARK: - Initializing a Flap View
70 |
71 | required init(builder: FlapViewBuilder, position: Position) {
72 | self.cornerRadii = CGSize(width: builder.cornerRadius, height: builder.cornerRadius)
73 | self.position = position
74 |
75 | super.init(frame: CGRect.zero)
76 |
77 | setupViewsWithBuilder(builder)
78 | }
79 |
80 | required init?(coder aDecoder: NSCoder) {
81 | cornerRadii = CGSize(width: 0, height: 0)
82 | position = .top
83 |
84 | super.init(coder: aDecoder)
85 | }
86 |
87 | // MARK: - Layout the View
88 |
89 | fileprivate var flipPointHeightFactor: CGFloat = 1.0
90 |
91 | /// Setup the views helping by the given builder.
92 | fileprivate func setupViewsWithBuilder(_ builder: FlapViewBuilder) {
93 | font = builder.font
94 |
95 | layer.masksToBounds = true
96 | backgroundColor = builder.backgroundColor
97 |
98 | digitLabel.textAlignment = builder.textAlignment
99 | digitLabel.textColor = builder.textColor
100 | digitLabel.backgroundColor = builder.backgroundColor
101 | digitLabel.adjustsFontSizeToFitWidth = builder.adjustsFontSizeToFitWidth
102 | flipPointHeightFactor = builder.flipPointHeightFactor
103 |
104 | addSubview(digitLabel)
105 |
106 | // Don't add the line if the color was set to nil
107 | if let lineColor = builder.lineColor {
108 | mainLineView.backgroundColor = lineColor
109 | secondaryLineView.backgroundColor = builder.backgroundColor
110 | addSubview(mainLineView)
111 | addSubview(secondaryLineView)
112 | }
113 | }
114 |
115 | // MARK: - Laying out Subviews
116 |
117 | override func layoutSubviews() {
118 | super.layoutSubviews()
119 |
120 | // Round corners
121 | let path: UIBezierPath
122 |
123 | if position == .top {
124 | path = UIBezierPath(roundedRect:bounds, byRoundingCorners:[.topLeft, .topRight], cornerRadii: cornerRadii)
125 | }
126 | else {
127 | path = UIBezierPath(roundedRect:bounds, byRoundingCorners:[.bottomLeft, .bottomRight], cornerRadii: cornerRadii)
128 | }
129 |
130 | let maskLayer = CAShapeLayer()
131 | maskLayer.path = path.cgPath
132 | layer.mask = maskLayer
133 |
134 | // Position elements
135 | var digitLabelFrame = bounds
136 | var mainLineViewFrame = bounds
137 | var secondaryLineViewFrame = bounds
138 |
139 | if position == .top {
140 | digitLabelFrame.size.height = digitLabelFrame.height * 2
141 | digitLabelFrame.origin.y = 0
142 | mainLineViewFrame = CGRect(x: 0, y: bounds.height - (2 * flipPointHeightFactor), width: bounds.width, height: 4 * flipPointHeightFactor)
143 | secondaryLineViewFrame = CGRect(x: 0, y: bounds.height - (1 * flipPointHeightFactor), width: bounds.width, height: 2 * flipPointHeightFactor)
144 | }
145 | else {
146 | digitLabelFrame.size.height = digitLabelFrame.height * 2
147 | digitLabelFrame.origin.y = -digitLabelFrame.height / 2
148 | mainLineViewFrame = CGRect(x: 0, y: -2 * flipPointHeightFactor, width: bounds.width, height: 3 * flipPointHeightFactor)
149 | secondaryLineViewFrame = CGRect(x: 0, y: -2 * flipPointHeightFactor, width: bounds.width, height: 2 * flipPointHeightFactor)
150 | }
151 |
152 | digitLabel.frame = digitLabelFrame
153 | digitLabel.font = font ?? UIFont(name: "Courier", size: bounds.width)
154 | mainLineView.frame = mainLineViewFrame
155 | secondaryLineView.frame = secondaryLineViewFrame
156 | }
157 | }
158 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | ***🛫 Splitflap*** is a simple to use component to present changeable alphanumeric text like often used as a public transport timetable in airports or railway stations or with some flip clocks.
15 |
16 |
17 | Requirements • Usage • Installation • Contribution • Contact • License
18 |
19 |
20 | ## Requirements
21 |
22 | - iOS 8.0+ / tvOS 9.0+
23 | - Xcode 9.0+
24 | - Swift 4.2+
25 |
26 | ## Usage
27 |
28 | ### Hello World
29 |
30 | The first example is the simplest way to use the `Splitflap` component. Here how to display this "Hello" text:
31 |
32 | 
33 |
34 | ```swift
35 | import Splitflap
36 |
37 | let splitflapView = Splitflap(frame: CGRect(x: 0, y: 0, width: 370, height: 53))
38 | splitflapView.datasource = self
39 |
40 | // Set the text to display by animating the flaps
41 | splitflapView.setText("Hello", animated: true)
42 |
43 | // MARK: - Splitflap DataSource Methods
44 |
45 | // Defines the number of flaps that will be used to display the text
46 | func numberOfFlapsInSplitflap(_ splitflap: Splitflap) -> Int {
47 | return 5
48 | }
49 |
50 | ```
51 |
52 | ### Theming
53 |
54 | `Splitflap` allows you to customize each flap individually by providing a `splitflap:builderForFlapAtIndex:` delegate method:
55 |
56 | 
57 |
58 | ```swift
59 | let splitflapView = Splitflap(frame: CGRect(x: 0, y: 0, width: 370, height: 53))
60 | splitflapView.delegate = self
61 | splitflapView.datasource = self
62 |
63 | // Set the text with an emoji
64 | splitflap.text = "Cat \u{1F63B}"
65 |
66 | // MARK: - Splitflap Delegate Methods
67 |
68 | // Configure the appearance for each flaps
69 | func splitflap(_ splitflap: Splitflap, builderForFlapAtIndex index: Int) -> FlapViewBuilder {
70 | return FlapViewBuilder { builder in
71 | builder.backgroundColor = UIColor(red: 251/255, green: 249/255, blue: 243/255, alpha: 1)
72 | builder.cornerRadius = 5
73 | builder.font = UIFont(name: "Avenir-Black", size:45)
74 | builder.textAlignment = .center
75 | builder.textColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.5)
76 | builder.lineColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.3)
77 | }
78 | }
79 |
80 | // MARK: - Splitflap DataSource Methods
81 |
82 | func tokensInSplitflap(_ splitflap: Splitflap) -> [String] {
83 | return " Cat\u{1F63B}".characters.map { String($0) }
84 | }
85 | ```
86 | ### And many more...
87 |
88 | To go further, take a look at the documentation and the example project.
89 |
90 | *Note: All contributions are welcome*
91 |
92 | ## Installation
93 |
94 | #### CocoaPods
95 |
96 | Install CocoaPods if not already available:
97 |
98 | ``` bash
99 | $ [sudo] gem install cocoapods
100 | $ pod setup
101 | ```
102 | Go to the directory of your Xcode project, and Create and Edit your Podfile and add _Splitflap_:
103 |
104 | ``` bash
105 | $ cd /path/to/MyProject
106 | $ touch Podfile
107 | $ edit Podfile
108 | source 'https://github.com/CocoaPods/Specs.git'
109 | platform :ios, '8.0'
110 |
111 | use_frameworks!
112 | pod 'Splitflap', '~> 4.1.0'
113 | ```
114 |
115 | Install into your project:
116 |
117 | ``` bash
118 | $ pod install
119 | ```
120 |
121 | Open your project in Xcode from the .xcworkspace file (not the usual project file):
122 |
123 | ``` bash
124 | $ open MyProject.xcworkspace
125 | ```
126 |
127 | You can now `import Splitflap` framework into your files.
128 |
129 | #### Carthage
130 |
131 | [Carthage](https://github.com/Carthage/Carthage) is a decentralized dependency manager that automates the process of adding frameworks to your Cocoa application.
132 |
133 | You can install Carthage with [Homebrew](http://brew.sh/) using the following command:
134 |
135 | ```bash
136 | $ brew update
137 | $ brew install carthage
138 | ```
139 |
140 | To integrate `Splitflap` into your Xcode project using Carthage, specify it in your `Cartfile` file:
141 |
142 | ```ogdl
143 | github "yannickl/Splitflap" >= 4.1.0
144 | ```
145 |
146 | #### Swift Package Manager
147 |
148 | You can use [The Swift Package Manager](https://swift.org/package-manager) to install `Splitflap` by adding the proper description to your `Package.swift` file:
149 | ```swift
150 | import PackageDescription
151 |
152 | let package = Package(
153 | name: "YOUR_PROJECT_NAME",
154 | targets: [],
155 | dependencies: [
156 | .Package(url: "https://github.com/yannickl/Splitflap.git", versions: "4.1.0" ..< Version.max)
157 | ]
158 | )
159 | ```
160 |
161 | Note that the [Swift Package Manager](https://swift.org/package-manager) is still in early design and development, for more information checkout its [GitHub Page](https://github.com/apple/swift-package-manager).
162 |
163 | #### Manually
164 |
165 | [Download](https://github.com/YannickL/Splitflap/archive/master.zip) the project and copy the `Splitflap` folder into your project to use it in.
166 |
167 | ## Contribution
168 |
169 | Contributions are welcomed and encouraged *♡*.
170 |
171 | ## Contact
172 |
173 | Yannick Loriot
174 | - [https://21.co/yannickl/](https://21.co/yannickl/)
175 | - [https://twitter.com/yannickloriot](https://twitter.com/yannickloriot)
176 |
177 | ## License (MIT)
178 |
179 | Copyright (c) 2015-present - Yannick Loriot
180 |
181 | Permission is hereby granted, free of charge, to any person obtaining a copy
182 | of this software and associated documentation files (the "Software"), to deal
183 | in the Software without restriction, including without limitation the rights
184 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
185 | copies of the Software, and to permit persons to whom the Software is
186 | furnished to do so, subject to the following conditions:
187 |
188 | The above copyright notice and this permission notice shall be included in
189 | all copies or substantial portions of the Software.
190 |
191 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
192 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
193 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
194 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
195 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
196 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
197 | THE SOFTWARE.
198 |
--------------------------------------------------------------------------------
/Sources/Splitflap.swift:
--------------------------------------------------------------------------------
1 | /*
2 | * Splitflap
3 | *
4 | * Copyright 2015-present Yannick Loriot.
5 | * http://yannickloriot.com
6 | *
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy
8 | * of this software and associated documentation files (the "Software"), to deal
9 | * in the Software without restriction, including without limitation the rights
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | * copies of the Software, and to permit persons to whom the Software is
12 | * furnished to do so, subject to the following conditions:
13 | *
14 | * The above copyright notice and this permission notice shall be included in
15 | * all copies or substantial portions of the Software.
16 | *
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | * THE SOFTWARE.
24 | *
25 | */
26 |
27 | import UIKit
28 |
29 | /**
30 | A split-flap display component that presents changeable alphanumeric text often
31 | used as a public transport timetable in airports or railway stations and with
32 | some flip clocks.
33 | */
34 | @IBDesignable open class Splitflap: UIView {
35 | // MARK: - Specifying the Data Source
36 |
37 | /**
38 | The data source for the split-flap view.
39 |
40 | The data source must adopt the SplitflapDataSource protocol and implement the
41 | required methods to return the number of flaps.
42 | */
43 | open weak var datasource: SplitflapDataSource?
44 |
45 | // MARK: - Specifying the Delegate
46 |
47 | /**
48 | The delegate for the split-flap view.
49 |
50 | The delegate must adopt the SplitflapDelegate protocol and implement the
51 | required methods to specify the flap rotation for example.
52 | */
53 | open weak var delegate: SplitflapDelegate?
54 |
55 | // MARK: - Getting Flaps
56 |
57 | /**
58 | Gets the number of flaps for the split-flap view.
59 |
60 | A Splitflap object fetches the value of this property from the data source and
61 | and caches it. The default value is zero.
62 | */
63 | open fileprivate(set) var numberOfFlaps: Int = 0
64 |
65 | /// The flap views used the the split-flap component to display text.
66 | fileprivate var flaps: [FlapView] = [] {
67 | didSet {
68 | for flap in oldValue {
69 | flap.removeFromSuperview()
70 | }
71 | }
72 | }
73 |
74 | // MARK: - Configuring the Flap Spacing
75 |
76 | /**
77 | Specifies the spacing to use between flaps.
78 |
79 | The default value of this property is 2.0.
80 | */
81 | @IBInspectable open var flapSpacing: CGFloat = 2
82 |
83 | // MARK: - Accessing the Text Attributes
84 |
85 | /// The current displayed text.
86 | fileprivate var textAsToken: String?
87 |
88 | /**
89 | The text displayed by the split-flap.
90 |
91 | Setting the text with this property is equilavent to call the setText:animated:
92 | methods with the animated attribute as false. This string is nil by default.
93 |
94 | - seealso: setText:animated:
95 | */
96 | open var text: String? {
97 | get {
98 | return textAsToken
99 | }
100 | set(newValue) {
101 | setText(newValue, animated: false)
102 | }
103 | }
104 |
105 | /**
106 | Displayed the given text in the split-flap.
107 |
108 | - parameter text: The text to display by the split-flap.
109 | - parameter animated: *true* to animate the text change by rotating the flaps
110 | (component) to the new value; if you specify *false*, the new text is shown
111 | immediately.
112 | - parameter completionBlock: A block called when the animation did finished.
113 | If the text update is not animated the block is called immediately.
114 | */
115 | open func setText(_ text: String?, animated: Bool, completionBlock: (() -> Void)? = nil) {
116 | let completionGroup = DispatchGroup()
117 | let target = (delegate ?? self)
118 | let delay = animated ? 0.181 : 0
119 |
120 | textAsToken = nil
121 |
122 | for (index, flap) in flaps.enumerated() {
123 | var tokens = self.datasource?.tokensInSplitflap(self, flap: index) ?? []
124 | let parser = TokenParser(tokens: tokens)
125 | if let string = text {
126 | tokens = parser.parseString(string)
127 | }
128 |
129 | let token: String? = index < tokens.count ? tokens[index] : nil
130 | let rotationDuration = animated ? target.splitflap(self, rotationDurationForFlapAtIndex: index) : 0
131 |
132 | if let t = token {
133 | textAsToken = textAsToken ?? ""
134 | textAsToken?.append(t)
135 | }
136 |
137 | if animated {
138 | var flapBlock: (() -> ())?
139 |
140 | if completionBlock != nil {
141 | completionGroup.enter()
142 |
143 | flapBlock = {
144 | completionGroup.leave()
145 | }
146 | }
147 |
148 | DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + Double(Int64(index) * Int64(delay * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC), execute: {
149 | flap.displayToken(token, rotationDuration: rotationDuration, completionBlock: flapBlock)
150 | })
151 | }
152 | else {
153 | flap.displayToken(token, rotationDuration: rotationDuration)
154 | }
155 | }
156 |
157 | completionGroup.notify(queue: DispatchQueue.main, execute: {
158 | completionBlock?()
159 | })
160 | }
161 |
162 | // MARK: - Observing View-Related Changes
163 |
164 | /// Tells the view that its window object changed.
165 | open override func didMoveToWindow() {
166 | reload()
167 | }
168 |
169 | // MARK: - Laying out Subviews
170 |
171 | /// Lay out subviews.
172 | open override func layoutSubviews() {
173 | super.layoutSubviews()
174 |
175 | let fNumberOfFlaps = CGFloat(numberOfFlaps)
176 | let widthPerFlap = (bounds.width - flapSpacing * (fNumberOfFlaps - 1)) / fNumberOfFlaps
177 |
178 | for (index, flap) in flaps.enumerated() {
179 | let fIndex = CGFloat(index)
180 | flap.frame = CGRect(x: fIndex * widthPerFlap + flapSpacing * fIndex, y: 0, width: widthPerFlap, height: bounds.height)
181 | }
182 | }
183 |
184 | /// Rebuild and layout the split-flap view.
185 | fileprivate func updateAndLayoutView() {
186 | let targetDelegate = (delegate ?? self)
187 |
188 | var tmp: [FlapView] = []
189 |
190 | for index in 0 ..< numberOfFlaps {
191 | let flap = FlapView(tokens: self.datasource?.tokensInSplitflap(self, flap: index) ?? [], builder: targetDelegate.splitflap(self, builderForFlapAtIndex: index))
192 |
193 | tmp.append(flap)
194 | addSubview(flap)
195 | }
196 |
197 | flaps = tmp
198 |
199 | layoutIfNeeded()
200 |
201 | setText(text, animated: false)
202 | }
203 |
204 | // MARK: - Reloading the Splitflap
205 |
206 | /**
207 | Reloads the split-flap.
208 |
209 | Call this method to reload all the data that is used to construct the split-flap
210 | view. It should not be called in during a animation.
211 | */
212 | open func reload() {
213 | let target = (datasource ?? self)
214 |
215 | numberOfFlaps = target.numberOfFlapsInSplitflap(self)
216 |
217 | updateAndLayoutView()
218 | }
219 | }
220 |
221 | /// Default implementation of SplitflapDataSource
222 | extension Splitflap: SplitflapDataSource {
223 | /// By default the Splitflap object does not have flaps, so returns 0.
224 | public func numberOfFlapsInSplitflap(_ splitflap: Splitflap) -> Int {
225 | return 0
226 | }
227 | }
228 |
229 | /// Default implementation of SplitflapDelegate
230 | extension Splitflap: SplitflapDelegate {
231 | }
232 |
--------------------------------------------------------------------------------
/Sources/FlapView.swift:
--------------------------------------------------------------------------------
1 | /*
2 | * Splitflap
3 | *
4 | * Copyright 2015-present Yannick Loriot.
5 | * http://yannickloriot.com
6 | *
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy
8 | * of this software and associated documentation files (the "Software"), to deal
9 | * in the Software without restriction, including without limitation the rights
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | * copies of the Software, and to permit persons to whom the Software is
12 | * furnished to do so, subject to the following conditions:
13 | *
14 | * The above copyright notice and this permission notice shall be included in
15 | * all copies or substantial portions of the Software.
16 | *
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | * THE SOFTWARE.
24 | *
25 | */
26 |
27 | import UIKit
28 | import QuartzCore
29 |
30 | /**
31 | A Flap view aims to display given tokens with by rotating its tiles to show the
32 | desired character or graphic.
33 | */
34 | final class FlapView: UIView, CAAnimationDelegate {
35 | // The tiles used to display and animate the flaps
36 | fileprivate let topTicTile: TileView
37 | fileprivate let bottomTicTile: TileView
38 | fileprivate let topTacTile: TileView
39 | fileprivate let bottomTacTile: TileView
40 |
41 | // MARK: - Working With Tokens
42 |
43 | let tokens: [String]
44 | fileprivate let tokenGenerator:TokenGenerator
45 | fileprivate var targetToken: String?
46 | fileprivate var targetCompletionBlock: (() -> ())? {
47 | didSet {
48 | oldValue?()
49 | }
50 | }
51 |
52 | // MARK: - Initializing a Flap View
53 |
54 | required init(tokens: [String], builder: FlapViewBuilder) {
55 | self.topTicTile = TileView(builder: builder, position: .top)
56 | self.bottomTicTile = TileView(builder: builder, position: .bottom)
57 | self.topTacTile = TileView(builder: builder, position: .top)
58 | self.bottomTacTile = TileView(builder: builder, position: .bottom)
59 |
60 | self.tokens = tokens
61 | self.tokenGenerator = TokenGenerator(tokens: tokens)
62 |
63 | super.init(frame: CGRect.zero)
64 |
65 | setupViews()
66 | setupAnimations()
67 | }
68 |
69 | required init?(coder aDecoder: NSCoder) {
70 | topTicTile = TileView(builder: FlapViewBuilder(), position: .top)
71 | bottomTicTile = TileView(builder: FlapViewBuilder(), position: .bottom)
72 | topTacTile = TileView(builder: FlapViewBuilder(), position: .top)
73 | bottomTacTile = TileView(builder: FlapViewBuilder(), position: .bottom)
74 | tokens = []
75 | tokenGenerator = TokenGenerator(tokens: [])
76 |
77 | super.init(coder: aDecoder)
78 | }
79 |
80 | // MARK: - Laying out Subviews
81 |
82 | override func layoutSubviews() {
83 | super.layoutSubviews()
84 |
85 | let topLeafFrame = CGRect(x: 0, y: 0, width: bounds.width, height: bounds.height / 2)
86 | let bottomLeafFrame = CGRect(x: 0, y: bounds.height / 2, width: bounds.width, height: bounds.height / 2)
87 |
88 | topTicTile.frame = topLeafFrame
89 | bottomTicTile.frame = bottomLeafFrame
90 | topTacTile.frame = topLeafFrame
91 | bottomTacTile.frame = bottomLeafFrame
92 | }
93 |
94 | // MARK: - Initializing the Flap View
95 |
96 | fileprivate func setupViews() {
97 | addSubview(topTicTile)
98 | addSubview(bottomTicTile)
99 | addSubview(topTacTile)
100 | addSubview(bottomTacTile)
101 |
102 | topTicTile.layer.anchorPoint = CGPoint(x: 0.5, y: 1.0)
103 | bottomTicTile.layer.anchorPoint = CGPoint(x: 0.5, y: 0)
104 | topTacTile.layer.anchorPoint = CGPoint(x: 0.5, y: 1.0)
105 | bottomTacTile.layer.anchorPoint = CGPoint(x: 0.5, y: 0)
106 |
107 | updateWithToken(tokenGenerator.firstToken, animated: false)
108 | }
109 |
110 | // MARK: - Settings the Animations
111 |
112 | /// Defines the current time of the animation to know which tile to display.
113 | fileprivate enum AnimationTime {
114 | /// Tic time.
115 | case tic
116 | /// Tac time.
117 | case tac
118 | }
119 |
120 | fileprivate var animationTime = AnimationTime.tac
121 | fileprivate let topAnim = CABasicAnimation(keyPath: "transform")
122 | fileprivate let bottomAnim = CABasicAnimation(keyPath: "transform")
123 |
124 | fileprivate func setupAnimations() {
125 | // Set the perspective
126 | let zDepth: CGFloat = 1000
127 | var skewedIdentityTransform = CATransform3DIdentity
128 | skewedIdentityTransform.m34 = 1 / -zDepth
129 |
130 | // Predefine the animation
131 | topAnim.fromValue = NSValue(caTransform3D: skewedIdentityTransform)
132 | topAnim.toValue = NSValue(caTransform3D: CATransform3DRotate(skewedIdentityTransform, CGFloat.pi / -2, 1, 0, 0))
133 | topAnim.isRemovedOnCompletion = false
134 | topAnim.fillMode = CAMediaTimingFillMode.forwards
135 | topAnim.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeIn)
136 |
137 | bottomAnim.fromValue = NSValue(caTransform3D: CATransform3DRotate(skewedIdentityTransform, CGFloat.pi / 2, 1, 0, 0))
138 | bottomAnim.toValue = NSValue(caTransform3D: skewedIdentityTransform)
139 | bottomAnim.delegate = self
140 | bottomAnim.isRemovedOnCompletion = true
141 | bottomAnim.fillMode = CAMediaTimingFillMode.both
142 | bottomAnim.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeOut)
143 | }
144 |
145 | // MARK: - Animating the Flap View
146 |
147 | /**
148 | Display the given token.
149 |
150 | - parameter token: A token string.
151 | - parameter rotationDuration: If upper than 0, it animates the change.
152 | - parameter completionBlock: A block called when the animation did finished.
153 | If the text update is not animated the block is called immediately.
154 | */
155 | func displayToken(_ token: String?, rotationDuration: Double, completionBlock: (() -> Void)? = nil) {
156 | let sanitizedToken = token ?? tokenGenerator.firstToken
157 |
158 | if rotationDuration > 0 {
159 | topAnim.duration = rotationDuration / 4 * 3
160 | bottomAnim.duration = rotationDuration / 4
161 |
162 | let animating = targetToken != nil
163 |
164 | targetToken = sanitizedToken
165 | targetCompletionBlock = completionBlock
166 |
167 | if !animating {
168 | displayNextToken()
169 | }
170 | }
171 | else {
172 | tokenGenerator.currentElement = sanitizedToken
173 |
174 | updateWithToken(sanitizedToken, animated: false)
175 |
176 | completionBlock?()
177 | }
178 | }
179 |
180 | /**
181 | Method used in conjunction with the `animationDidStop:finished:` callback in
182 | order to display all the tokens between the current one and the target one.
183 | */
184 | fileprivate func displayNextToken() {
185 | guard tokenGenerator.currentElement != targetToken && targetToken != nil else {
186 | targetToken = nil
187 | targetCompletionBlock = nil
188 |
189 | return
190 | }
191 |
192 | if let token = tokenGenerator.next() {
193 | updateWithToken(token, animated: true)
194 | }
195 | }
196 |
197 | /// Display the given token. If animated it rotate the flaps.
198 | fileprivate func updateWithToken(_ token: String?, animated: Bool) {
199 | let topBack = animationTime == .tic ? topTicTile : topTacTile
200 | let bottomBack = animationTime == .tic ? bottomTicTile : bottomTacTile
201 | let topFront = animationTime == .tic ? topTacTile : topTicTile
202 |
203 | topBack.setSymbol(token)
204 | bottomBack.setSymbol(token)
205 |
206 | topBack.layer.removeAllAnimations()
207 | bottomBack.layer.removeAllAnimations()
208 | topFront.layer.removeAllAnimations()
209 |
210 | if animated {
211 | bringSubviewToFront(topFront)
212 | bringSubviewToFront(bottomBack)
213 |
214 | // Animation
215 | topAnim.beginTime = CACurrentMediaTime()
216 | topFront.layer.add(topAnim, forKey: "topDownFlip")
217 |
218 | bottomAnim.beginTime = topAnim.beginTime + topAnim.duration
219 | bottomBack.layer.add(bottomAnim, forKey: "bottomDownFlip")
220 | }
221 | else {
222 | bringSubviewToFront(topBack)
223 | bringSubviewToFront(bottomBack)
224 |
225 | animationTime = animationTime == .tic ? .tac : .tic
226 | }
227 | }
228 |
229 | // MARK: - CAAnimation Delegate Methods
230 |
231 | func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {
232 | animationTime = animationTime == .tic ? .tac : .tic
233 |
234 | displayNextToken()
235 | }
236 | }
237 |
--------------------------------------------------------------------------------
/Example/SplitflapExample.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | CE50405D1C050024007D9E9F /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE50405C1C050024007D9E9F /* AppDelegate.swift */; };
11 | CE50405F1C050024007D9E9F /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE50405E1C050024007D9E9F /* ViewController.swift */; };
12 | CE5040621C050024007D9E9F /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = CE5040601C050024007D9E9F /* Main.storyboard */; };
13 | CE5040641C050024007D9E9F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = CE5040631C050024007D9E9F /* Assets.xcassets */; };
14 | CE525D8B1BF3833700429200 /* Splitflap.h in Headers */ = {isa = PBXBuildFile; fileRef = CE525D8A1BF3833700429200 /* Splitflap.h */; settings = {ATTRIBUTES = (Public, ); }; };
15 | CE525D8F1BF3833700429200 /* Splitflap.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CE525D881BF3833700429200 /* Splitflap.framework */; };
16 | CE525D901BF3833700429200 /* Splitflap.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = CE525D881BF3833700429200 /* Splitflap.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
17 | CEB139A41BF266DD00DE6BA9 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEB139A31BF266DD00DE6BA9 /* AppDelegate.swift */; };
18 | CEB139A61BF266DD00DE6BA9 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEB139A51BF266DD00DE6BA9 /* ViewController.swift */; };
19 | CEB139A91BF266DD00DE6BA9 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = CEB139A71BF266DD00DE6BA9 /* Main.storyboard */; };
20 | CEB139AB1BF266DD00DE6BA9 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = CEB139AA1BF266DD00DE6BA9 /* Assets.xcassets */; };
21 | CEB139AE1BF266DD00DE6BA9 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = CEB139AC1BF266DD00DE6BA9 /* LaunchScreen.storyboard */; };
22 | CEE3408D1D8AEA4600FF580D /* FlapView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEE340841D8AEA4600FF580D /* FlapView.swift */; };
23 | CEE3408E1D8AEA4600FF580D /* FlapViewBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEE340851D8AEA4600FF580D /* FlapViewBuilder.swift */; };
24 | CEE3408F1D8AEA4600FF580D /* Splitflap.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEE340861D8AEA4600FF580D /* Splitflap.swift */; };
25 | CEE340901D8AEA4600FF580D /* SplitflapDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEE340871D8AEA4600FF580D /* SplitflapDataSource.swift */; };
26 | CEE340911D8AEA4600FF580D /* SplitflapDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEE340881D8AEA4600FF580D /* SplitflapDelegate.swift */; };
27 | CEE340921D8AEA4600FF580D /* SplitflapTokens.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEE340891D8AEA4600FF580D /* SplitflapTokens.swift */; };
28 | CEE340931D8AEA4600FF580D /* TileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEE3408A1D8AEA4600FF580D /* TileView.swift */; };
29 | CEE340941D8AEA4600FF580D /* TokenGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEE3408B1D8AEA4600FF580D /* TokenGenerator.swift */; };
30 | CEE340951D8AEA4600FF580D /* TokenParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEE3408C1D8AEA4600FF580D /* TokenParser.swift */; };
31 | CEE340961D8AEA4900FF580D /* FlapView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEE340841D8AEA4600FF580D /* FlapView.swift */; };
32 | CEE340971D8AEA4900FF580D /* FlapViewBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEE340851D8AEA4600FF580D /* FlapViewBuilder.swift */; };
33 | CEE340981D8AEA4900FF580D /* Splitflap.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEE340861D8AEA4600FF580D /* Splitflap.swift */; };
34 | CEE340991D8AEA4900FF580D /* SplitflapDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEE340871D8AEA4600FF580D /* SplitflapDataSource.swift */; };
35 | CEE3409A1D8AEA4900FF580D /* SplitflapDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEE340881D8AEA4600FF580D /* SplitflapDelegate.swift */; };
36 | CEE3409B1D8AEA4900FF580D /* SplitflapTokens.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEE340891D8AEA4600FF580D /* SplitflapTokens.swift */; };
37 | CEE3409C1D8AEA4900FF580D /* TileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEE3408A1D8AEA4600FF580D /* TileView.swift */; };
38 | CEE3409D1D8AEA4900FF580D /* TokenGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEE3408B1D8AEA4600FF580D /* TokenGenerator.swift */; };
39 | CEE3409E1D8AEA4900FF580D /* TokenParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEE3408C1D8AEA4600FF580D /* TokenParser.swift */; };
40 | CEE3409F1D8AEA4A00FF580D /* FlapView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEE340841D8AEA4600FF580D /* FlapView.swift */; };
41 | CEE340A01D8AEA4A00FF580D /* FlapViewBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEE340851D8AEA4600FF580D /* FlapViewBuilder.swift */; };
42 | CEE340A11D8AEA4A00FF580D /* Splitflap.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEE340861D8AEA4600FF580D /* Splitflap.swift */; };
43 | CEE340A21D8AEA4A00FF580D /* SplitflapDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEE340871D8AEA4600FF580D /* SplitflapDataSource.swift */; };
44 | CEE340A31D8AEA4A00FF580D /* SplitflapDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEE340881D8AEA4600FF580D /* SplitflapDelegate.swift */; };
45 | CEE340A41D8AEA4A00FF580D /* SplitflapTokens.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEE340891D8AEA4600FF580D /* SplitflapTokens.swift */; };
46 | CEE340A51D8AEA4A00FF580D /* TileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEE3408A1D8AEA4600FF580D /* TileView.swift */; };
47 | CEE340A61D8AEA4A00FF580D /* TokenGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEE3408B1D8AEA4600FF580D /* TokenGenerator.swift */; };
48 | CEE340A71D8AEA4A00FF580D /* TokenParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEE3408C1D8AEA4600FF580D /* TokenParser.swift */; };
49 | CEE340A81D8AEA4A00FF580D /* FlapView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEE340841D8AEA4600FF580D /* FlapView.swift */; };
50 | CEE340A91D8AEA4A00FF580D /* FlapViewBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEE340851D8AEA4600FF580D /* FlapViewBuilder.swift */; };
51 | CEE340AA1D8AEA4A00FF580D /* Splitflap.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEE340861D8AEA4600FF580D /* Splitflap.swift */; };
52 | CEE340AB1D8AEA4A00FF580D /* SplitflapDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEE340871D8AEA4600FF580D /* SplitflapDataSource.swift */; };
53 | CEE340AC1D8AEA4A00FF580D /* SplitflapDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEE340881D8AEA4600FF580D /* SplitflapDelegate.swift */; };
54 | CEE340AD1D8AEA4A00FF580D /* SplitflapTokens.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEE340891D8AEA4600FF580D /* SplitflapTokens.swift */; };
55 | CEE340AE1D8AEA4A00FF580D /* TileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEE3408A1D8AEA4600FF580D /* TileView.swift */; };
56 | CEE340AF1D8AEA4A00FF580D /* TokenGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEE3408B1D8AEA4600FF580D /* TokenGenerator.swift */; };
57 | CEE340B01D8AEA4A00FF580D /* TokenParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEE3408C1D8AEA4600FF580D /* TokenParser.swift */; };
58 | CEE340BA1D8AEA5F00FF580D /* FlapViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEE340B11D8AEA5F00FF580D /* FlapViewTests.swift */; };
59 | CEE340BB1D8AEA5F00FF580D /* SplitflapDataSourceDatasourceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEE340B21D8AEA5F00FF580D /* SplitflapDataSourceDatasourceTests.swift */; };
60 | CEE340BC1D8AEA5F00FF580D /* SplitflapDelegateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEE340B31D8AEA5F00FF580D /* SplitflapDelegateTests.swift */; };
61 | CEE340BD1D8AEA5F00FF580D /* SplitflapTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEE340B41D8AEA5F00FF580D /* SplitflapTests.swift */; };
62 | CEE340BE1D8AEA5F00FF580D /* StoryboardTests.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = CEE340B51D8AEA5F00FF580D /* StoryboardTests.storyboard */; };
63 | CEE340BF1D8AEA5F00FF580D /* TileViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEE340B61D8AEA5F00FF580D /* TileViewTests.swift */; };
64 | CEE340C01D8AEA5F00FF580D /* TokenGeneratorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEE340B71D8AEA5F00FF580D /* TokenGeneratorTests.swift */; };
65 | CEE340C11D8AEA5F00FF580D /* TokenParserTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEE340B81D8AEA5F00FF580D /* TokenParserTests.swift */; };
66 | CEE340C21D8AEA5F00FF580D /* XCTTestCaseTemplate.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEE340B91D8AEA5F00FF580D /* XCTTestCaseTemplate.swift */; };
67 | /* End PBXBuildFile section */
68 |
69 | /* Begin PBXContainerItemProxy section */
70 | CE525D8D1BF3833700429200 /* PBXContainerItemProxy */ = {
71 | isa = PBXContainerItemProxy;
72 | containerPortal = CEB139981BF266DD00DE6BA9 /* Project object */;
73 | proxyType = 1;
74 | remoteGlobalIDString = CE525D871BF3833700429200;
75 | remoteInfo = Splitflap;
76 | };
77 | CE525DB21BF4FBC800429200 /* PBXContainerItemProxy */ = {
78 | isa = PBXContainerItemProxy;
79 | containerPortal = CEB139981BF266DD00DE6BA9 /* Project object */;
80 | proxyType = 1;
81 | remoteGlobalIDString = CEB1399F1BF266DD00DE6BA9;
82 | remoteInfo = SplitflapExample;
83 | };
84 | /* End PBXContainerItemProxy section */
85 |
86 | /* Begin PBXCopyFilesBuildPhase section */
87 | CE525D941BF3833700429200 /* Embed Frameworks */ = {
88 | isa = PBXCopyFilesBuildPhase;
89 | buildActionMask = 2147483647;
90 | dstPath = "";
91 | dstSubfolderSpec = 10;
92 | files = (
93 | CE525D901BF3833700429200 /* Splitflap.framework in Embed Frameworks */,
94 | );
95 | name = "Embed Frameworks";
96 | runOnlyForDeploymentPostprocessing = 0;
97 | };
98 | /* End PBXCopyFilesBuildPhase section */
99 |
100 | /* Begin PBXFileReference section */
101 | CE50405A1C050024007D9E9F /* tvOSExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = tvOSExample.app; sourceTree = BUILT_PRODUCTS_DIR; };
102 | CE50405C1C050024007D9E9F /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
103 | CE50405E1C050024007D9E9F /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; };
104 | CE5040611C050024007D9E9F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
105 | CE5040631C050024007D9E9F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
106 | CE5040651C050024007D9E9F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
107 | CE525D881BF3833700429200 /* Splitflap.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Splitflap.framework; sourceTree = BUILT_PRODUCTS_DIR; };
108 | CE525D8A1BF3833700429200 /* Splitflap.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Splitflap.h; sourceTree = ""; };
109 | CE525D8C1BF3833700429200 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
110 | CE525DAD1BF4FBC800429200 /* SplitflapTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SplitflapTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
111 | CE525DB11BF4FBC800429200 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
112 | CEB139A01BF266DD00DE6BA9 /* SplitflapExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SplitflapExample.app; sourceTree = BUILT_PRODUCTS_DIR; };
113 | CEB139A31BF266DD00DE6BA9 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
114 | CEB139A51BF266DD00DE6BA9 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; };
115 | CEB139A81BF266DD00DE6BA9 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
116 | CEB139AA1BF266DD00DE6BA9 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
117 | CEB139AD1BF266DD00DE6BA9 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
118 | CEB139AF1BF266DD00DE6BA9 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
119 | CEE340841D8AEA4600FF580D /* FlapView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FlapView.swift; sourceTree = ""; };
120 | CEE340851D8AEA4600FF580D /* FlapViewBuilder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FlapViewBuilder.swift; sourceTree = ""; };
121 | CEE340861D8AEA4600FF580D /* Splitflap.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Splitflap.swift; sourceTree = ""; };
122 | CEE340871D8AEA4600FF580D /* SplitflapDataSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SplitflapDataSource.swift; sourceTree = ""; };
123 | CEE340881D8AEA4600FF580D /* SplitflapDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SplitflapDelegate.swift; sourceTree = ""; };
124 | CEE340891D8AEA4600FF580D /* SplitflapTokens.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SplitflapTokens.swift; sourceTree = ""; };
125 | CEE3408A1D8AEA4600FF580D /* TileView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TileView.swift; sourceTree = ""; };
126 | CEE3408B1D8AEA4600FF580D /* TokenGenerator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TokenGenerator.swift; sourceTree = ""; };
127 | CEE3408C1D8AEA4600FF580D /* TokenParser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TokenParser.swift; sourceTree = ""; };
128 | CEE340B11D8AEA5F00FF580D /* FlapViewTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FlapViewTests.swift; path = ../../Tests/FlapViewTests.swift; sourceTree = ""; };
129 | CEE340B21D8AEA5F00FF580D /* SplitflapDataSourceDatasourceTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SplitflapDataSourceDatasourceTests.swift; path = ../../Tests/SplitflapDataSourceDatasourceTests.swift; sourceTree = ""; };
130 | CEE340B31D8AEA5F00FF580D /* SplitflapDelegateTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SplitflapDelegateTests.swift; path = ../../Tests/SplitflapDelegateTests.swift; sourceTree = ""; };
131 | CEE340B41D8AEA5F00FF580D /* SplitflapTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SplitflapTests.swift; path = ../../Tests/SplitflapTests.swift; sourceTree = ""; };
132 | CEE340B51D8AEA5F00FF580D /* StoryboardTests.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = StoryboardTests.storyboard; path = ../../Tests/StoryboardTests.storyboard; sourceTree = ""; };
133 | CEE340B61D8AEA5F00FF580D /* TileViewTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = TileViewTests.swift; path = ../../Tests/TileViewTests.swift; sourceTree = ""; };
134 | CEE340B71D8AEA5F00FF580D /* TokenGeneratorTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = TokenGeneratorTests.swift; path = ../../Tests/TokenGeneratorTests.swift; sourceTree = ""; };
135 | CEE340B81D8AEA5F00FF580D /* TokenParserTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = TokenParserTests.swift; path = ../../Tests/TokenParserTests.swift; sourceTree = ""; };
136 | CEE340B91D8AEA5F00FF580D /* XCTTestCaseTemplate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = XCTTestCaseTemplate.swift; path = ../../Tests/XCTTestCaseTemplate.swift; sourceTree = ""; };
137 | /* End PBXFileReference section */
138 |
139 | /* Begin PBXFrameworksBuildPhase section */
140 | CE5040571C050024007D9E9F /* Frameworks */ = {
141 | isa = PBXFrameworksBuildPhase;
142 | buildActionMask = 2147483647;
143 | files = (
144 | );
145 | runOnlyForDeploymentPostprocessing = 0;
146 | };
147 | CE525D841BF3833700429200 /* Frameworks */ = {
148 | isa = PBXFrameworksBuildPhase;
149 | buildActionMask = 2147483647;
150 | files = (
151 | );
152 | runOnlyForDeploymentPostprocessing = 0;
153 | };
154 | CE525DAA1BF4FBC800429200 /* Frameworks */ = {
155 | isa = PBXFrameworksBuildPhase;
156 | buildActionMask = 2147483647;
157 | files = (
158 | );
159 | runOnlyForDeploymentPostprocessing = 0;
160 | };
161 | CEB1399D1BF266DD00DE6BA9 /* Frameworks */ = {
162 | isa = PBXFrameworksBuildPhase;
163 | buildActionMask = 2147483647;
164 | files = (
165 | CE525D8F1BF3833700429200 /* Splitflap.framework in Frameworks */,
166 | );
167 | runOnlyForDeploymentPostprocessing = 0;
168 | };
169 | /* End PBXFrameworksBuildPhase section */
170 |
171 | /* Begin PBXGroup section */
172 | CE50405B1C050024007D9E9F /* tvOSExample */ = {
173 | isa = PBXGroup;
174 | children = (
175 | CE50405C1C050024007D9E9F /* AppDelegate.swift */,
176 | CE50405E1C050024007D9E9F /* ViewController.swift */,
177 | CE5040601C050024007D9E9F /* Main.storyboard */,
178 | CE5040631C050024007D9E9F /* Assets.xcassets */,
179 | CE5040651C050024007D9E9F /* Info.plist */,
180 | );
181 | path = tvOSExample;
182 | sourceTree = "";
183 | };
184 | CE525D891BF3833700429200 /* Splitflap */ = {
185 | isa = PBXGroup;
186 | children = (
187 | CE525D8A1BF3833700429200 /* Splitflap.h */,
188 | CE525D8C1BF3833700429200 /* Info.plist */,
189 | );
190 | path = Splitflap;
191 | sourceTree = "";
192 | };
193 | CE525DAE1BF4FBC800429200 /* SplitflapTests */ = {
194 | isa = PBXGroup;
195 | children = (
196 | CE525DB11BF4FBC800429200 /* Info.plist */,
197 | CEE340B11D8AEA5F00FF580D /* FlapViewTests.swift */,
198 | CEE340B21D8AEA5F00FF580D /* SplitflapDataSourceDatasourceTests.swift */,
199 | CEE340B31D8AEA5F00FF580D /* SplitflapDelegateTests.swift */,
200 | CEE340B41D8AEA5F00FF580D /* SplitflapTests.swift */,
201 | CEE340B51D8AEA5F00FF580D /* StoryboardTests.storyboard */,
202 | CEE340B61D8AEA5F00FF580D /* TileViewTests.swift */,
203 | CEE340B71D8AEA5F00FF580D /* TokenGeneratorTests.swift */,
204 | CEE340B81D8AEA5F00FF580D /* TokenParserTests.swift */,
205 | CEE340B91D8AEA5F00FF580D /* XCTTestCaseTemplate.swift */,
206 | );
207 | path = SplitflapTests;
208 | sourceTree = "";
209 | };
210 | CEB139971BF266DD00DE6BA9 = {
211 | isa = PBXGroup;
212 | children = (
213 | CEE340831D8AEA4600FF580D /* Splitflap */,
214 | CEB139A21BF266DD00DE6BA9 /* SplitflapExample */,
215 | CE525D891BF3833700429200 /* Splitflap */,
216 | CE525DAE1BF4FBC800429200 /* SplitflapTests */,
217 | CE50405B1C050024007D9E9F /* tvOSExample */,
218 | CEB139A11BF266DD00DE6BA9 /* Products */,
219 | );
220 | sourceTree = "";
221 | };
222 | CEB139A11BF266DD00DE6BA9 /* Products */ = {
223 | isa = PBXGroup;
224 | children = (
225 | CEB139A01BF266DD00DE6BA9 /* SplitflapExample.app */,
226 | CE525D881BF3833700429200 /* Splitflap.framework */,
227 | CE525DAD1BF4FBC800429200 /* SplitflapTests.xctest */,
228 | CE50405A1C050024007D9E9F /* tvOSExample.app */,
229 | );
230 | name = Products;
231 | sourceTree = "";
232 | };
233 | CEB139A21BF266DD00DE6BA9 /* SplitflapExample */ = {
234 | isa = PBXGroup;
235 | children = (
236 | CEB139A31BF266DD00DE6BA9 /* AppDelegate.swift */,
237 | CEB139A51BF266DD00DE6BA9 /* ViewController.swift */,
238 | CEB139A71BF266DD00DE6BA9 /* Main.storyboard */,
239 | CEB139AA1BF266DD00DE6BA9 /* Assets.xcassets */,
240 | CEB139AC1BF266DD00DE6BA9 /* LaunchScreen.storyboard */,
241 | CEB139AF1BF266DD00DE6BA9 /* Info.plist */,
242 | );
243 | path = SplitflapExample;
244 | sourceTree = "";
245 | };
246 | CEE340831D8AEA4600FF580D /* Splitflap */ = {
247 | isa = PBXGroup;
248 | children = (
249 | CEE340841D8AEA4600FF580D /* FlapView.swift */,
250 | CEE340851D8AEA4600FF580D /* FlapViewBuilder.swift */,
251 | CEE340861D8AEA4600FF580D /* Splitflap.swift */,
252 | CEE340871D8AEA4600FF580D /* SplitflapDataSource.swift */,
253 | CEE340881D8AEA4600FF580D /* SplitflapDelegate.swift */,
254 | CEE340891D8AEA4600FF580D /* SplitflapTokens.swift */,
255 | CEE3408A1D8AEA4600FF580D /* TileView.swift */,
256 | CEE3408B1D8AEA4600FF580D /* TokenGenerator.swift */,
257 | CEE3408C1D8AEA4600FF580D /* TokenParser.swift */,
258 | );
259 | name = Splitflap;
260 | path = ../Sources;
261 | sourceTree = "";
262 | };
263 | /* End PBXGroup section */
264 |
265 | /* Begin PBXHeadersBuildPhase section */
266 | CE525D851BF3833700429200 /* Headers */ = {
267 | isa = PBXHeadersBuildPhase;
268 | buildActionMask = 2147483647;
269 | files = (
270 | CE525D8B1BF3833700429200 /* Splitflap.h in Headers */,
271 | );
272 | runOnlyForDeploymentPostprocessing = 0;
273 | };
274 | /* End PBXHeadersBuildPhase section */
275 |
276 | /* Begin PBXNativeTarget section */
277 | CE5040591C050024007D9E9F /* tvOSExample */ = {
278 | isa = PBXNativeTarget;
279 | buildConfigurationList = CE5040681C050024007D9E9F /* Build configuration list for PBXNativeTarget "tvOSExample" */;
280 | buildPhases = (
281 | CE5040561C050024007D9E9F /* Sources */,
282 | CE5040571C050024007D9E9F /* Frameworks */,
283 | CE5040581C050024007D9E9F /* Resources */,
284 | );
285 | buildRules = (
286 | );
287 | dependencies = (
288 | );
289 | name = tvOSExample;
290 | productName = tvOSExample;
291 | productReference = CE50405A1C050024007D9E9F /* tvOSExample.app */;
292 | productType = "com.apple.product-type.application";
293 | };
294 | CE525D871BF3833700429200 /* Splitflap */ = {
295 | isa = PBXNativeTarget;
296 | buildConfigurationList = CE525D911BF3833700429200 /* Build configuration list for PBXNativeTarget "Splitflap" */;
297 | buildPhases = (
298 | CE525D831BF3833700429200 /* Sources */,
299 | CE525D841BF3833700429200 /* Frameworks */,
300 | CE525D851BF3833700429200 /* Headers */,
301 | CE525D861BF3833700429200 /* Resources */,
302 | );
303 | buildRules = (
304 | );
305 | dependencies = (
306 | );
307 | name = Splitflap;
308 | productName = Splitflap;
309 | productReference = CE525D881BF3833700429200 /* Splitflap.framework */;
310 | productType = "com.apple.product-type.framework";
311 | };
312 | CE525DAC1BF4FBC800429200 /* SplitflapTests */ = {
313 | isa = PBXNativeTarget;
314 | buildConfigurationList = CE525DB61BF4FBC800429200 /* Build configuration list for PBXNativeTarget "SplitflapTests" */;
315 | buildPhases = (
316 | CE525DA91BF4FBC800429200 /* Sources */,
317 | CE525DAA1BF4FBC800429200 /* Frameworks */,
318 | CE525DAB1BF4FBC800429200 /* Resources */,
319 | );
320 | buildRules = (
321 | );
322 | dependencies = (
323 | CE525DB31BF4FBC800429200 /* PBXTargetDependency */,
324 | );
325 | name = SplitflapTests;
326 | productName = SplitflapTests;
327 | productReference = CE525DAD1BF4FBC800429200 /* SplitflapTests.xctest */;
328 | productType = "com.apple.product-type.bundle.unit-test";
329 | };
330 | CEB1399F1BF266DD00DE6BA9 /* SplitflapExample */ = {
331 | isa = PBXNativeTarget;
332 | buildConfigurationList = CEB139B21BF266DD00DE6BA9 /* Build configuration list for PBXNativeTarget "SplitflapExample" */;
333 | buildPhases = (
334 | CEB1399C1BF266DD00DE6BA9 /* Sources */,
335 | CEB1399D1BF266DD00DE6BA9 /* Frameworks */,
336 | CEB1399E1BF266DD00DE6BA9 /* Resources */,
337 | CE525D941BF3833700429200 /* Embed Frameworks */,
338 | );
339 | buildRules = (
340 | );
341 | dependencies = (
342 | CE525D8E1BF3833700429200 /* PBXTargetDependency */,
343 | );
344 | name = SplitflapExample;
345 | productName = SplitflapExample;
346 | productReference = CEB139A01BF266DD00DE6BA9 /* SplitflapExample.app */;
347 | productType = "com.apple.product-type.application";
348 | };
349 | /* End PBXNativeTarget section */
350 |
351 | /* Begin PBXProject section */
352 | CEB139981BF266DD00DE6BA9 /* Project object */ = {
353 | isa = PBXProject;
354 | attributes = {
355 | LastSwiftUpdateCheck = 0710;
356 | LastUpgradeCheck = 1020;
357 | ORGANIZATIONNAME = "Yannick LORIOT";
358 | TargetAttributes = {
359 | CE5040591C050024007D9E9F = {
360 | CreatedOnToolsVersion = 7.1.1;
361 | LastSwiftMigration = 0800;
362 | };
363 | CE525D871BF3833700429200 = {
364 | CreatedOnToolsVersion = 7.1;
365 | LastSwiftMigration = 1020;
366 | };
367 | CE525DAC1BF4FBC800429200 = {
368 | CreatedOnToolsVersion = 7.1;
369 | LastSwiftMigration = 0800;
370 | };
371 | CEB1399F1BF266DD00DE6BA9 = {
372 | CreatedOnToolsVersion = 7.1;
373 | LastSwiftMigration = 1020;
374 | };
375 | };
376 | };
377 | buildConfigurationList = CEB1399B1BF266DD00DE6BA9 /* Build configuration list for PBXProject "SplitflapExample" */;
378 | compatibilityVersion = "Xcode 3.2";
379 | developmentRegion = en;
380 | hasScannedForEncodings = 0;
381 | knownRegions = (
382 | en,
383 | Base,
384 | );
385 | mainGroup = CEB139971BF266DD00DE6BA9;
386 | productRefGroup = CEB139A11BF266DD00DE6BA9 /* Products */;
387 | projectDirPath = "";
388 | projectRoot = "";
389 | targets = (
390 | CEB1399F1BF266DD00DE6BA9 /* SplitflapExample */,
391 | CE525DAC1BF4FBC800429200 /* SplitflapTests */,
392 | CE525D871BF3833700429200 /* Splitflap */,
393 | CE5040591C050024007D9E9F /* tvOSExample */,
394 | );
395 | };
396 | /* End PBXProject section */
397 |
398 | /* Begin PBXResourcesBuildPhase section */
399 | CE5040581C050024007D9E9F /* Resources */ = {
400 | isa = PBXResourcesBuildPhase;
401 | buildActionMask = 2147483647;
402 | files = (
403 | CE5040641C050024007D9E9F /* Assets.xcassets in Resources */,
404 | CE5040621C050024007D9E9F /* Main.storyboard in Resources */,
405 | );
406 | runOnlyForDeploymentPostprocessing = 0;
407 | };
408 | CE525D861BF3833700429200 /* Resources */ = {
409 | isa = PBXResourcesBuildPhase;
410 | buildActionMask = 2147483647;
411 | files = (
412 | );
413 | runOnlyForDeploymentPostprocessing = 0;
414 | };
415 | CE525DAB1BF4FBC800429200 /* Resources */ = {
416 | isa = PBXResourcesBuildPhase;
417 | buildActionMask = 2147483647;
418 | files = (
419 | CEE340BE1D8AEA5F00FF580D /* StoryboardTests.storyboard in Resources */,
420 | );
421 | runOnlyForDeploymentPostprocessing = 0;
422 | };
423 | CEB1399E1BF266DD00DE6BA9 /* Resources */ = {
424 | isa = PBXResourcesBuildPhase;
425 | buildActionMask = 2147483647;
426 | files = (
427 | CEB139AE1BF266DD00DE6BA9 /* LaunchScreen.storyboard in Resources */,
428 | CEB139AB1BF266DD00DE6BA9 /* Assets.xcassets in Resources */,
429 | CEB139A91BF266DD00DE6BA9 /* Main.storyboard in Resources */,
430 | );
431 | runOnlyForDeploymentPostprocessing = 0;
432 | };
433 | /* End PBXResourcesBuildPhase section */
434 |
435 | /* Begin PBXSourcesBuildPhase section */
436 | CE5040561C050024007D9E9F /* Sources */ = {
437 | isa = PBXSourcesBuildPhase;
438 | buildActionMask = 2147483647;
439 | files = (
440 | CE50405F1C050024007D9E9F /* ViewController.swift in Sources */,
441 | CEE340A91D8AEA4A00FF580D /* FlapViewBuilder.swift in Sources */,
442 | CEE340AC1D8AEA4A00FF580D /* SplitflapDelegate.swift in Sources */,
443 | CEE340AD1D8AEA4A00FF580D /* SplitflapTokens.swift in Sources */,
444 | CEE340AE1D8AEA4A00FF580D /* TileView.swift in Sources */,
445 | CEE340AF1D8AEA4A00FF580D /* TokenGenerator.swift in Sources */,
446 | CEE340B01D8AEA4A00FF580D /* TokenParser.swift in Sources */,
447 | CEE340AB1D8AEA4A00FF580D /* SplitflapDataSource.swift in Sources */,
448 | CE50405D1C050024007D9E9F /* AppDelegate.swift in Sources */,
449 | CEE340A81D8AEA4A00FF580D /* FlapView.swift in Sources */,
450 | CEE340AA1D8AEA4A00FF580D /* Splitflap.swift in Sources */,
451 | );
452 | runOnlyForDeploymentPostprocessing = 0;
453 | };
454 | CE525D831BF3833700429200 /* Sources */ = {
455 | isa = PBXSourcesBuildPhase;
456 | buildActionMask = 2147483647;
457 | files = (
458 | CEE340A11D8AEA4A00FF580D /* Splitflap.swift in Sources */,
459 | CEE340A51D8AEA4A00FF580D /* TileView.swift in Sources */,
460 | CEE3409F1D8AEA4A00FF580D /* FlapView.swift in Sources */,
461 | CEE340A21D8AEA4A00FF580D /* SplitflapDataSource.swift in Sources */,
462 | CEE340A71D8AEA4A00FF580D /* TokenParser.swift in Sources */,
463 | CEE340A01D8AEA4A00FF580D /* FlapViewBuilder.swift in Sources */,
464 | CEE340A31D8AEA4A00FF580D /* SplitflapDelegate.swift in Sources */,
465 | CEE340A41D8AEA4A00FF580D /* SplitflapTokens.swift in Sources */,
466 | CEE340A61D8AEA4A00FF580D /* TokenGenerator.swift in Sources */,
467 | );
468 | runOnlyForDeploymentPostprocessing = 0;
469 | };
470 | CE525DA91BF4FBC800429200 /* Sources */ = {
471 | isa = PBXSourcesBuildPhase;
472 | buildActionMask = 2147483647;
473 | files = (
474 | CEE340C01D8AEA5F00FF580D /* TokenGeneratorTests.swift in Sources */,
475 | CEE3409B1D8AEA4900FF580D /* SplitflapTokens.swift in Sources */,
476 | CEE340C11D8AEA5F00FF580D /* TokenParserTests.swift in Sources */,
477 | CEE340981D8AEA4900FF580D /* Splitflap.swift in Sources */,
478 | CEE340BD1D8AEA5F00FF580D /* SplitflapTests.swift in Sources */,
479 | CEE340991D8AEA4900FF580D /* SplitflapDataSource.swift in Sources */,
480 | CEE340BA1D8AEA5F00FF580D /* FlapViewTests.swift in Sources */,
481 | CEE340971D8AEA4900FF580D /* FlapViewBuilder.swift in Sources */,
482 | CEE3409A1D8AEA4900FF580D /* SplitflapDelegate.swift in Sources */,
483 | CEE3409D1D8AEA4900FF580D /* TokenGenerator.swift in Sources */,
484 | CEE340961D8AEA4900FF580D /* FlapView.swift in Sources */,
485 | CEE340BC1D8AEA5F00FF580D /* SplitflapDelegateTests.swift in Sources */,
486 | CEE340BF1D8AEA5F00FF580D /* TileViewTests.swift in Sources */,
487 | CEE3409C1D8AEA4900FF580D /* TileView.swift in Sources */,
488 | CEE340C21D8AEA5F00FF580D /* XCTTestCaseTemplate.swift in Sources */,
489 | CEE340BB1D8AEA5F00FF580D /* SplitflapDataSourceDatasourceTests.swift in Sources */,
490 | CEE3409E1D8AEA4900FF580D /* TokenParser.swift in Sources */,
491 | );
492 | runOnlyForDeploymentPostprocessing = 0;
493 | };
494 | CEB1399C1BF266DD00DE6BA9 /* Sources */ = {
495 | isa = PBXSourcesBuildPhase;
496 | buildActionMask = 2147483647;
497 | files = (
498 | CEB139A61BF266DD00DE6BA9 /* ViewController.swift in Sources */,
499 | CEE3408E1D8AEA4600FF580D /* FlapViewBuilder.swift in Sources */,
500 | CEE340911D8AEA4600FF580D /* SplitflapDelegate.swift in Sources */,
501 | CEE340921D8AEA4600FF580D /* SplitflapTokens.swift in Sources */,
502 | CEE340931D8AEA4600FF580D /* TileView.swift in Sources */,
503 | CEE340941D8AEA4600FF580D /* TokenGenerator.swift in Sources */,
504 | CEE340951D8AEA4600FF580D /* TokenParser.swift in Sources */,
505 | CEE340901D8AEA4600FF580D /* SplitflapDataSource.swift in Sources */,
506 | CEB139A41BF266DD00DE6BA9 /* AppDelegate.swift in Sources */,
507 | CEE3408D1D8AEA4600FF580D /* FlapView.swift in Sources */,
508 | CEE3408F1D8AEA4600FF580D /* Splitflap.swift in Sources */,
509 | );
510 | runOnlyForDeploymentPostprocessing = 0;
511 | };
512 | /* End PBXSourcesBuildPhase section */
513 |
514 | /* Begin PBXTargetDependency section */
515 | CE525D8E1BF3833700429200 /* PBXTargetDependency */ = {
516 | isa = PBXTargetDependency;
517 | target = CE525D871BF3833700429200 /* Splitflap */;
518 | targetProxy = CE525D8D1BF3833700429200 /* PBXContainerItemProxy */;
519 | };
520 | CE525DB31BF4FBC800429200 /* PBXTargetDependency */ = {
521 | isa = PBXTargetDependency;
522 | target = CEB1399F1BF266DD00DE6BA9 /* SplitflapExample */;
523 | targetProxy = CE525DB21BF4FBC800429200 /* PBXContainerItemProxy */;
524 | };
525 | /* End PBXTargetDependency section */
526 |
527 | /* Begin PBXVariantGroup section */
528 | CE5040601C050024007D9E9F /* Main.storyboard */ = {
529 | isa = PBXVariantGroup;
530 | children = (
531 | CE5040611C050024007D9E9F /* Base */,
532 | );
533 | name = Main.storyboard;
534 | sourceTree = "";
535 | };
536 | CEB139A71BF266DD00DE6BA9 /* Main.storyboard */ = {
537 | isa = PBXVariantGroup;
538 | children = (
539 | CEB139A81BF266DD00DE6BA9 /* Base */,
540 | );
541 | name = Main.storyboard;
542 | sourceTree = "";
543 | };
544 | CEB139AC1BF266DD00DE6BA9 /* LaunchScreen.storyboard */ = {
545 | isa = PBXVariantGroup;
546 | children = (
547 | CEB139AD1BF266DD00DE6BA9 /* Base */,
548 | );
549 | name = LaunchScreen.storyboard;
550 | sourceTree = "";
551 | };
552 | /* End PBXVariantGroup section */
553 |
554 | /* Begin XCBuildConfiguration section */
555 | CE5040661C050024007D9E9F /* Debug */ = {
556 | isa = XCBuildConfiguration;
557 | buildSettings = {
558 | ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image";
559 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
560 | INFOPLIST_FILE = tvOSExample/Info.plist;
561 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
562 | PRODUCT_BUNDLE_IDENTIFIER = com.yannickloriot.tvOSExample;
563 | PRODUCT_NAME = "$(TARGET_NAME)";
564 | SDKROOT = appletvos;
565 | SWIFT_VERSION = 4.2;
566 | TARGETED_DEVICE_FAMILY = 3;
567 | TVOS_DEPLOYMENT_TARGET = 9.0;
568 | };
569 | name = Debug;
570 | };
571 | CE5040671C050024007D9E9F /* Release */ = {
572 | isa = XCBuildConfiguration;
573 | buildSettings = {
574 | ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image";
575 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
576 | INFOPLIST_FILE = tvOSExample/Info.plist;
577 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
578 | PRODUCT_BUNDLE_IDENTIFIER = com.yannickloriot.tvOSExample;
579 | PRODUCT_NAME = "$(TARGET_NAME)";
580 | SDKROOT = appletvos;
581 | SWIFT_VERSION = 4.2;
582 | TARGETED_DEVICE_FAMILY = 3;
583 | TVOS_DEPLOYMENT_TARGET = 9.0;
584 | };
585 | name = Release;
586 | };
587 | CE525D921BF3833700429200 /* Debug */ = {
588 | isa = XCBuildConfiguration;
589 | buildSettings = {
590 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
591 | CURRENT_PROJECT_VERSION = 1;
592 | DEFINES_MODULE = YES;
593 | DYLIB_COMPATIBILITY_VERSION = 1;
594 | DYLIB_CURRENT_VERSION = 1;
595 | DYLIB_INSTALL_NAME_BASE = "@rpath";
596 | GCC_GENERATE_TEST_COVERAGE_FILES = YES;
597 | GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES;
598 | INFOPLIST_FILE = Splitflap/Info.plist;
599 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
600 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
601 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
602 | PRODUCT_BUNDLE_IDENTIFIER = com.yannickloriot.Splitflap;
603 | PRODUCT_NAME = "$(TARGET_NAME)";
604 | SKIP_INSTALL = YES;
605 | SWIFT_VERSION = 5.0;
606 | VERSIONING_SYSTEM = "apple-generic";
607 | VERSION_INFO_PREFIX = "";
608 | };
609 | name = Debug;
610 | };
611 | CE525D931BF3833700429200 /* Release */ = {
612 | isa = XCBuildConfiguration;
613 | buildSettings = {
614 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
615 | CURRENT_PROJECT_VERSION = 1;
616 | DEFINES_MODULE = YES;
617 | DYLIB_COMPATIBILITY_VERSION = 1;
618 | DYLIB_CURRENT_VERSION = 1;
619 | DYLIB_INSTALL_NAME_BASE = "@rpath";
620 | GCC_GENERATE_TEST_COVERAGE_FILES = YES;
621 | GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES;
622 | INFOPLIST_FILE = Splitflap/Info.plist;
623 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
624 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
625 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
626 | PRODUCT_BUNDLE_IDENTIFIER = com.yannickloriot.Splitflap;
627 | PRODUCT_NAME = "$(TARGET_NAME)";
628 | SKIP_INSTALL = YES;
629 | SWIFT_VERSION = 5.0;
630 | VERSIONING_SYSTEM = "apple-generic";
631 | VERSION_INFO_PREFIX = "";
632 | };
633 | name = Release;
634 | };
635 | CE525DB41BF4FBC800429200 /* Debug */ = {
636 | isa = XCBuildConfiguration;
637 | buildSettings = {
638 | INFOPLIST_FILE = SplitflapTests/Info.plist;
639 | IPHONEOS_DEPLOYMENT_TARGET = 9.1;
640 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
641 | PRODUCT_BUNDLE_IDENTIFIER = com.yannickloriot.SplitflapTests;
642 | PRODUCT_NAME = "$(TARGET_NAME)";
643 | SWIFT_VERSION = 4.2;
644 | };
645 | name = Debug;
646 | };
647 | CE525DB51BF4FBC800429200 /* Release */ = {
648 | isa = XCBuildConfiguration;
649 | buildSettings = {
650 | INFOPLIST_FILE = SplitflapTests/Info.plist;
651 | IPHONEOS_DEPLOYMENT_TARGET = 9.1;
652 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
653 | PRODUCT_BUNDLE_IDENTIFIER = com.yannickloriot.SplitflapTests;
654 | PRODUCT_NAME = "$(TARGET_NAME)";
655 | SWIFT_VERSION = 4.2;
656 | };
657 | name = Release;
658 | };
659 | CEB139B01BF266DD00DE6BA9 /* Debug */ = {
660 | isa = XCBuildConfiguration;
661 | buildSettings = {
662 | ALWAYS_SEARCH_USER_PATHS = NO;
663 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
664 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
665 | CLANG_CXX_LIBRARY = "libc++";
666 | CLANG_ENABLE_MODULES = YES;
667 | CLANG_ENABLE_OBJC_ARC = YES;
668 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
669 | CLANG_WARN_BOOL_CONVERSION = YES;
670 | CLANG_WARN_COMMA = YES;
671 | CLANG_WARN_CONSTANT_CONVERSION = YES;
672 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
673 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
674 | CLANG_WARN_EMPTY_BODY = YES;
675 | CLANG_WARN_ENUM_CONVERSION = YES;
676 | CLANG_WARN_INFINITE_RECURSION = YES;
677 | CLANG_WARN_INT_CONVERSION = YES;
678 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
679 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
680 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
681 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
682 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
683 | CLANG_WARN_STRICT_PROTOTYPES = YES;
684 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
685 | CLANG_WARN_UNREACHABLE_CODE = YES;
686 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
687 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
688 | COPY_PHASE_STRIP = NO;
689 | DEBUG_INFORMATION_FORMAT = dwarf;
690 | ENABLE_STRICT_OBJC_MSGSEND = YES;
691 | ENABLE_TESTABILITY = YES;
692 | GCC_C_LANGUAGE_STANDARD = gnu99;
693 | GCC_DYNAMIC_NO_PIC = NO;
694 | GCC_NO_COMMON_BLOCKS = YES;
695 | GCC_OPTIMIZATION_LEVEL = 0;
696 | GCC_PREPROCESSOR_DEFINITIONS = (
697 | "DEBUG=1",
698 | "$(inherited)",
699 | );
700 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
701 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
702 | GCC_WARN_UNDECLARED_SELECTOR = YES;
703 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
704 | GCC_WARN_UNUSED_FUNCTION = YES;
705 | GCC_WARN_UNUSED_VARIABLE = YES;
706 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
707 | MTL_ENABLE_DEBUG_INFO = YES;
708 | ONLY_ACTIVE_ARCH = YES;
709 | SDKROOT = iphoneos;
710 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
711 | SWIFT_VERSION = 4.2;
712 | TARGETED_DEVICE_FAMILY = "1,2";
713 | };
714 | name = Debug;
715 | };
716 | CEB139B11BF266DD00DE6BA9 /* Release */ = {
717 | isa = XCBuildConfiguration;
718 | buildSettings = {
719 | ALWAYS_SEARCH_USER_PATHS = NO;
720 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
721 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
722 | CLANG_CXX_LIBRARY = "libc++";
723 | CLANG_ENABLE_MODULES = YES;
724 | CLANG_ENABLE_OBJC_ARC = YES;
725 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
726 | CLANG_WARN_BOOL_CONVERSION = YES;
727 | CLANG_WARN_COMMA = YES;
728 | CLANG_WARN_CONSTANT_CONVERSION = YES;
729 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
730 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
731 | CLANG_WARN_EMPTY_BODY = YES;
732 | CLANG_WARN_ENUM_CONVERSION = YES;
733 | CLANG_WARN_INFINITE_RECURSION = YES;
734 | CLANG_WARN_INT_CONVERSION = YES;
735 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
736 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
737 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
738 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
739 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
740 | CLANG_WARN_STRICT_PROTOTYPES = YES;
741 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
742 | CLANG_WARN_UNREACHABLE_CODE = YES;
743 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
744 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
745 | COPY_PHASE_STRIP = NO;
746 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
747 | ENABLE_NS_ASSERTIONS = NO;
748 | ENABLE_STRICT_OBJC_MSGSEND = YES;
749 | GCC_C_LANGUAGE_STANDARD = gnu99;
750 | GCC_NO_COMMON_BLOCKS = YES;
751 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
752 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
753 | GCC_WARN_UNDECLARED_SELECTOR = YES;
754 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
755 | GCC_WARN_UNUSED_FUNCTION = YES;
756 | GCC_WARN_UNUSED_VARIABLE = YES;
757 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
758 | MTL_ENABLE_DEBUG_INFO = NO;
759 | SDKROOT = iphoneos;
760 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
761 | SWIFT_VERSION = 4.2;
762 | TARGETED_DEVICE_FAMILY = "1,2";
763 | VALIDATE_PRODUCT = YES;
764 | };
765 | name = Release;
766 | };
767 | CEB139B31BF266DD00DE6BA9 /* Debug */ = {
768 | isa = XCBuildConfiguration;
769 | buildSettings = {
770 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
771 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
772 | INFOPLIST_FILE = SplitflapExample/Info.plist;
773 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
774 | PRODUCT_BUNDLE_IDENTIFIER = com.yannickloriot.SplitflapExample;
775 | PRODUCT_NAME = "$(TARGET_NAME)";
776 | SWIFT_VERSION = 5.0;
777 | };
778 | name = Debug;
779 | };
780 | CEB139B41BF266DD00DE6BA9 /* Release */ = {
781 | isa = XCBuildConfiguration;
782 | buildSettings = {
783 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
784 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
785 | INFOPLIST_FILE = SplitflapExample/Info.plist;
786 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
787 | PRODUCT_BUNDLE_IDENTIFIER = com.yannickloriot.SplitflapExample;
788 | PRODUCT_NAME = "$(TARGET_NAME)";
789 | SWIFT_VERSION = 5.0;
790 | };
791 | name = Release;
792 | };
793 | /* End XCBuildConfiguration section */
794 |
795 | /* Begin XCConfigurationList section */
796 | CE5040681C050024007D9E9F /* Build configuration list for PBXNativeTarget "tvOSExample" */ = {
797 | isa = XCConfigurationList;
798 | buildConfigurations = (
799 | CE5040661C050024007D9E9F /* Debug */,
800 | CE5040671C050024007D9E9F /* Release */,
801 | );
802 | defaultConfigurationIsVisible = 0;
803 | defaultConfigurationName = Release;
804 | };
805 | CE525D911BF3833700429200 /* Build configuration list for PBXNativeTarget "Splitflap" */ = {
806 | isa = XCConfigurationList;
807 | buildConfigurations = (
808 | CE525D921BF3833700429200 /* Debug */,
809 | CE525D931BF3833700429200 /* Release */,
810 | );
811 | defaultConfigurationIsVisible = 0;
812 | defaultConfigurationName = Release;
813 | };
814 | CE525DB61BF4FBC800429200 /* Build configuration list for PBXNativeTarget "SplitflapTests" */ = {
815 | isa = XCConfigurationList;
816 | buildConfigurations = (
817 | CE525DB41BF4FBC800429200 /* Debug */,
818 | CE525DB51BF4FBC800429200 /* Release */,
819 | );
820 | defaultConfigurationIsVisible = 0;
821 | defaultConfigurationName = Release;
822 | };
823 | CEB1399B1BF266DD00DE6BA9 /* Build configuration list for PBXProject "SplitflapExample" */ = {
824 | isa = XCConfigurationList;
825 | buildConfigurations = (
826 | CEB139B01BF266DD00DE6BA9 /* Debug */,
827 | CEB139B11BF266DD00DE6BA9 /* Release */,
828 | );
829 | defaultConfigurationIsVisible = 0;
830 | defaultConfigurationName = Release;
831 | };
832 | CEB139B21BF266DD00DE6BA9 /* Build configuration list for PBXNativeTarget "SplitflapExample" */ = {
833 | isa = XCConfigurationList;
834 | buildConfigurations = (
835 | CEB139B31BF266DD00DE6BA9 /* Debug */,
836 | CEB139B41BF266DD00DE6BA9 /* Release */,
837 | );
838 | defaultConfigurationIsVisible = 0;
839 | defaultConfigurationName = Release;
840 | };
841 | /* End XCConfigurationList section */
842 | };
843 | rootObject = CEB139981BF266DD00DE6BA9 /* Project object */;
844 | }
845 |
--------------------------------------------------------------------------------