├── .gitignore
├── .swift-version
├── Example
├── Kamishibai.xcodeproj
│ ├── project.pbxproj
│ ├── project.xcworkspace
│ │ └── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── xcschemes
│ │ └── Kamishibai-Example.xcscheme
├── Kamishibai.xcworkspace
│ └── contents.xcworkspacedata
├── Kamishibai
│ ├── AppDelegate.swift
│ ├── Base.lproj
│ │ ├── LaunchScreen.xib
│ │ └── Main.storyboard
│ ├── Images.xcassets
│ │ └── AppIcon.appiconset
│ │ │ └── Contents.json
│ ├── Info.plist
│ ├── SampleDataSource.swift
│ ├── SampleGuideLabel.swift
│ ├── SampleGuideView.swift
│ ├── SampleGuideView.xib
│ ├── SampleTableViewCell.swift
│ ├── SecondViewController.swift
│ └── ViewController.swift
├── Kamishibai_ExampleUITests
│ ├── Info.plist
│ └── Kamishibai_ExampleUITests.swift
├── Podfile
├── Podfile.lock
├── Pods
│ ├── FBSnapshotTestCase
│ │ ├── FBSnapshotTestCase
│ │ │ ├── Categories
│ │ │ │ ├── UIApplication+StrictKeyWindow.h
│ │ │ │ ├── UIApplication+StrictKeyWindow.m
│ │ │ │ ├── UIImage+Compare.h
│ │ │ │ ├── UIImage+Compare.m
│ │ │ │ ├── UIImage+Diff.h
│ │ │ │ ├── UIImage+Diff.m
│ │ │ │ ├── UIImage+Snapshot.h
│ │ │ │ └── UIImage+Snapshot.m
│ │ │ ├── FBSnapshotTestCase.h
│ │ │ ├── FBSnapshotTestCase.m
│ │ │ ├── FBSnapshotTestCasePlatform.h
│ │ │ ├── FBSnapshotTestCasePlatform.m
│ │ │ ├── FBSnapshotTestController.h
│ │ │ ├── FBSnapshotTestController.m
│ │ │ └── SwiftSupport.swift
│ │ ├── LICENSE
│ │ └── README.md
│ ├── Local Podspecs
│ │ └── Kamishibai.podspec.json
│ ├── Manifest.lock
│ ├── Pods.xcodeproj
│ │ ├── project.pbxproj
│ │ └── xcshareddata
│ │ │ └── xcschemes
│ │ │ └── Kamishibai.xcscheme
│ └── Target Support Files
│ │ ├── FBSnapshotTestCase
│ │ ├── FBSnapshotTestCase-dummy.m
│ │ ├── FBSnapshotTestCase-prefix.pch
│ │ ├── FBSnapshotTestCase-umbrella.h
│ │ ├── FBSnapshotTestCase.modulemap
│ │ ├── FBSnapshotTestCase.xcconfig
│ │ └── Info.plist
│ │ ├── Kamishibai
│ │ ├── Info.plist
│ │ ├── Kamishibai-dummy.m
│ │ ├── Kamishibai-prefix.pch
│ │ ├── Kamishibai-umbrella.h
│ │ ├── Kamishibai.modulemap
│ │ └── Kamishibai.xcconfig
│ │ ├── Pods-Kamishibai_Example
│ │ ├── Info.plist
│ │ ├── Pods-Kamishibai_Example-acknowledgements.markdown
│ │ ├── Pods-Kamishibai_Example-acknowledgements.plist
│ │ ├── Pods-Kamishibai_Example-dummy.m
│ │ ├── Pods-Kamishibai_Example-frameworks.sh
│ │ ├── Pods-Kamishibai_Example-resources.sh
│ │ ├── Pods-Kamishibai_Example-umbrella.h
│ │ ├── Pods-Kamishibai_Example.debug.xcconfig
│ │ ├── Pods-Kamishibai_Example.modulemap
│ │ └── Pods-Kamishibai_Example.release.xcconfig
│ │ └── Pods-Kamishibai_Tests
│ │ ├── Info.plist
│ │ ├── Pods-Kamishibai_Tests-acknowledgements.markdown
│ │ ├── Pods-Kamishibai_Tests-acknowledgements.plist
│ │ ├── Pods-Kamishibai_Tests-dummy.m
│ │ ├── Pods-Kamishibai_Tests-frameworks.sh
│ │ ├── Pods-Kamishibai_Tests-resources.sh
│ │ ├── Pods-Kamishibai_Tests-umbrella.h
│ │ ├── Pods-Kamishibai_Tests.debug.xcconfig
│ │ ├── Pods-Kamishibai_Tests.modulemap
│ │ └── Pods-Kamishibai_Tests.release.xcconfig
└── Tests
│ ├── Info.plist
│ └── KamishibaiTests.swift
├── Kamishibai.podspec
├── Kamishibai
├── Assets
│ └── .gitkeep
└── Classes
│ ├── .gitkeep
│ ├── Kamishibai.swift
│ ├── KamishibaiFocusView.swift
│ ├── KamishibaiFocusViewController.swift
│ ├── KamishibaiScene.swift
│ └── KamishibaiTransitioning.swift
├── LICENSE
├── README.md
├── Screenshots
└── Kamishibai1.gif
└── _Pods.xcodeproj
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | #
3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
4 |
5 | ## Build generated
6 | build/
7 | DerivedData/
8 |
9 | ## Various settings
10 | *.pbxuser
11 | !default.pbxuser
12 | *.mode1v3
13 | !default.mode1v3
14 | *.mode2v3
15 | !default.mode2v3
16 | *.perspectivev3
17 | !default.perspectivev3
18 | xcuserdata/
19 |
20 | ## Other
21 | *.moved-aside
22 | *.xccheckout
23 | *.xcscmblueprint
24 |
25 | ## Obj-C/Swift specific
26 | *.hmap
27 | *.ipa
28 | *.dSYM.zip
29 | *.dSYM
30 |
31 | ## Playgrounds
32 | timeline.xctimeline
33 | playground.xcworkspace
34 |
35 | # Swift Package Manager
36 | #
37 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
38 | # Packages/
39 | # Package.pins
40 | .build/
41 |
42 | # CocoaPods
43 | #
44 | # We recommend against adding the Pods directory to your .gitignore. However
45 | # you should judge for yourself, the pros and cons are mentioned at:
46 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
47 | #
48 | Pods/
49 |
50 | # Carthage
51 | #
52 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
53 | # Carthage/Checkouts
54 |
55 | Carthage/Build
56 |
57 | # fastlane
58 | #
59 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
60 | # screenshots whenever they are needed.
61 | # For more information about the recommended setup visit:
62 | # https://docs.fastlane.tools/best-practices/source-control/#source-control
63 |
64 | fastlane/report.xml
65 | fastlane/Preview.html
66 | fastlane/screenshots
67 | fastlane/test_output
68 |
--------------------------------------------------------------------------------
/.swift-version:
--------------------------------------------------------------------------------
1 | 4.0
2 |
--------------------------------------------------------------------------------
/Example/Kamishibai.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Example/Kamishibai.xcodeproj/xcshareddata/xcschemes/Kamishibai-Example.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
29 |
35 |
36 |
37 |
38 |
39 |
44 |
45 |
47 |
53 |
54 |
55 |
57 |
63 |
64 |
65 |
66 |
67 |
73 |
74 |
75 |
76 |
77 |
78 |
88 |
90 |
96 |
97 |
98 |
99 |
100 |
101 |
107 |
109 |
115 |
116 |
117 |
118 |
120 |
121 |
124 |
125 |
126 |
--------------------------------------------------------------------------------
/Example/Kamishibai.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/Example/Kamishibai/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // Kamishibai
4 | //
5 | // Created by Keisuke Matsuo on 2017/08/12.
6 | //
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: [UIApplicationLaunchOptionsKey: 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/Kamishibai/Base.lproj/LaunchScreen.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
25 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/Example/Kamishibai/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 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
--------------------------------------------------------------------------------
/Example/Kamishibai/Images.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "20x20",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "20x20",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "29x29",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "29x29",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "40x40",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "40x40",
31 | "scale" : "3x"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "size" : "60x60",
36 | "scale" : "2x"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "size" : "60x60",
41 | "scale" : "3x"
42 | }
43 | ],
44 | "info" : {
45 | "version" : 1,
46 | "author" : "xcode"
47 | }
48 | }
--------------------------------------------------------------------------------
/Example/Kamishibai/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 | 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 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/Example/Kamishibai/SampleDataSource.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SampleDataSource.swift
3 | // Kamishibai
4 | //
5 | // Created by Keisuke Matsuo on 2017/08/12.
6 | //
7 | //
8 |
9 | import Foundation
10 | import UIKit
11 |
12 | class SampleDataSource: NSObject, UITableViewDataSource {
13 | var data = [
14 | "Kamishibai helps you to create a long tutorial to guide new users.\n\nKamishibai has following features.\n- Highlight a specific view\n- Transition screen\n- Tap a specific view, then go to next\n\nTap this cell after read above.",
15 | "Available adding custom UI"
16 | ]
17 |
18 | public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
19 | return data.count
20 | }
21 |
22 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
23 | let cell = tableView.dequeueReusableCell(withIdentifier: "SampleTableViewCell", for: indexPath)
24 | if let cell = cell as? SampleTableViewCell {
25 | cell.label.text = data[indexPath.row]
26 | }
27 | return cell
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/Example/Kamishibai/SampleGuideLabel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SampleGuideLabel.swift
3 | // Kamishibai
4 | //
5 | // Created by Matsuo Keisuke on 9/18/17.
6 | // Copyright © 2017 CocoaPods. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class SampleGuideLabel: UILabel {
12 | init(_ text: String) {
13 | super.init(frame: CGRect.zero)
14 | self.text = text
15 | self.numberOfLines = 0
16 | self.sizeToFit()
17 | self.textColor = UIColor.white
18 | }
19 |
20 | required init?(coder aDecoder: NSCoder) {
21 | fatalError("init(coder:) has not been implemented")
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/Example/Kamishibai/SampleGuideView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SampleGuideView.swift
3 | // Kamishibai
4 | //
5 | // Created by Matsuo Keisuke on 8/13/17.
6 | // Copyright © 2017 CocoaPods. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import Kamishibai
11 |
12 | class SampleGuideView: UIView, KamishibaiCustomViewAnimation {
13 |
14 | @IBOutlet weak var titleLabel: UILabel!
15 | @IBOutlet weak var button: UIButton!
16 |
17 | func show(animated: Bool, fulfill: @escaping () -> Void) {
18 | alpha = 0
19 | transform = CGAffineTransform(scaleX: 0, y: 0)
20 | UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 0.7, initialSpringVelocity: 0.7, options: UIViewAnimationOptions(rawValue: 0), animations: {
21 | self.alpha = 1
22 | self.transform = CGAffineTransform.identity
23 | }) { (_) in
24 | fulfill()
25 | }
26 | }
27 | func hide(animated: Bool, fulfill: @escaping () -> Void) {
28 | UIView.animate(withDuration: 0.2, animations: {
29 | self.alpha = 0.0
30 | self.transform = CGAffineTransform(scaleX: 0.1, y: 0.1)
31 | }) { (_) in
32 | fulfill()
33 | }
34 | }
35 |
36 | static func create() -> SampleGuideView {
37 | let nib = UINib(nibName: String(describing: SampleGuideView.self), bundle: nil)
38 | .instantiate(withOwner: nil, options: nil)
39 | return nib.first as! SampleGuideView
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/Example/Kamishibai/SampleGuideView.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
24 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/Example/Kamishibai/SampleTableViewCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SampleTableViewCell.swift
3 | // Kamishibai
4 | //
5 | // Created by Keisuke Matsuo on 2017/08/12.
6 | //
7 | //
8 |
9 | import UIKit
10 |
11 | class SampleTableViewCell: UITableViewCell {
12 |
13 | @IBOutlet weak var label: UILabel!
14 |
15 | override func awakeFromNib() {
16 | super.awakeFromNib()
17 | // Initialization code
18 | }
19 |
20 | override func setSelected(_ selected: Bool, animated: Bool) {
21 | super.setSelected(selected, animated: animated)
22 |
23 | // Configure the view for the selected state
24 | }
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/Example/Kamishibai/SecondViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SecondViewController.swift
3 | // Kamishibai
4 | //
5 | // Created by Keisuke Matsuo on 2017/08/12.
6 | //
7 | //
8 |
9 | import UIKit
10 |
11 | class SecondViewController: UIViewController {
12 |
13 | @IBOutlet weak var button: UIButton!
14 |
15 | override func viewDidLoad() {
16 | super.viewDidLoad()
17 |
18 | // Do any additional setup after loading the view.
19 | }
20 |
21 | override func didReceiveMemoryWarning() {
22 | super.didReceiveMemoryWarning()
23 | // Dispose of any resources that can be recreated.
24 | }
25 |
26 |
27 | /*
28 | // MARK: - Navigation
29 |
30 | // In a kamishibaiboard-based application, you will often want to do a little preparation before navigation
31 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
32 | // Get the new view controller using segue.destinationViewController.
33 | // Pass the selected object to the new view controller.
34 | }
35 | */
36 |
37 |
38 | static func create() -> SecondViewController {
39 | let vc = UIStoryboard.init(name: "Main", bundle: nil)
40 | .instantiateViewController(withIdentifier: String(describing: SecondViewController.self)) as! SecondViewController
41 | return vc
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/Example/Kamishibai/ViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // Kamishibai
4 | //
5 | // Created by Keisuke Matsuo on 2017/08/12.
6 | //
7 | //
8 |
9 | import UIKit
10 | import Kamishibai
11 |
12 | class ViewController: UIViewController {
13 |
14 | var dataSource = SampleDataSource()
15 | lazy var kamishibai: Kamishibai = {
16 | let kamishibai = Kamishibai(initialViewController: self)
17 | return kamishibai
18 | }()
19 |
20 | @IBOutlet weak var tableView: UITableView! {
21 | didSet {
22 | tableView.rowHeight = UITableViewAutomaticDimension
23 | tableView.estimatedRowHeight = 40
24 | }
25 | }
26 |
27 | override func viewDidLoad() {
28 | super.viewDidLoad()
29 | // Do any additional setup after loading the view, typically from a nib.
30 | title = "About Kamishibai"
31 | tableView.dataSource = dataSource
32 | tableView.reloadData()
33 |
34 | DispatchQueue.main.asyncAfter(timeInterval: 0.1) {
35 | self.setupStory()
36 | }
37 | }
38 | override func viewWillAppear(_ animated: Bool) {
39 | super.viewWillAppear(animated)
40 | guard !(isMovingFromParentViewController || isMovingToParentViewController) else {
41 | assertionFailure("Don't call push(fromVC:toVC:animated:) dualing isMovingFromParentViewController or isMovingFromParentViewController ")
42 | return
43 | }
44 | }
45 |
46 | override func viewDidAppear(_ animated: Bool) {
47 | super.viewDidAppear(animated)
48 | }
49 |
50 | func setupStory() {
51 | setupTest(identifier: "start kamishibai", scene: nil)
52 |
53 | kamishibai.append(KamishibaiScene(scene: { (scene) in
54 | self.setupTest(identifier: "scene 1", scene: scene)
55 |
56 | guard let vc = scene.kamishibai?.currentViewController as? ViewController else { return }
57 | let cell = vc.tableView.cellForRow(at: IndexPath(row: 0, section: 0))!
58 | let frame = cell.convert(cell.bounds, to: vc.view)
59 |
60 | scene.kamishibai?.focus.on(view: vc.navigationController?.view, focus: Focus.Rect(frame: frame))
61 | scene.fulfillWhenTapFocus()
62 | }))
63 | kamishibai.append(KamishibaiScene(scene: { (scene) in
64 | self.setupTest(identifier: "scene 2", scene: scene)
65 |
66 | guard let vc = scene.kamishibai?.currentViewController as? ViewController else { return }
67 | let cell = vc.tableView.cellForRow(at: IndexPath(row: 1, section: 0))!
68 | let frame = cell.convert(cell.bounds, to: vc.view)
69 | scene.kamishibai?.focus.on(view: vc.navigationController?.view, focus: Focus.Rect(frame: frame))
70 |
71 | let guide = SampleGuideView.create()
72 | scene.kamishibai?.focus.addCustomView(view: guide, position: .bottomRight(CGPoint.zero))
73 | scene.fulfillWhenTap(view: guide.button)
74 | }))
75 | kamishibai.append(KamishibaiScene(transition: .push(SecondViewController.create()), scene: { (scene) in
76 | self.setupTest(identifier: "scene 3", scene: scene)
77 |
78 | guard let vc = scene.kamishibai?.currentViewController as? SecondViewController else { return }
79 | scene.kamishibai?.focus.on(view: vc.navigationController?.view, focus: Focus.Rect(frame: vc.button.frame))
80 |
81 | let guide = SampleGuideLabel("Available screen transition\n- pushViewController\n- presentViewController\n- custom")
82 | scene.kamishibai?.focus.addCustomView(view: guide, position: .bottomRight(CGPoint(x: -20, y: 10)))
83 | scene.fulfillWhenTapFocus()
84 | }))
85 | kamishibai.append(KamishibaiScene(transition: .present(SecondViewController.create()), scene: { (scene) in
86 | self.setupTest(identifier: "scene 4", scene: scene)
87 |
88 | guard let vc = scene.kamishibai?.currentViewController as? SecondViewController else { return }
89 | scene.kamishibai?.focus.on(view: vc.navigationController?.view, focus: Focus.Rect(frame: vc.button.frame))
90 |
91 | let guide = SampleGuideLabel("Also available\n- popViewController\n- dismissViewController\n- custom")
92 | scene.kamishibai?.focus.addCustomView(view: guide, position: .bottomRight(CGPoint(x: -20, y: 10)))
93 | scene.fulfillWhenTapFocus()
94 | }))
95 | kamishibai.append(KamishibaiScene(transition: .dismiss, scene: { (scene) in
96 | self.setupTest(identifier: "scene 5", scene: scene)
97 |
98 | scene.fulfill()
99 | }))
100 | kamishibai.append(KamishibaiScene(transition: .popToRoot, scene: { (scene) in
101 | self.setupTest(identifier: "scene 6", scene: scene)
102 |
103 | guard let vc = scene.kamishibai?.currentViewController else { return }
104 | let alert = UIAlertController(title: "Congraturations!",
105 | message: "You have successfully finished this tutorial.",
106 | preferredStyle: .alert)
107 | alert.addAction(UIAlertAction(title: "Close", style: .default, handler: { (alert) in
108 | scene.fulfill()
109 | }))
110 | vc.present(alert, animated: true, completion: nil)
111 | }))
112 |
113 | kamishibai.completion = {
114 | self.setupTest(identifier: "complete kamishibai", scene: nil)
115 | }
116 |
117 | kamishibai.startStory()
118 | }
119 |
120 | func setupTest(identifier: String, scene: KamishibaiScene?) {
121 | print(identifier)
122 | guard let vc = scene?.kamishibai?.currentViewController else { return }
123 | vc.title = identifier
124 |
125 | scene?.kamishibai?.focus.view.accessibilityIdentifier = "focus"
126 | vc.view.accessibilityIdentifier = identifier
127 | }
128 | }
129 |
130 |
131 | public extension DispatchQueue {
132 | func asyncAfter(timeInterval: TimeInterval, execute: @escaping () -> Void) {
133 | let onesec = DispatchTime(uptimeNanoseconds: DispatchTime.now().uptimeNanoseconds + UInt64(timeInterval * 1_000_000_000))
134 | DispatchQueue.main.asyncAfter(deadline: onesec) {
135 | execute()
136 | }
137 |
138 | // DispatchQueue.main.asyncAfter(deadline: .now() + timeInterval) {
139 | // execute()
140 | // }
141 | }
142 | }
143 |
--------------------------------------------------------------------------------
/Example/Kamishibai_ExampleUITests/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 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 |
22 |
23 |
--------------------------------------------------------------------------------
/Example/Kamishibai_ExampleUITests/Kamishibai_ExampleUITests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Kamishibai_ExampleUITests.swift
3 | // Kamishibai_ExampleUITests
4 | //
5 | // Created by Matsuo Keisuke on 8/22/17.
6 | // Copyright © 2017 CocoaPods. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | @testable import Kamishibai_Example
11 |
12 | class Kamishibai_ExampleUITests: XCTestCase {
13 |
14 | var app: XCUIApplication!
15 |
16 | override func setUp() {
17 | super.setUp()
18 |
19 | // Put setup code here. This method is called before the invocation of each test method in the class.
20 |
21 | // In UI tests it is usually best to stop immediately when a failure occurs.
22 | continueAfterFailure = false
23 | // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method.
24 | app = XCUIApplication()
25 | app.launch()
26 | }
27 |
28 | override func tearDown() {
29 | // Put teardown code here. This method is called after the invocation of each test method in the class.
30 | super.tearDown()
31 | }
32 |
33 | func testExample() {
34 |
35 | let screenSize = UIScreen.main.bounds.size
36 | let screenCenter = CGPoint(x: screenSize.width / 2, y: screenSize.height / 2)
37 |
38 | // scene 1
39 | waitFor(element: app.otherElements["scene 1"])
40 | app.otherElements["focus"].tap(point: CGPoint(x: 0, y: 100))
41 |
42 | // scene 2
43 | waitFor(element: app.otherElements["scene 2"])
44 | app.buttons["Next"].tap()
45 |
46 | // scene 3
47 | waitFor(element: app.otherElements["scene 3"])
48 | app.otherElements["focus"].tap(point: screenCenter)
49 |
50 | // scene 4
51 | waitFor(element: app.otherElements["scene 4"])
52 | app.otherElements["focus"].tap(point: screenCenter)
53 |
54 | // scene 5 finish immediately
55 |
56 | // scene 6
57 | waitFor(element: app.otherElements["scene 6"])
58 | waitFor(element: app.alerts["Congraturations!"])
59 | }
60 | }
61 |
62 | extension XCUIElement {
63 | func tap(point: CGPoint) {
64 | let screenSize = UIScreen.main.bounds.size
65 | let vector = CGVector(dx: point.x / screenSize.width, dy: point.y / screenSize.height)
66 | self.coordinate(withNormalizedOffset: vector).tap()
67 | }
68 | }
69 |
70 | extension XCTestCase {
71 | func waitFor(element: Any, error: ((Error?) -> Void)? = nil) {
72 | let existsPredicate = NSPredicate(format: "exists == true")
73 | expectation(for: existsPredicate, evaluatedWith: element, handler: nil)
74 | waitForExpectations(timeout: 10, handler: error)
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/Example/Podfile:
--------------------------------------------------------------------------------
1 | use_frameworks!
2 |
3 | target 'Kamishibai_Example' do
4 | pod 'Kamishibai', :path => '../'
5 |
6 | target 'Kamishibai_Tests' do
7 | inherit! :search_paths
8 |
9 | pod 'FBSnapshotTestCase'
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/Example/Podfile.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - FBSnapshotTestCase (2.1.4):
3 | - FBSnapshotTestCase/SwiftSupport (= 2.1.4)
4 | - FBSnapshotTestCase/Core (2.1.4)
5 | - FBSnapshotTestCase/SwiftSupport (2.1.4):
6 | - FBSnapshotTestCase/Core
7 | - Kamishibai (0.1.0)
8 |
9 | DEPENDENCIES:
10 | - FBSnapshotTestCase
11 | - Kamishibai (from `../`)
12 |
13 | EXTERNAL SOURCES:
14 | Kamishibai:
15 | :path: ../
16 |
17 | SPEC CHECKSUMS:
18 | FBSnapshotTestCase: 094f9f314decbabe373b87cc339bea235a63e07a
19 | Kamishibai: 1486b69cfe4f536efdf2afd97eeb4d1c2d68ef37
20 |
21 | PODFILE CHECKSUM: 64fe81f6ef8cda5dd3325c2c66e75dbfab546e37
22 |
23 | COCOAPODS: 1.3.1
24 |
--------------------------------------------------------------------------------
/Example/Pods/FBSnapshotTestCase/FBSnapshotTestCase/Categories/UIApplication+StrictKeyWindow.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2015, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | *
9 | */
10 |
11 | #import
12 |
13 | @interface UIApplication (StrictKeyWindow)
14 |
15 | /**
16 | @return The receiver's @c keyWindow. Raises an assertion if @c nil.
17 | */
18 | - (UIWindow *)fb_strictKeyWindow;
19 |
20 | @end
21 |
--------------------------------------------------------------------------------
/Example/Pods/FBSnapshotTestCase/FBSnapshotTestCase/Categories/UIApplication+StrictKeyWindow.m:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2015, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | *
9 | */
10 |
11 | #import
12 |
13 | @implementation UIApplication (StrictKeyWindow)
14 |
15 | - (UIWindow *)fb_strictKeyWindow
16 | {
17 | UIWindow *keyWindow = [UIApplication sharedApplication].keyWindow;
18 | if (!keyWindow) {
19 | [NSException raise:@"FBSnapshotTestCaseNilKeyWindowException"
20 | format:@"Snapshot tests must be hosted by an application with a key window. Please ensure your test"
21 | " host sets up a key window at launch (either via storyboards or programmatically) and doesn't"
22 | " do anything to remove it while snapshot tests are running."];
23 | }
24 | return keyWindow;
25 | }
26 |
27 | @end
28 |
--------------------------------------------------------------------------------
/Example/Pods/FBSnapshotTestCase/FBSnapshotTestCase/Categories/UIImage+Compare.h:
--------------------------------------------------------------------------------
1 | //
2 | // Created by Gabriel Handford on 3/1/09.
3 | // Copyright 2009-2013. All rights reserved.
4 | // Created by John Boiles on 10/20/11.
5 | // Copyright (c) 2011. All rights reserved
6 | // Modified by Felix Schulze on 2/11/13.
7 | // Copyright 2013. All rights reserved.
8 | //
9 | // Permission is hereby granted, free of charge, to any person
10 | // obtaining a copy of this software and associated documentation
11 | // files (the "Software"), to deal in the Software without
12 | // restriction, including without limitation the rights to use,
13 | // copy, modify, merge, publish, distribute, sublicense, and/or sell
14 | // copies of the Software, and to permit persons to whom the
15 | // Software is furnished to do so, subject to the following
16 | // conditions:
17 | //
18 | // The above copyright notice and this permission notice shall be
19 | // included in all copies or substantial portions of the Software.
20 | //
21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
23 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
25 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
26 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
28 | // OTHER DEALINGS IN THE SOFTWARE.
29 | //
30 |
31 | #import
32 |
33 | @interface UIImage (Compare)
34 |
35 | - (BOOL)fb_compareWithImage:(UIImage *)image tolerance:(CGFloat)tolerance;
36 |
37 | @end
38 |
--------------------------------------------------------------------------------
/Example/Pods/FBSnapshotTestCase/FBSnapshotTestCase/Categories/UIImage+Compare.m:
--------------------------------------------------------------------------------
1 | //
2 | // Created by Gabriel Handford on 3/1/09.
3 | // Copyright 2009-2013. All rights reserved.
4 | // Created by John Boiles on 10/20/11.
5 | // Copyright (c) 2011. All rights reserved
6 | // Modified by Felix Schulze on 2/11/13.
7 | // Copyright 2013. All rights reserved.
8 | //
9 | // Permission is hereby granted, free of charge, to any person
10 | // obtaining a copy of this software and associated documentation
11 | // files (the "Software"), to deal in the Software without
12 | // restriction, including without limitation the rights to use,
13 | // copy, modify, merge, publish, distribute, sublicense, and/or sell
14 | // copies of the Software, and to permit persons to whom the
15 | // Software is furnished to do so, subject to the following
16 | // conditions:
17 | //
18 | // The above copyright notice and this permission notice shall be
19 | // included in all copies or substantial portions of the Software.
20 | //
21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
23 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
25 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
26 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
28 | // OTHER DEALINGS IN THE SOFTWARE.
29 | //
30 |
31 | #import
32 |
33 | // This makes debugging much more fun
34 | typedef union {
35 | uint32_t raw;
36 | unsigned char bytes[4];
37 | struct {
38 | char red;
39 | char green;
40 | char blue;
41 | char alpha;
42 | } __attribute__ ((packed)) pixels;
43 | } FBComparePixel;
44 |
45 | @implementation UIImage (Compare)
46 |
47 | - (BOOL)fb_compareWithImage:(UIImage *)image tolerance:(CGFloat)tolerance
48 | {
49 | NSAssert(CGSizeEqualToSize(self.size, image.size), @"Images must be same size.");
50 |
51 | CGSize referenceImageSize = CGSizeMake(CGImageGetWidth(self.CGImage), CGImageGetHeight(self.CGImage));
52 | CGSize imageSize = CGSizeMake(CGImageGetWidth(image.CGImage), CGImageGetHeight(image.CGImage));
53 |
54 | // The images have the equal size, so we could use the smallest amount of bytes because of byte padding
55 | size_t minBytesPerRow = MIN(CGImageGetBytesPerRow(self.CGImage), CGImageGetBytesPerRow(image.CGImage));
56 | size_t referenceImageSizeBytes = referenceImageSize.height * minBytesPerRow;
57 | void *referenceImagePixels = calloc(1, referenceImageSizeBytes);
58 | void *imagePixels = calloc(1, referenceImageSizeBytes);
59 |
60 | if (!referenceImagePixels || !imagePixels) {
61 | free(referenceImagePixels);
62 | free(imagePixels);
63 | return NO;
64 | }
65 |
66 | CGContextRef referenceImageContext = CGBitmapContextCreate(referenceImagePixels,
67 | referenceImageSize.width,
68 | referenceImageSize.height,
69 | CGImageGetBitsPerComponent(self.CGImage),
70 | minBytesPerRow,
71 | CGImageGetColorSpace(self.CGImage),
72 | (CGBitmapInfo)kCGImageAlphaPremultipliedLast
73 | );
74 | CGContextRef imageContext = CGBitmapContextCreate(imagePixels,
75 | imageSize.width,
76 | imageSize.height,
77 | CGImageGetBitsPerComponent(image.CGImage),
78 | minBytesPerRow,
79 | CGImageGetColorSpace(image.CGImage),
80 | (CGBitmapInfo)kCGImageAlphaPremultipliedLast
81 | );
82 |
83 | if (!referenceImageContext || !imageContext) {
84 | CGContextRelease(referenceImageContext);
85 | CGContextRelease(imageContext);
86 | free(referenceImagePixels);
87 | free(imagePixels);
88 | return NO;
89 | }
90 |
91 | CGContextDrawImage(referenceImageContext, CGRectMake(0, 0, referenceImageSize.width, referenceImageSize.height), self.CGImage);
92 | CGContextDrawImage(imageContext, CGRectMake(0, 0, imageSize.width, imageSize.height), image.CGImage);
93 |
94 | CGContextRelease(referenceImageContext);
95 | CGContextRelease(imageContext);
96 |
97 | BOOL imageEqual = YES;
98 |
99 | // Do a fast compare if we can
100 | if (tolerance == 0) {
101 | imageEqual = (memcmp(referenceImagePixels, imagePixels, referenceImageSizeBytes) == 0);
102 | } else {
103 | // Go through each pixel in turn and see if it is different
104 | const NSInteger pixelCount = referenceImageSize.width * referenceImageSize.height;
105 |
106 | FBComparePixel *p1 = referenceImagePixels;
107 | FBComparePixel *p2 = imagePixels;
108 |
109 | NSInteger numDiffPixels = 0;
110 | for (int n = 0; n < pixelCount; ++n) {
111 | // If this pixel is different, increment the pixel diff count and see
112 | // if we have hit our limit.
113 | if (p1->raw != p2->raw) {
114 | numDiffPixels ++;
115 |
116 | CGFloat percent = (CGFloat)numDiffPixels / pixelCount;
117 | if (percent > tolerance) {
118 | imageEqual = NO;
119 | break;
120 | }
121 | }
122 |
123 | p1++;
124 | p2++;
125 | }
126 | }
127 |
128 | free(referenceImagePixels);
129 | free(imagePixels);
130 |
131 | return imageEqual;
132 | }
133 |
134 | @end
135 |
--------------------------------------------------------------------------------
/Example/Pods/FBSnapshotTestCase/FBSnapshotTestCase/Categories/UIImage+Diff.h:
--------------------------------------------------------------------------------
1 | //
2 | // Created by Gabriel Handford on 3/1/09.
3 | // Copyright 2009-2013. All rights reserved.
4 | // Created by John Boiles on 10/20/11.
5 | // Copyright (c) 2011. All rights reserved
6 | // Modified by Felix Schulze on 2/11/13.
7 | // Copyright 2013. All rights reserved.
8 | //
9 | // Permission is hereby granted, free of charge, to any person
10 | // obtaining a copy of this software and associated documentation
11 | // files (the "Software"), to deal in the Software without
12 | // restriction, including without limitation the rights to use,
13 | // copy, modify, merge, publish, distribute, sublicense, and/or sell
14 | // copies of the Software, and to permit persons to whom the
15 | // Software is furnished to do so, subject to the following
16 | // conditions:
17 | //
18 | // The above copyright notice and this permission notice shall be
19 | // included in all copies or substantial portions of the Software.
20 | //
21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
23 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
25 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
26 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
28 | // OTHER DEALINGS IN THE SOFTWARE.
29 | //
30 |
31 | #import
32 |
33 | @interface UIImage (Diff)
34 |
35 | - (UIImage *)fb_diffWithImage:(UIImage *)image;
36 |
37 | @end
38 |
--------------------------------------------------------------------------------
/Example/Pods/FBSnapshotTestCase/FBSnapshotTestCase/Categories/UIImage+Diff.m:
--------------------------------------------------------------------------------
1 | //
2 | // Created by Gabriel Handford on 3/1/09.
3 | // Copyright 2009-2013. All rights reserved.
4 | // Created by John Boiles on 10/20/11.
5 | // Copyright (c) 2011. All rights reserved
6 | // Modified by Felix Schulze on 2/11/13.
7 | // Copyright 2013. All rights reserved.
8 | //
9 | // Permission is hereby granted, free of charge, to any person
10 | // obtaining a copy of this software and associated documentation
11 | // files (the "Software"), to deal in the Software without
12 | // restriction, including without limitation the rights to use,
13 | // copy, modify, merge, publish, distribute, sublicense, and/or sell
14 | // copies of the Software, and to permit persons to whom the
15 | // Software is furnished to do so, subject to the following
16 | // conditions:
17 | //
18 | // The above copyright notice and this permission notice shall be
19 | // included in all copies or substantial portions of the Software.
20 | //
21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
23 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
25 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
26 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
28 | // OTHER DEALINGS IN THE SOFTWARE.
29 | //
30 |
31 | #import
32 |
33 | @implementation UIImage (Diff)
34 |
35 | - (UIImage *)fb_diffWithImage:(UIImage *)image
36 | {
37 | if (!image) {
38 | return nil;
39 | }
40 | CGSize imageSize = CGSizeMake(MAX(self.size.width, image.size.width), MAX(self.size.height, image.size.height));
41 | UIGraphicsBeginImageContextWithOptions(imageSize, YES, 0);
42 | CGContextRef context = UIGraphicsGetCurrentContext();
43 | [self drawInRect:CGRectMake(0, 0, self.size.width, self.size.height)];
44 | CGContextSetAlpha(context, 0.5);
45 | CGContextBeginTransparencyLayer(context, NULL);
46 | [image drawInRect:CGRectMake(0, 0, image.size.width, image.size.height)];
47 | CGContextSetBlendMode(context, kCGBlendModeDifference);
48 | CGContextSetFillColorWithColor(context,[UIColor whiteColor].CGColor);
49 | CGContextFillRect(context, CGRectMake(0, 0, self.size.width, self.size.height));
50 | CGContextEndTransparencyLayer(context);
51 | UIImage *returnImage = UIGraphicsGetImageFromCurrentImageContext();
52 | UIGraphicsEndImageContext();
53 | return returnImage;
54 | }
55 |
56 | @end
57 |
--------------------------------------------------------------------------------
/Example/Pods/FBSnapshotTestCase/FBSnapshotTestCase/Categories/UIImage+Snapshot.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2015, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | *
9 | */
10 |
11 | #import
12 |
13 | @interface UIImage (Snapshot)
14 |
15 | /// Uses renderInContext: to get a snapshot of the layer.
16 | + (UIImage *)fb_imageForLayer:(CALayer *)layer;
17 |
18 | /// Uses renderInContext: to get a snapshot of the view layer.
19 | + (UIImage *)fb_imageForViewLayer:(UIView *)view;
20 |
21 | /// Uses drawViewHierarchyInRect: to get a snapshot of the view and adds the view into a window if needed.
22 | + (UIImage *)fb_imageForView:(UIView *)view;
23 |
24 | @end
25 |
--------------------------------------------------------------------------------
/Example/Pods/FBSnapshotTestCase/FBSnapshotTestCase/Categories/UIImage+Snapshot.m:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2015, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | *
9 | */
10 |
11 | #import
12 | #import
13 |
14 | @implementation UIImage (Snapshot)
15 |
16 | + (UIImage *)fb_imageForLayer:(CALayer *)layer
17 | {
18 | CGRect bounds = layer.bounds;
19 | NSAssert1(CGRectGetWidth(bounds), @"Zero width for layer %@", layer);
20 | NSAssert1(CGRectGetHeight(bounds), @"Zero height for layer %@", layer);
21 |
22 | UIGraphicsBeginImageContextWithOptions(bounds.size, NO, 0);
23 | CGContextRef context = UIGraphicsGetCurrentContext();
24 | NSAssert1(context, @"Could not generate context for layer %@", layer);
25 | CGContextSaveGState(context);
26 | [layer layoutIfNeeded];
27 | [layer renderInContext:context];
28 | CGContextRestoreGState(context);
29 |
30 | UIImage *snapshot = UIGraphicsGetImageFromCurrentImageContext();
31 | UIGraphicsEndImageContext();
32 | return snapshot;
33 | }
34 |
35 | + (UIImage *)fb_imageForViewLayer:(UIView *)view
36 | {
37 | [view layoutIfNeeded];
38 | return [self fb_imageForLayer:view.layer];
39 | }
40 |
41 | + (UIImage *)fb_imageForView:(UIView *)view
42 | {
43 | CGRect bounds = view.bounds;
44 | NSAssert1(CGRectGetWidth(bounds), @"Zero width for view %@", view);
45 | NSAssert1(CGRectGetHeight(bounds), @"Zero height for view %@", view);
46 |
47 | // If the input view is already a UIWindow, then just use that. Otherwise wrap in a window.
48 | UIWindow *window = [view isKindOfClass:[UIWindow class]] ? (UIWindow *)view : view.window;
49 | BOOL removeFromSuperview = NO;
50 | if (!window) {
51 | window = [[UIApplication sharedApplication] fb_strictKeyWindow];
52 | }
53 |
54 | if (!view.window && view != window) {
55 | [window addSubview:view];
56 | removeFromSuperview = YES;
57 | }
58 |
59 | UIGraphicsBeginImageContextWithOptions(bounds.size, NO, 0);
60 | [view layoutIfNeeded];
61 | [view drawViewHierarchyInRect:view.bounds afterScreenUpdates:YES];
62 |
63 | UIImage *snapshot = UIGraphicsGetImageFromCurrentImageContext();
64 | UIGraphicsEndImageContext();
65 |
66 | if (removeFromSuperview) {
67 | [view removeFromSuperview];
68 | }
69 |
70 | return snapshot;
71 | }
72 |
73 | @end
74 |
--------------------------------------------------------------------------------
/Example/Pods/FBSnapshotTestCase/FBSnapshotTestCase/FBSnapshotTestCase.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2015, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | *
9 | */
10 |
11 | #import
12 | #import
13 |
14 | #import
15 |
16 | #import
17 |
18 | #import
19 |
20 | /*
21 | There are three ways of setting reference image directories.
22 |
23 | 1. Set the preprocessor macro FB_REFERENCE_IMAGE_DIR to a double quoted
24 | c-string with the path.
25 | 2. Set an environment variable named FB_REFERENCE_IMAGE_DIR with the path. This
26 | takes precedence over the preprocessor macro to allow for run-time override.
27 | 3. Keep everything unset, which will cause the reference images to be looked up
28 | inside the bundle holding the current test, in the
29 | Resources/ReferenceImages_* directories.
30 | */
31 | #ifndef FB_REFERENCE_IMAGE_DIR
32 | #define FB_REFERENCE_IMAGE_DIR ""
33 | #endif
34 |
35 | /**
36 | Similar to our much-loved XCTAssert() macros. Use this to perform your test. No need to write an explanation, though.
37 | @param view The view to snapshot
38 | @param identifier An optional identifier, used if there are multiple snapshot tests in a given -test method.
39 | @param suffixes An NSOrderedSet of strings for the different suffixes
40 | @param tolerance The percentage of pixels that can differ and still count as an 'identical' view
41 | */
42 | #define FBSnapshotVerifyViewWithOptions(view__, identifier__, suffixes__, tolerance__) \
43 | FBSnapshotVerifyViewOrLayerWithOptions(View, view__, identifier__, suffixes__, tolerance__)
44 |
45 | #define FBSnapshotVerifyView(view__, identifier__) \
46 | FBSnapshotVerifyViewWithOptions(view__, identifier__, FBSnapshotTestCaseDefaultSuffixes(), 0)
47 |
48 |
49 | /**
50 | Similar to our much-loved XCTAssert() macros. Use this to perform your test. No need to write an explanation, though.
51 | @param layer The layer to snapshot
52 | @param identifier An optional identifier, used if there are multiple snapshot tests in a given -test method.
53 | @param suffixes An NSOrderedSet of strings for the different suffixes
54 | @param tolerance The percentage of pixels that can differ and still count as an 'identical' layer
55 | */
56 | #define FBSnapshotVerifyLayerWithOptions(layer__, identifier__, suffixes__, tolerance__) \
57 | FBSnapshotVerifyViewOrLayerWithOptions(Layer, layer__, identifier__, suffixes__, tolerance__)
58 |
59 | #define FBSnapshotVerifyLayer(layer__, identifier__) \
60 | FBSnapshotVerifyLayerWithOptions(layer__, identifier__, FBSnapshotTestCaseDefaultSuffixes(), 0)
61 |
62 |
63 | #define FBSnapshotVerifyViewOrLayerWithOptions(what__, viewOrLayer__, identifier__, suffixes__, tolerance__) \
64 | { \
65 | NSString *errorDescription = [self snapshotVerifyViewOrLayer:viewOrLayer__ identifier:identifier__ suffixes:suffixes__ tolerance:tolerance__]; \
66 | BOOL noErrors = (errorDescription == nil); \
67 | XCTAssertTrue(noErrors, @"%@", errorDescription); \
68 | }
69 |
70 |
71 | /**
72 | The base class of view snapshotting tests. If you have small UI component, it's often easier to configure it in a test
73 | and compare an image of the view to a reference image that write lots of complex layout-code tests.
74 |
75 | In order to flip the tests in your subclass to record the reference images set @c recordMode to @c YES.
76 |
77 | @attention When recording, the reference image directory should be explicitly
78 | set, otherwise the images may be written to somewhere inside the
79 | simulator directory.
80 |
81 | For example:
82 | @code
83 | - (void)setUp
84 | {
85 | [super setUp];
86 | self.recordMode = YES;
87 | }
88 | @endcode
89 | */
90 | @interface FBSnapshotTestCase : XCTestCase
91 |
92 | /**
93 | When YES, the test macros will save reference images, rather than performing an actual test.
94 | */
95 | @property (readwrite, nonatomic, assign) BOOL recordMode;
96 |
97 | /**
98 | When @c YES appends the name of the device model and OS to the snapshot file name.
99 | The default value is @c NO.
100 | */
101 | @property (readwrite, nonatomic, assign, getter=isDeviceAgnostic) BOOL deviceAgnostic;
102 |
103 | /**
104 | When YES, renders a snapshot of the complete view hierarchy as visible onscreen.
105 | There are several things that do not work if renderInContext: is used.
106 | - UIVisualEffect #70
107 | - UIAppearance #91
108 | - Size Classes #92
109 |
110 | @attention If the view does't belong to a UIWindow, it will create one and add the view as a subview.
111 | */
112 | @property (readwrite, nonatomic, assign) BOOL usesDrawViewHierarchyInRect;
113 |
114 | - (void)setUp NS_REQUIRES_SUPER;
115 | - (void)tearDown NS_REQUIRES_SUPER;
116 |
117 | /**
118 | Performs the comparison or records a snapshot of the layer if recordMode is YES.
119 | @param viewOrLayer The UIView or CALayer to snapshot
120 | @param identifier An optional identifier, used if there are multiple snapshot tests in a given -test method.
121 | @param suffixes An NSOrderedSet of strings for the different suffixes
122 | @param tolerance The percentage difference to still count as identical - 0 mean pixel perfect, 1 means I don't care
123 | @returns nil if the comparison (or saving of the reference image) succeeded. Otherwise it contains an error description.
124 | */
125 | - (NSString *)snapshotVerifyViewOrLayer:(id)viewOrLayer
126 | identifier:(NSString *)identifier
127 | suffixes:(NSOrderedSet *)suffixes
128 | tolerance:(CGFloat)tolerance;
129 |
130 | /**
131 | Performs the comparison or records a snapshot of the layer if recordMode is YES.
132 | @param layer The Layer to snapshot
133 | @param referenceImagesDirectory The directory in which reference images are stored.
134 | @param identifier An optional identifier, used if there are multiple snapshot tests in a given -test method.
135 | @param tolerance The percentage difference to still count as identical - 0 mean pixel perfect, 1 means I don't care
136 | @param errorPtr An error to log in an XCTAssert() macro if the method fails (missing reference image, images differ, etc).
137 | @returns YES if the comparison (or saving of the reference image) succeeded.
138 | */
139 | - (BOOL)compareSnapshotOfLayer:(CALayer *)layer
140 | referenceImagesDirectory:(NSString *)referenceImagesDirectory
141 | identifier:(NSString *)identifier
142 | tolerance:(CGFloat)tolerance
143 | error:(NSError **)errorPtr;
144 |
145 | /**
146 | Performs the comparison or records a snapshot of the view if recordMode is YES.
147 | @param view The view to snapshot
148 | @param referenceImagesDirectory The directory in which reference images are stored.
149 | @param identifier An optional identifier, used if there are multiple snapshot tests in a given -test method.
150 | @param tolerance The percentage difference to still count as identical - 0 mean pixel perfect, 1 means I don't care
151 | @param errorPtr An error to log in an XCTAssert() macro if the method fails (missing reference image, images differ, etc).
152 | @returns YES if the comparison (or saving of the reference image) succeeded.
153 | */
154 | - (BOOL)compareSnapshotOfView:(UIView *)view
155 | referenceImagesDirectory:(NSString *)referenceImagesDirectory
156 | identifier:(NSString *)identifier
157 | tolerance:(CGFloat)tolerance
158 | error:(NSError **)errorPtr;
159 |
160 | /**
161 | Checks if reference image with identifier based name exists in the reference images directory.
162 | @param referenceImagesDirectory The directory in which reference images are stored.
163 | @param identifier An optional identifier, used if there are multiple snapshot tests in a given -test method.
164 | @param errorPtr An error to log in an XCTAssert() macro if the method fails (missing reference image, images differ, etc).
165 | @returns YES if reference image exists.
166 | */
167 | - (BOOL)referenceImageRecordedInDirectory:(NSString *)referenceImagesDirectory
168 | identifier:(NSString *)identifier
169 | error:(NSError **)errorPtr;
170 |
171 | /**
172 | Returns the reference image directory.
173 |
174 | Helper function used to implement the assert macros.
175 |
176 | @param dir directory to use if environment variable not specified. Ignored if null or empty.
177 | */
178 | - (NSString *)getReferenceImageDirectoryWithDefault:(NSString *)dir;
179 |
180 | @end
181 |
--------------------------------------------------------------------------------
/Example/Pods/FBSnapshotTestCase/FBSnapshotTestCase/FBSnapshotTestCase.m:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2015, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | *
9 | */
10 |
11 | #import
12 | #import
13 |
14 | @implementation FBSnapshotTestCase
15 | {
16 | FBSnapshotTestController *_snapshotController;
17 | }
18 |
19 | #pragma mark - Overrides
20 |
21 | - (void)setUp
22 | {
23 | [super setUp];
24 | _snapshotController = [[FBSnapshotTestController alloc] initWithTestName:NSStringFromClass([self class])];
25 | }
26 |
27 | - (void)tearDown
28 | {
29 | _snapshotController = nil;
30 | [super tearDown];
31 | }
32 |
33 | - (BOOL)recordMode
34 | {
35 | return _snapshotController.recordMode;
36 | }
37 |
38 | - (void)setRecordMode:(BOOL)recordMode
39 | {
40 | NSAssert1(_snapshotController, @"%s cannot be called before [super setUp]", __FUNCTION__);
41 | _snapshotController.recordMode = recordMode;
42 | }
43 |
44 | - (BOOL)isDeviceAgnostic
45 | {
46 | return _snapshotController.deviceAgnostic;
47 | }
48 |
49 | - (void)setDeviceAgnostic:(BOOL)deviceAgnostic
50 | {
51 | NSAssert1(_snapshotController, @"%s cannot be called before [super setUp]", __FUNCTION__);
52 | _snapshotController.deviceAgnostic = deviceAgnostic;
53 | }
54 |
55 | - (BOOL)usesDrawViewHierarchyInRect
56 | {
57 | return _snapshotController.usesDrawViewHierarchyInRect;
58 | }
59 |
60 | - (void)setUsesDrawViewHierarchyInRect:(BOOL)usesDrawViewHierarchyInRect
61 | {
62 | NSAssert1(_snapshotController, @"%s cannot be called before [super setUp]", __FUNCTION__);
63 | _snapshotController.usesDrawViewHierarchyInRect = usesDrawViewHierarchyInRect;
64 | }
65 |
66 | #pragma mark - Public API
67 |
68 | - (NSString *)snapshotVerifyViewOrLayer:(id)viewOrLayer
69 | identifier:(NSString *)identifier
70 | suffixes:(NSOrderedSet *)suffixes
71 | tolerance:(CGFloat)tolerance
72 | {
73 | if (nil == viewOrLayer) {
74 | return @"Object to be snapshotted must not be nil";
75 | }
76 | NSString *referenceImageDirectory = [self getReferenceImageDirectoryWithDefault:(@ FB_REFERENCE_IMAGE_DIR)];
77 | if (referenceImageDirectory == nil) {
78 | return @"Missing value for referenceImagesDirectory - Set FB_REFERENCE_IMAGE_DIR as Environment variable in your scheme.";
79 | }
80 | if (suffixes.count == 0) {
81 | return [NSString stringWithFormat:@"Suffixes set cannot be empty %@", suffixes];
82 | }
83 |
84 | BOOL testSuccess = NO;
85 | NSError *error = nil;
86 | NSMutableArray *errors = [NSMutableArray array];
87 |
88 | if (self.recordMode) {
89 | NSString *referenceImagesDirectory = [NSString stringWithFormat:@"%@%@", referenceImageDirectory, suffixes.firstObject];
90 | BOOL referenceImageSaved = [self _compareSnapshotOfViewOrLayer:viewOrLayer referenceImagesDirectory:referenceImagesDirectory identifier:(identifier) tolerance:tolerance error:&error];
91 | if (!referenceImageSaved) {
92 | [errors addObject:error];
93 | }
94 | } else {
95 | for (NSString *suffix in suffixes) {
96 | NSString *referenceImagesDirectory = [NSString stringWithFormat:@"%@%@", referenceImageDirectory, suffix];
97 | BOOL referenceImageAvailable = [self referenceImageRecordedInDirectory:referenceImagesDirectory identifier:(identifier) error:&error];
98 |
99 | if (referenceImageAvailable) {
100 | BOOL comparisonSuccess = [self _compareSnapshotOfViewOrLayer:viewOrLayer referenceImagesDirectory:referenceImagesDirectory identifier:identifier tolerance:tolerance error:&error];
101 | [errors removeAllObjects];
102 | if (comparisonSuccess) {
103 | testSuccess = YES;
104 | break;
105 | } else {
106 | [errors addObject:error];
107 | }
108 | } else {
109 | [errors addObject:error];
110 | }
111 | }
112 | }
113 |
114 | if (!testSuccess) {
115 | return [NSString stringWithFormat:@"Snapshot comparison failed: %@", errors.firstObject];
116 | }
117 | if (self.recordMode) {
118 | return @"Test ran in record mode. Reference image is now saved. Disable record mode to perform an actual snapshot comparison!";
119 | }
120 |
121 | return nil;
122 | }
123 |
124 | - (BOOL)compareSnapshotOfLayer:(CALayer *)layer
125 | referenceImagesDirectory:(NSString *)referenceImagesDirectory
126 | identifier:(NSString *)identifier
127 | tolerance:(CGFloat)tolerance
128 | error:(NSError **)errorPtr
129 | {
130 | return [self _compareSnapshotOfViewOrLayer:layer
131 | referenceImagesDirectory:referenceImagesDirectory
132 | identifier:identifier
133 | tolerance:tolerance
134 | error:errorPtr];
135 | }
136 |
137 | - (BOOL)compareSnapshotOfView:(UIView *)view
138 | referenceImagesDirectory:(NSString *)referenceImagesDirectory
139 | identifier:(NSString *)identifier
140 | tolerance:(CGFloat)tolerance
141 | error:(NSError **)errorPtr
142 | {
143 | return [self _compareSnapshotOfViewOrLayer:view
144 | referenceImagesDirectory:referenceImagesDirectory
145 | identifier:identifier
146 | tolerance:tolerance
147 | error:errorPtr];
148 | }
149 |
150 | - (BOOL)referenceImageRecordedInDirectory:(NSString *)referenceImagesDirectory
151 | identifier:(NSString *)identifier
152 | error:(NSError **)errorPtr
153 | {
154 | NSAssert1(_snapshotController, @"%s cannot be called before [super setUp]", __FUNCTION__);
155 | _snapshotController.referenceImagesDirectory = referenceImagesDirectory;
156 | UIImage *referenceImage = [_snapshotController referenceImageForSelector:self.invocation.selector
157 | identifier:identifier
158 | error:errorPtr];
159 |
160 | return (referenceImage != nil);
161 | }
162 |
163 | - (NSString *)getReferenceImageDirectoryWithDefault:(NSString *)dir
164 | {
165 | NSString *envReferenceImageDirectory = [NSProcessInfo processInfo].environment[@"FB_REFERENCE_IMAGE_DIR"];
166 | if (envReferenceImageDirectory) {
167 | return envReferenceImageDirectory;
168 | }
169 | if (dir && dir.length > 0) {
170 | return dir;
171 | }
172 | return [[NSBundle bundleForClass:self.class].resourcePath stringByAppendingPathComponent:@"ReferenceImages"];
173 | }
174 |
175 |
176 | #pragma mark - Private API
177 |
178 | - (BOOL)_compareSnapshotOfViewOrLayer:(id)viewOrLayer
179 | referenceImagesDirectory:(NSString *)referenceImagesDirectory
180 | identifier:(NSString *)identifier
181 | tolerance:(CGFloat)tolerance
182 | error:(NSError **)errorPtr
183 | {
184 | _snapshotController.referenceImagesDirectory = referenceImagesDirectory;
185 | return [_snapshotController compareSnapshotOfViewOrLayer:viewOrLayer
186 | selector:self.invocation.selector
187 | identifier:identifier
188 | tolerance:tolerance
189 | error:errorPtr];
190 | }
191 |
192 | @end
193 |
--------------------------------------------------------------------------------
/Example/Pods/FBSnapshotTestCase/FBSnapshotTestCase/FBSnapshotTestCasePlatform.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2015, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | *
9 | */
10 |
11 | #import
12 |
13 | #ifdef __cplusplus
14 | extern "C" {
15 | #endif
16 |
17 | /**
18 | Returns a Boolean value that indicates whether the snapshot test is running in 64Bit.
19 | This method is a convenience for creating the suffixes set based on the architecture
20 | that the test is running.
21 |
22 | @returns @c YES if the test is running in 64bit, otherwise @c NO.
23 | */
24 | BOOL FBSnapshotTestCaseIs64Bit(void);
25 |
26 | /**
27 | Returns a default set of strings that is used to append a suffix based on the architectures.
28 | @warning Do not modify this function, you can create your own and use it with @c FBSnapshotVerifyViewWithOptions()
29 |
30 | @returns An @c NSOrderedSet object containing strings that are appended to the reference images directory.
31 | */
32 | NSOrderedSet *FBSnapshotTestCaseDefaultSuffixes(void);
33 |
34 | /**
35 | Returns a fully «normalized» file name.
36 | Strips punctuation and spaces and replaces them with @c _. Also appends the device model, running OS and screen size to the file name.
37 |
38 | @returns An @c NSString object containing the passed @c fileName with the device model, OS and screen size appended at the end.
39 | */
40 | NSString *FBDeviceAgnosticNormalizedFileName(NSString *fileName);
41 |
42 | #ifdef __cplusplus
43 | }
44 | #endif
45 |
--------------------------------------------------------------------------------
/Example/Pods/FBSnapshotTestCase/FBSnapshotTestCase/FBSnapshotTestCasePlatform.m:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2015, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | *
9 | */
10 |
11 | #import
12 | #import
13 | #import
14 |
15 | BOOL FBSnapshotTestCaseIs64Bit(void)
16 | {
17 | #if __LP64__
18 | return YES;
19 | #else
20 | return NO;
21 | #endif
22 | }
23 |
24 | NSOrderedSet *FBSnapshotTestCaseDefaultSuffixes(void)
25 | {
26 | NSMutableOrderedSet *suffixesSet = [[NSMutableOrderedSet alloc] init];
27 | [suffixesSet addObject:@"_32"];
28 | [suffixesSet addObject:@"_64"];
29 | if (FBSnapshotTestCaseIs64Bit()) {
30 | return [suffixesSet reversedOrderedSet];
31 | }
32 | return [suffixesSet copy];
33 | }
34 |
35 | NSString *FBDeviceAgnosticNormalizedFileName(NSString *fileName)
36 | {
37 | UIDevice *device = [UIDevice currentDevice];
38 | UIWindow *keyWindow = [[UIApplication sharedApplication] fb_strictKeyWindow];
39 | CGSize screenSize = keyWindow.bounds.size;
40 | NSString *os = device.systemVersion;
41 |
42 | fileName = [NSString stringWithFormat:@"%@_%@%@_%.0fx%.0f", fileName, device.model, os, screenSize.width, screenSize.height];
43 |
44 | NSMutableCharacterSet *invalidCharacters = [NSMutableCharacterSet new];
45 | [invalidCharacters formUnionWithCharacterSet:[NSCharacterSet whitespaceCharacterSet]];
46 | [invalidCharacters formUnionWithCharacterSet:[NSCharacterSet punctuationCharacterSet]];
47 | NSArray *validComponents = [fileName componentsSeparatedByCharactersInSet:invalidCharacters];
48 | fileName = [validComponents componentsJoinedByString:@"_"];
49 |
50 | return fileName;
51 | }
--------------------------------------------------------------------------------
/Example/Pods/FBSnapshotTestCase/FBSnapshotTestCase/FBSnapshotTestController.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2015, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | *
9 | */
10 |
11 | #import
12 | #import
13 |
14 | typedef NS_ENUM(NSInteger, FBSnapshotTestControllerErrorCode) {
15 | FBSnapshotTestControllerErrorCodeUnknown,
16 | FBSnapshotTestControllerErrorCodeNeedsRecord,
17 | FBSnapshotTestControllerErrorCodePNGCreationFailed,
18 | FBSnapshotTestControllerErrorCodeImagesDifferentSizes,
19 | FBSnapshotTestControllerErrorCodeImagesDifferent,
20 | };
21 | /**
22 | Errors returned by the methods of FBSnapshotTestController use this domain.
23 | */
24 | extern NSString *const FBSnapshotTestControllerErrorDomain;
25 |
26 | /**
27 | Errors returned by the methods of FBSnapshotTestController sometimes contain this key in the `userInfo` dictionary.
28 | */
29 | extern NSString *const FBReferenceImageFilePathKey;
30 |
31 | /**
32 | Errors returned by the methods of FBSnapshotTestController sometimes contain this key in the `userInfo` dictionary.
33 | */
34 | extern NSString *const FBReferenceImageKey;
35 |
36 | /**
37 | Errors returned by the methods of FBSnapshotTestController sometimes contain this key in the `userInfo` dictionary.
38 | */
39 | extern NSString *const FBCapturedImageKey;
40 |
41 | /**
42 | Errors returned by the methods of FBSnapshotTestController sometimes contain this key in the `userInfo` dictionary.
43 | */
44 | extern NSString *const FBDiffedImageKey;
45 |
46 | /**
47 | Provides the heavy-lifting for FBSnapshotTestCase. It loads and saves images, along with performing the actual pixel-
48 | by-pixel comparison of images.
49 | Instances are initialized with the test class, and directories to read and write to.
50 | */
51 | @interface FBSnapshotTestController : NSObject
52 |
53 | /**
54 | Record snapshots.
55 | */
56 | @property (readwrite, nonatomic, assign) BOOL recordMode;
57 |
58 | /**
59 | When @c YES appends the name of the device model and OS to the snapshot file name.
60 | The default value is @c NO.
61 | */
62 | @property (readwrite, nonatomic, assign, getter=isDeviceAgnostic) BOOL deviceAgnostic;
63 |
64 | /**
65 | Uses drawViewHierarchyInRect:afterScreenUpdates: to draw the image instead of renderInContext:
66 | */
67 | @property (readwrite, nonatomic, assign) BOOL usesDrawViewHierarchyInRect;
68 |
69 | /**
70 | The directory in which referfence images are stored.
71 | */
72 | @property (readwrite, nonatomic, copy) NSString *referenceImagesDirectory;
73 |
74 | /**
75 | @param testClass The subclass of FBSnapshotTestCase that is using this controller.
76 | @returns An instance of FBSnapshotTestController.
77 | */
78 | - (instancetype)initWithTestClass:(Class)testClass;
79 |
80 | /**
81 | Designated initializer.
82 | @param testName The name of the tests.
83 | @returns An instance of FBSnapshotTestController.
84 | */
85 | - (instancetype)initWithTestName:(NSString *)testName;
86 |
87 | /**
88 | Performs the comparison of the layer.
89 | @param layer The Layer to snapshot.
90 | @param selector The test method being run.
91 | @param identifier An optional identifier, used is there are muliptle snapshot tests in a given -test method.
92 | @param error An error to log in an XCTAssert() macro if the method fails (missing reference image, images differ, etc).
93 | @returns YES if the comparison (or saving of the reference image) succeeded.
94 | */
95 | - (BOOL)compareSnapshotOfLayer:(CALayer *)layer
96 | selector:(SEL)selector
97 | identifier:(NSString *)identifier
98 | error:(NSError **)errorPtr;
99 |
100 | /**
101 | Performs the comparison of the view.
102 | @param view The view to snapshot.
103 | @param selector The test method being run.
104 | @param identifier An optional identifier, used is there are muliptle snapshot tests in a given -test method.
105 | @param error An error to log in an XCTAssert() macro if the method fails (missing reference image, images differ, etc).
106 | @returns YES if the comparison (or saving of the reference image) succeeded.
107 | */
108 | - (BOOL)compareSnapshotOfView:(UIView *)view
109 | selector:(SEL)selector
110 | identifier:(NSString *)identifier
111 | error:(NSError **)errorPtr;
112 |
113 | /**
114 | Performs the comparison of a view or layer.
115 | @param view The view or layer to snapshot.
116 | @param selector The test method being run.
117 | @param identifier An optional identifier, used is there are muliptle snapshot tests in a given -test method.
118 | @param tolerance The percentage of pixels that can differ and still be considered 'identical'
119 | @param error An error to log in an XCTAssert() macro if the method fails (missing reference image, images differ, etc).
120 | @returns YES if the comparison (or saving of the reference image) succeeded.
121 | */
122 | - (BOOL)compareSnapshotOfViewOrLayer:(id)viewOrLayer
123 | selector:(SEL)selector
124 | identifier:(NSString *)identifier
125 | tolerance:(CGFloat)tolerance
126 | error:(NSError **)errorPtr;
127 |
128 | /**
129 | Loads a reference image.
130 | @param selector The test method being run.
131 | @param identifier The optional identifier, used when multiple images are tested in a single -test method.
132 | @param errorPtr An error, if this methods returns nil, the error will be something useful.
133 | @returns An image.
134 | */
135 | - (UIImage *)referenceImageForSelector:(SEL)selector
136 | identifier:(NSString *)identifier
137 | error:(NSError **)errorPtr;
138 |
139 | /**
140 | Performs a pixel-by-pixel comparison of the two images with an allowable margin of error.
141 | @param referenceImage The reference (correct) image.
142 | @param image The image to test against the reference.
143 | @param tolerance The percentage of pixels that can differ and still be considered 'identical'
144 | @param errorPtr An error that indicates why the comparison failed if it does.
145 | @returns YES if the comparison succeeded and the images are the same(ish).
146 | */
147 | - (BOOL)compareReferenceImage:(UIImage *)referenceImage
148 | toImage:(UIImage *)image
149 | tolerance:(CGFloat)tolerance
150 | error:(NSError **)errorPtr;
151 |
152 | /**
153 | Saves the reference image and the test image to `failedOutputDirectory`.
154 | @param referenceImage The reference (correct) image.
155 | @param testImage The image to test against the reference.
156 | @param selector The test method being run.
157 | @param identifier The optional identifier, used when multiple images are tested in a single -test method.
158 | @param errorPtr An error that indicates why the comparison failed if it does.
159 | @returns YES if the save succeeded.
160 | */
161 | - (BOOL)saveFailedReferenceImage:(UIImage *)referenceImage
162 | testImage:(UIImage *)testImage
163 | selector:(SEL)selector
164 | identifier:(NSString *)identifier
165 | error:(NSError **)errorPtr;
166 | @end
167 |
--------------------------------------------------------------------------------
/Example/Pods/FBSnapshotTestCase/FBSnapshotTestCase/FBSnapshotTestController.m:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2015, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | *
9 | */
10 |
11 | #import
12 | #import
13 | #import
14 | #import
15 | #import
16 |
17 | #import
18 |
19 | NSString *const FBSnapshotTestControllerErrorDomain = @"FBSnapshotTestControllerErrorDomain";
20 | NSString *const FBReferenceImageFilePathKey = @"FBReferenceImageFilePathKey";
21 | NSString *const FBReferenceImageKey = @"FBReferenceImageKey";
22 | NSString *const FBCapturedImageKey = @"FBCapturedImageKey";
23 | NSString *const FBDiffedImageKey = @"FBDiffedImageKey";
24 |
25 | typedef NS_ENUM(NSUInteger, FBTestSnapshotFileNameType) {
26 | FBTestSnapshotFileNameTypeReference,
27 | FBTestSnapshotFileNameTypeFailedReference,
28 | FBTestSnapshotFileNameTypeFailedTest,
29 | FBTestSnapshotFileNameTypeFailedTestDiff,
30 | };
31 |
32 | @implementation FBSnapshotTestController
33 | {
34 | NSString *_testName;
35 | NSFileManager *_fileManager;
36 | }
37 |
38 | #pragma mark - Initializers
39 |
40 | - (instancetype)initWithTestClass:(Class)testClass;
41 | {
42 | return [self initWithTestName:NSStringFromClass(testClass)];
43 | }
44 |
45 | - (instancetype)initWithTestName:(NSString *)testName
46 | {
47 | if (self = [super init]) {
48 | _testName = [testName copy];
49 | _deviceAgnostic = NO;
50 |
51 | _fileManager = [[NSFileManager alloc] init];
52 | }
53 | return self;
54 | }
55 |
56 | #pragma mark - Overrides
57 |
58 | - (NSString *)description
59 | {
60 | return [NSString stringWithFormat:@"%@ %@", [super description], _referenceImagesDirectory];
61 | }
62 |
63 | #pragma mark - Public API
64 |
65 | - (BOOL)compareSnapshotOfLayer:(CALayer *)layer
66 | selector:(SEL)selector
67 | identifier:(NSString *)identifier
68 | error:(NSError **)errorPtr
69 | {
70 | return [self compareSnapshotOfViewOrLayer:layer
71 | selector:selector
72 | identifier:identifier
73 | tolerance:0
74 | error:errorPtr];
75 | }
76 |
77 | - (BOOL)compareSnapshotOfView:(UIView *)view
78 | selector:(SEL)selector
79 | identifier:(NSString *)identifier
80 | error:(NSError **)errorPtr
81 | {
82 | return [self compareSnapshotOfViewOrLayer:view
83 | selector:selector
84 | identifier:identifier
85 | tolerance:0
86 | error:errorPtr];
87 | }
88 |
89 | - (BOOL)compareSnapshotOfViewOrLayer:(id)viewOrLayer
90 | selector:(SEL)selector
91 | identifier:(NSString *)identifier
92 | tolerance:(CGFloat)tolerance
93 | error:(NSError **)errorPtr
94 | {
95 | if (self.recordMode) {
96 | return [self _recordSnapshotOfViewOrLayer:viewOrLayer selector:selector identifier:identifier error:errorPtr];
97 | } else {
98 | return [self _performPixelComparisonWithViewOrLayer:viewOrLayer selector:selector identifier:identifier tolerance:tolerance error:errorPtr];
99 | }
100 | }
101 |
102 | - (UIImage *)referenceImageForSelector:(SEL)selector
103 | identifier:(NSString *)identifier
104 | error:(NSError **)errorPtr
105 | {
106 | NSString *filePath = [self _referenceFilePathForSelector:selector identifier:identifier];
107 | UIImage *image = [UIImage imageWithContentsOfFile:filePath];
108 | if (nil == image && NULL != errorPtr) {
109 | BOOL exists = [_fileManager fileExistsAtPath:filePath];
110 | if (!exists) {
111 | *errorPtr = [NSError errorWithDomain:FBSnapshotTestControllerErrorDomain
112 | code:FBSnapshotTestControllerErrorCodeNeedsRecord
113 | userInfo:@{
114 | FBReferenceImageFilePathKey: filePath,
115 | NSLocalizedDescriptionKey: @"Unable to load reference image.",
116 | NSLocalizedFailureReasonErrorKey: @"Reference image not found. You need to run the test in record mode",
117 | }];
118 | } else {
119 | *errorPtr = [NSError errorWithDomain:FBSnapshotTestControllerErrorDomain
120 | code:FBSnapshotTestControllerErrorCodeUnknown
121 | userInfo:nil];
122 | }
123 | }
124 | return image;
125 | }
126 |
127 | - (BOOL)compareReferenceImage:(UIImage *)referenceImage
128 | toImage:(UIImage *)image
129 | tolerance:(CGFloat)tolerance
130 | error:(NSError **)errorPtr
131 | {
132 | BOOL sameImageDimensions = CGSizeEqualToSize(referenceImage.size, image.size);
133 | if (sameImageDimensions && [referenceImage fb_compareWithImage:image tolerance:tolerance]) {
134 | return YES;
135 | }
136 |
137 | if (NULL != errorPtr) {
138 | NSString *errorDescription = sameImageDimensions ? @"Images different" : @"Images different sizes";
139 | NSString *errorReason = sameImageDimensions ? [NSString stringWithFormat:@"image pixels differed by more than %.2f%% from the reference image", tolerance * 100]
140 | : [NSString stringWithFormat:@"referenceImage:%@, image:%@", NSStringFromCGSize(referenceImage.size), NSStringFromCGSize(image.size)];
141 | FBSnapshotTestControllerErrorCode errorCode = sameImageDimensions ? FBSnapshotTestControllerErrorCodeImagesDifferent : FBSnapshotTestControllerErrorCodeImagesDifferentSizes;
142 |
143 | *errorPtr = [NSError errorWithDomain:FBSnapshotTestControllerErrorDomain
144 | code:errorCode
145 | userInfo:@{
146 | NSLocalizedDescriptionKey: errorDescription,
147 | NSLocalizedFailureReasonErrorKey: errorReason,
148 | FBReferenceImageKey: referenceImage,
149 | FBCapturedImageKey: image,
150 | FBDiffedImageKey: [referenceImage fb_diffWithImage:image],
151 | }];
152 | }
153 | return NO;
154 | }
155 |
156 | - (BOOL)saveFailedReferenceImage:(UIImage *)referenceImage
157 | testImage:(UIImage *)testImage
158 | selector:(SEL)selector
159 | identifier:(NSString *)identifier
160 | error:(NSError **)errorPtr
161 | {
162 | NSData *referencePNGData = UIImagePNGRepresentation(referenceImage);
163 | NSData *testPNGData = UIImagePNGRepresentation(testImage);
164 |
165 | NSString *referencePath = [self _failedFilePathForSelector:selector
166 | identifier:identifier
167 | fileNameType:FBTestSnapshotFileNameTypeFailedReference];
168 |
169 | NSError *creationError = nil;
170 | BOOL didCreateDir = [_fileManager createDirectoryAtPath:[referencePath stringByDeletingLastPathComponent]
171 | withIntermediateDirectories:YES
172 | attributes:nil
173 | error:&creationError];
174 | if (!didCreateDir) {
175 | if (NULL != errorPtr) {
176 | *errorPtr = creationError;
177 | }
178 | return NO;
179 | }
180 |
181 | if (![referencePNGData writeToFile:referencePath options:NSDataWritingAtomic error:errorPtr]) {
182 | return NO;
183 | }
184 |
185 | NSString *testPath = [self _failedFilePathForSelector:selector
186 | identifier:identifier
187 | fileNameType:FBTestSnapshotFileNameTypeFailedTest];
188 |
189 | if (![testPNGData writeToFile:testPath options:NSDataWritingAtomic error:errorPtr]) {
190 | return NO;
191 | }
192 |
193 | NSString *diffPath = [self _failedFilePathForSelector:selector
194 | identifier:identifier
195 | fileNameType:FBTestSnapshotFileNameTypeFailedTestDiff];
196 |
197 | UIImage *diffImage = [referenceImage fb_diffWithImage:testImage];
198 | NSData *diffImageData = UIImagePNGRepresentation(diffImage);
199 |
200 | if (![diffImageData writeToFile:diffPath options:NSDataWritingAtomic error:errorPtr]) {
201 | return NO;
202 | }
203 |
204 | NSLog(@"If you have Kaleidoscope installed you can run this command to see an image diff:\n"
205 | @"ksdiff \"%@\" \"%@\"", referencePath, testPath);
206 |
207 | return YES;
208 | }
209 |
210 | #pragma mark - Private API
211 |
212 | - (NSString *)_fileNameForSelector:(SEL)selector
213 | identifier:(NSString *)identifier
214 | fileNameType:(FBTestSnapshotFileNameType)fileNameType
215 | {
216 | NSString *fileName = nil;
217 | switch (fileNameType) {
218 | case FBTestSnapshotFileNameTypeFailedReference:
219 | fileName = @"reference_";
220 | break;
221 | case FBTestSnapshotFileNameTypeFailedTest:
222 | fileName = @"failed_";
223 | break;
224 | case FBTestSnapshotFileNameTypeFailedTestDiff:
225 | fileName = @"diff_";
226 | break;
227 | default:
228 | fileName = @"";
229 | break;
230 | }
231 | fileName = [fileName stringByAppendingString:NSStringFromSelector(selector)];
232 | if (0 < identifier.length) {
233 | fileName = [fileName stringByAppendingFormat:@"_%@", identifier];
234 | }
235 |
236 | if (self.isDeviceAgnostic) {
237 | fileName = FBDeviceAgnosticNormalizedFileName(fileName);
238 | }
239 |
240 | if ([[UIScreen mainScreen] scale] > 1) {
241 | fileName = [fileName stringByAppendingFormat:@"@%.fx", [[UIScreen mainScreen] scale]];
242 | }
243 | fileName = [fileName stringByAppendingPathExtension:@"png"];
244 | return fileName;
245 | }
246 |
247 | - (NSString *)_referenceFilePathForSelector:(SEL)selector
248 | identifier:(NSString *)identifier
249 | {
250 | NSString *fileName = [self _fileNameForSelector:selector
251 | identifier:identifier
252 | fileNameType:FBTestSnapshotFileNameTypeReference];
253 | NSString *filePath = [_referenceImagesDirectory stringByAppendingPathComponent:_testName];
254 | filePath = [filePath stringByAppendingPathComponent:fileName];
255 | return filePath;
256 | }
257 |
258 | - (NSString *)_failedFilePathForSelector:(SEL)selector
259 | identifier:(NSString *)identifier
260 | fileNameType:(FBTestSnapshotFileNameType)fileNameType
261 | {
262 | NSString *fileName = [self _fileNameForSelector:selector
263 | identifier:identifier
264 | fileNameType:fileNameType];
265 | NSString *folderPath = NSTemporaryDirectory();
266 | if (getenv("IMAGE_DIFF_DIR")) {
267 | folderPath = @(getenv("IMAGE_DIFF_DIR"));
268 | }
269 | NSString *filePath = [folderPath stringByAppendingPathComponent:_testName];
270 | filePath = [filePath stringByAppendingPathComponent:fileName];
271 | return filePath;
272 | }
273 |
274 | - (BOOL)_performPixelComparisonWithViewOrLayer:(id)viewOrLayer
275 | selector:(SEL)selector
276 | identifier:(NSString *)identifier
277 | tolerance:(CGFloat)tolerance
278 | error:(NSError **)errorPtr
279 | {
280 | UIImage *referenceImage = [self referenceImageForSelector:selector identifier:identifier error:errorPtr];
281 | if (nil != referenceImage) {
282 | UIImage *snapshot = [self _imageForViewOrLayer:viewOrLayer];
283 | BOOL imagesSame = [self compareReferenceImage:referenceImage toImage:snapshot tolerance:tolerance error:errorPtr];
284 | if (!imagesSame) {
285 | NSError *saveError = nil;
286 | if ([self saveFailedReferenceImage:referenceImage testImage:snapshot selector:selector identifier:identifier error:&saveError] == NO) {
287 | NSLog(@"Error saving test images: %@", saveError);
288 | }
289 | }
290 | return imagesSame;
291 | }
292 | return NO;
293 | }
294 |
295 | - (BOOL)_recordSnapshotOfViewOrLayer:(id)viewOrLayer
296 | selector:(SEL)selector
297 | identifier:(NSString *)identifier
298 | error:(NSError **)errorPtr
299 | {
300 | UIImage *snapshot = [self _imageForViewOrLayer:viewOrLayer];
301 | return [self _saveReferenceImage:snapshot selector:selector identifier:identifier error:errorPtr];
302 | }
303 |
304 | - (BOOL)_saveReferenceImage:(UIImage *)image
305 | selector:(SEL)selector
306 | identifier:(NSString *)identifier
307 | error:(NSError **)errorPtr
308 | {
309 | BOOL didWrite = NO;
310 | if (nil != image) {
311 | NSString *filePath = [self _referenceFilePathForSelector:selector identifier:identifier];
312 | NSData *pngData = UIImagePNGRepresentation(image);
313 | if (nil != pngData) {
314 | NSError *creationError = nil;
315 | BOOL didCreateDir = [_fileManager createDirectoryAtPath:[filePath stringByDeletingLastPathComponent]
316 | withIntermediateDirectories:YES
317 | attributes:nil
318 | error:&creationError];
319 | if (!didCreateDir) {
320 | if (NULL != errorPtr) {
321 | *errorPtr = creationError;
322 | }
323 | return NO;
324 | }
325 | didWrite = [pngData writeToFile:filePath options:NSDataWritingAtomic error:errorPtr];
326 | if (didWrite) {
327 | NSLog(@"Reference image save at: %@", filePath);
328 | }
329 | } else {
330 | if (nil != errorPtr) {
331 | *errorPtr = [NSError errorWithDomain:FBSnapshotTestControllerErrorDomain
332 | code:FBSnapshotTestControllerErrorCodePNGCreationFailed
333 | userInfo:@{
334 | FBReferenceImageFilePathKey: filePath,
335 | }];
336 | }
337 | }
338 | }
339 | return didWrite;
340 | }
341 |
342 | - (UIImage *)_imageForViewOrLayer:(id)viewOrLayer
343 | {
344 | if ([viewOrLayer isKindOfClass:[UIView class]]) {
345 | if (_usesDrawViewHierarchyInRect) {
346 | return [UIImage fb_imageForView:viewOrLayer];
347 | } else {
348 | return [UIImage fb_imageForViewLayer:viewOrLayer];
349 | }
350 | } else if ([viewOrLayer isKindOfClass:[CALayer class]]) {
351 | return [UIImage fb_imageForLayer:viewOrLayer];
352 | } else {
353 | [NSException raise:@"Only UIView and CALayer classes can be snapshotted" format:@"%@", viewOrLayer];
354 | }
355 | return nil;
356 | }
357 |
358 | @end
359 |
--------------------------------------------------------------------------------
/Example/Pods/FBSnapshotTestCase/FBSnapshotTestCase/SwiftSupport.swift:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2015, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | *
9 | */
10 |
11 | #if swift(>=3)
12 | public extension FBSnapshotTestCase {
13 | public func FBSnapshotVerifyView(_ view: UIView, identifier: String = "", suffixes: NSOrderedSet = FBSnapshotTestCaseDefaultSuffixes(), tolerance: CGFloat = 0, file: StaticString = #file, line: UInt = #line) {
14 | FBSnapshotVerifyViewOrLayer(view, identifier: identifier, suffixes: suffixes, tolerance: tolerance, file: file, line: line)
15 | }
16 |
17 | public func FBSnapshotVerifyLayer(_ layer: CALayer, identifier: String = "", suffixes: NSOrderedSet = FBSnapshotTestCaseDefaultSuffixes(), tolerance: CGFloat = 0, file: StaticString = #file, line: UInt = #line) {
18 | FBSnapshotVerifyViewOrLayer(layer, identifier: identifier, suffixes: suffixes, tolerance: tolerance, file: file, line: line)
19 | }
20 |
21 | private func FBSnapshotVerifyViewOrLayer(_ viewOrLayer: AnyObject, identifier: String = "", suffixes: NSOrderedSet = FBSnapshotTestCaseDefaultSuffixes(), tolerance: CGFloat = 0, file: StaticString = #file, line: UInt = #line) {
22 | let envReferenceImageDirectory = self.getReferenceImageDirectory(withDefault: FB_REFERENCE_IMAGE_DIR)
23 | var error: NSError?
24 | var comparisonSuccess = false
25 |
26 | if let envReferenceImageDirectory = envReferenceImageDirectory {
27 | for suffix in suffixes {
28 | let referenceImagesDirectory = "\(envReferenceImageDirectory)\(suffix)"
29 | if viewOrLayer.isKind(of: UIView.self) {
30 | do {
31 | try compareSnapshot(of: viewOrLayer as! UIView, referenceImagesDirectory: referenceImagesDirectory, identifier: identifier, tolerance: tolerance)
32 | comparisonSuccess = true
33 | } catch let error1 as NSError {
34 | error = error1
35 | comparisonSuccess = false
36 | }
37 | } else if viewOrLayer.isKind(of: CALayer.self) {
38 | do {
39 | try compareSnapshot(of: viewOrLayer as! CALayer, referenceImagesDirectory: referenceImagesDirectory, identifier: identifier, tolerance: tolerance)
40 | comparisonSuccess = true
41 | } catch let error1 as NSError {
42 | error = error1
43 | comparisonSuccess = false
44 | }
45 | } else {
46 | assertionFailure("Only UIView and CALayer classes can be snapshotted")
47 | }
48 |
49 | assert(recordMode == false, message: "Test ran in record mode. Reference image is now saved. Disable record mode to perform an actual snapshot comparison!", file: file, line: line)
50 |
51 | if comparisonSuccess || recordMode {
52 | break
53 | }
54 |
55 | assert(comparisonSuccess, message: "Snapshot comparison failed: \(error)", file: file, line: line)
56 | }
57 | } else {
58 | XCTFail("Missing value for referenceImagesDirectory - Set FB_REFERENCE_IMAGE_DIR as Environment variable in your scheme.")
59 | }
60 | }
61 |
62 | func assert(_ assertion: Bool, message: String, file: StaticString, line: UInt) {
63 | if !assertion {
64 | XCTFail(message, file: file, line: line)
65 | }
66 | }
67 | }
68 | #else
69 | public extension FBSnapshotTestCase {
70 | public func FBSnapshotVerifyView(view: UIView, identifier: String = "", suffixes: NSOrderedSet = FBSnapshotTestCaseDefaultSuffixes(), tolerance: CGFloat = 0, file: StaticString = #file, line: UInt = #line) {
71 | FBSnapshotVerifyViewOrLayer(view, identifier: identifier, suffixes: suffixes, tolerance: tolerance, file: file, line: line)
72 | }
73 |
74 | public func FBSnapshotVerifyLayer(layer: CALayer, identifier: String = "", suffixes: NSOrderedSet = FBSnapshotTestCaseDefaultSuffixes(), tolerance: CGFloat = 0, file: StaticString = #file, line: UInt = #line) {
75 | FBSnapshotVerifyViewOrLayer(layer, identifier: identifier, suffixes: suffixes, tolerance: tolerance, file: file, line: line)
76 | }
77 |
78 | private func FBSnapshotVerifyViewOrLayer(viewOrLayer: AnyObject, identifier: String = "", suffixes: NSOrderedSet = FBSnapshotTestCaseDefaultSuffixes(), tolerance: CGFloat = 0, file: StaticString = #file, line: UInt = #line) {
79 | let envReferenceImageDirectory = self.getReferenceImageDirectoryWithDefault(FB_REFERENCE_IMAGE_DIR)
80 | var error: NSError?
81 | var comparisonSuccess = false
82 |
83 | if let envReferenceImageDirectory = envReferenceImageDirectory {
84 | for suffix in suffixes {
85 | let referenceImagesDirectory = "\(envReferenceImageDirectory)\(suffix)"
86 | if viewOrLayer.isKindOfClass(UIView) {
87 | do {
88 | try compareSnapshotOfView(viewOrLayer as! UIView, referenceImagesDirectory: referenceImagesDirectory, identifier: identifier, tolerance: tolerance)
89 | comparisonSuccess = true
90 | } catch let error1 as NSError {
91 | error = error1
92 | comparisonSuccess = false
93 | }
94 | } else if viewOrLayer.isKindOfClass(CALayer) {
95 | do {
96 | try compareSnapshotOfLayer(viewOrLayer as! CALayer, referenceImagesDirectory: referenceImagesDirectory, identifier: identifier, tolerance: tolerance)
97 | comparisonSuccess = true
98 | } catch let error1 as NSError {
99 | error = error1
100 | comparisonSuccess = false
101 | }
102 | } else {
103 | assertionFailure("Only UIView and CALayer classes can be snapshotted")
104 | }
105 |
106 | assert(recordMode == false, message: "Test ran in record mode. Reference image is now saved. Disable record mode to perform an actual snapshot comparison!", file: file, line: line)
107 |
108 | if comparisonSuccess || recordMode {
109 | break
110 | }
111 |
112 | assert(comparisonSuccess, message: "Snapshot comparison failed: \(error)", file: file, line: line)
113 | }
114 | } else {
115 | XCTFail("Missing value for referenceImagesDirectory - Set FB_REFERENCE_IMAGE_DIR as Environment variable in your scheme.")
116 | }
117 | }
118 |
119 | func assert(assertion: Bool, message: String, file: StaticString, line: UInt) {
120 | if !assertion {
121 | XCTFail(message, file: file, line: line)
122 | }
123 | }
124 | }
125 | #endif
126 |
--------------------------------------------------------------------------------
/Example/Pods/FBSnapshotTestCase/LICENSE:
--------------------------------------------------------------------------------
1 | BSD License
2 |
3 | For the FBSnapshotTestCase software
4 |
5 | Copyright (c) 2013, Facebook, Inc.
6 | All rights reserved.
7 |
8 | Redistribution and use in source and binary forms, with or without
9 | modification, are permitted provided that the following conditions are met:
10 |
11 | * Redistributions of source code must retain the above copyright notice,
12 | this list of conditions and the following disclaimer.
13 | * Redistributions in binary form must reproduce the above copyright notice,
14 | this list of conditions and the following disclaimer in the documentation
15 | and/or other materials provided with the distribution.
16 | * Neither the name Facebook nor the names of its contributors may be used to
17 | endorse or promote products derived from this software without specific
18 | prior written permission.
19 |
20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 |
--------------------------------------------------------------------------------
/Example/Pods/FBSnapshotTestCase/README.md:
--------------------------------------------------------------------------------
1 | FBSnapshotTestCase
2 | ======================
3 |
4 | [](https://travis-ci.org/facebook/ios-snapshot-test-case) [](http://cocoadocs.org/docsets/FBSnapshotTestCase/)
5 |
6 | What it does
7 | ------------
8 |
9 | A "snapshot test case" takes a configured `UIView` or `CALayer` and uses the
10 | `renderInContext:` method to get an image snapshot of its contents. It
11 | compares this snapshot to a "reference image" stored in your source code
12 | repository and fails the test if the two images don't match.
13 |
14 | Why?
15 | ----
16 |
17 | At Facebook we write a lot of UI code. As you might imagine, each type of
18 | feed story is rendered using a subclass of `UIView`. There are a lot of edge
19 | cases that we want to handle correctly:
20 |
21 | - What if there is more text than can fit in the space available?
22 | - What if an image doesn't match the size of an image view?
23 | - What should the highlighted state look like?
24 |
25 | It's straightforward to test logic code, but less obvious how you should test
26 | views. You can do a lot of rectangle asserts, but these are hard to understand
27 | or visualize. Looking at an image diff shows you exactly what changed and how
28 | it will look to users.
29 |
30 | We developed `FBSnapshotTestCase` to make snapshot tests easy.
31 |
32 | Installation with CocoaPods
33 | ---------------------------
34 |
35 | 1. Add the following lines to your Podfile:
36 |
37 | ```
38 | target "Tests" do
39 | pod 'FBSnapshotTestCase'
40 | end
41 | ```
42 |
43 | If you support iOS 7 use `FBSnapshotTestCase/Core` instead, which doesn't contain Swift support.
44 |
45 | Replace "Tests" with the name of your test project.
46 |
47 | 2. There are [three ways](https://github.com/facebook/ios-snapshot-test-case/blob/master/FBSnapshotTestCase/FBSnapshotTestCase.h#L19-L29) of setting reference image directories, the recommended one is to define `FB_REFERENCE_IMAGE_DIR` in your scheme. This should point to the directory where you want reference images to be stored. At Facebook, we normally use this:
48 |
49 | |Name|Value|
50 | |:---|:----|
51 | |`FB_REFERENCE_IMAGE_DIR`|`$(SOURCE_ROOT)/$(PROJECT_NAME)Tests/ReferenceImages`|
52 |
53 |
54 | 
55 |
56 | Creating a snapshot test
57 | ------------------------
58 |
59 | 1. Subclass `FBSnapshotTestCase` instead of `XCTestCase`.
60 | 2. From within your test, use `FBSnapshotVerifyView`.
61 | 3. Run the test once with `self.recordMode = YES;` in the test's `-setUp`
62 | method. (This creates the reference images on disk.)
63 | 4. Remove the line enabling record mode and run the test.
64 |
65 | Features
66 | --------
67 |
68 | - Automatically names reference images on disk according to test class and
69 | selector.
70 | - Prints a descriptive error message to the console on failure. (Bonus:
71 | failure message includes a one-line command to see an image diff if
72 | you have [Kaleidoscope](http://www.kaleidoscopeapp.com) installed.)
73 | - Supply an optional "identifier" if you want to perform multiple snapshots
74 | in a single test method.
75 | - Support for `CALayer` via `FBSnapshotVerifyLayer`.
76 | - `usesDrawViewHierarchyInRect` to handle cases like `UIVisualEffect`, `UIAppearance` and Size Classes.
77 | - `isDeviceAgnostic` to allow appending the device model (`iPhone`, `iPad`, `iPod Touch`, etc), OS version and screen size to the images (allowing to have multiple tests for the same «snapshot» for different `OS`s and devices).
78 |
79 | Notes
80 | -----
81 |
82 | Your unit test must be an "application test", not a "logic test." (That is, it
83 | must be run within the Simulator so that it has access to UIKit.) In Xcode 5
84 | and later new projects only offer application tests, but older projects will
85 | have separate targets for the two types.
86 |
87 | Authors
88 | -------
89 |
90 | `FBSnapshotTestCase` was written at Facebook by
91 | [Jonathan Dann](https://facebook.com/j.p.dann) with significant contributions by
92 | [Todd Krabach](https://facebook.com/toddkrabach).
93 |
94 | License
95 | -------
96 |
97 | `FBSnapshotTestCase` is BSD-licensed. See `LICENSE`.
98 |
--------------------------------------------------------------------------------
/Example/Pods/Local Podspecs/Kamishibai.podspec.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Kamishibai",
3 | "version": "0.1.0",
4 | "summary": "A short description of Kamishibai.",
5 | "description": "TODO: Add long description of the pod here.",
6 | "homepage": "https://github.com/ksk.matsuo@gmail.com/Kamishibai",
7 | "license": {
8 | "type": "MIT",
9 | "file": "LICENSE"
10 | },
11 | "authors": {
12 | "ksk.matsuo@gmail.com": "ksk.matsuo@gmail.com"
13 | },
14 | "source": {
15 | "git": "https://github.com/ksk.matsuo@gmail.com/Kamishibai.git",
16 | "tag": "0.1.0"
17 | },
18 | "platforms": {
19 | "ios": "8.0"
20 | },
21 | "source_files": "Kamishibai/Classes/**/*"
22 | }
23 |
--------------------------------------------------------------------------------
/Example/Pods/Manifest.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - FBSnapshotTestCase (2.1.4):
3 | - FBSnapshotTestCase/SwiftSupport (= 2.1.4)
4 | - FBSnapshotTestCase/Core (2.1.4)
5 | - FBSnapshotTestCase/SwiftSupport (2.1.4):
6 | - FBSnapshotTestCase/Core
7 | - Kamishibai (0.1.0)
8 |
9 | DEPENDENCIES:
10 | - FBSnapshotTestCase
11 | - Kamishibai (from `../`)
12 |
13 | EXTERNAL SOURCES:
14 | Kamishibai:
15 | :path: ../
16 |
17 | SPEC CHECKSUMS:
18 | FBSnapshotTestCase: 094f9f314decbabe373b87cc339bea235a63e07a
19 | Kamishibai: 1486b69cfe4f536efdf2afd97eeb4d1c2d68ef37
20 |
21 | PODFILE CHECKSUM: 64fe81f6ef8cda5dd3325c2c66e75dbfab546e37
22 |
23 | COCOAPODS: 1.3.1
24 |
--------------------------------------------------------------------------------
/Example/Pods/Pods.xcodeproj/xcshareddata/xcschemes/Kamishibai.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
31 |
32 |
33 |
34 |
35 |
36 |
47 |
48 |
54 |
55 |
56 |
57 |
58 |
59 |
65 |
66 |
68 |
69 |
72 |
73 |
74 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/FBSnapshotTestCase/FBSnapshotTestCase-dummy.m:
--------------------------------------------------------------------------------
1 | #import
2 | @interface PodsDummy_FBSnapshotTestCase : NSObject
3 | @end
4 | @implementation PodsDummy_FBSnapshotTestCase
5 | @end
6 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/FBSnapshotTestCase/FBSnapshotTestCase-prefix.pch:
--------------------------------------------------------------------------------
1 | #ifdef __OBJC__
2 | #import
3 | #else
4 | #ifndef FOUNDATION_EXPORT
5 | #if defined(__cplusplus)
6 | #define FOUNDATION_EXPORT extern "C"
7 | #else
8 | #define FOUNDATION_EXPORT extern
9 | #endif
10 | #endif
11 | #endif
12 |
13 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/FBSnapshotTestCase/FBSnapshotTestCase-umbrella.h:
--------------------------------------------------------------------------------
1 | #ifdef __OBJC__
2 | #import
3 | #else
4 | #ifndef FOUNDATION_EXPORT
5 | #if defined(__cplusplus)
6 | #define FOUNDATION_EXPORT extern "C"
7 | #else
8 | #define FOUNDATION_EXPORT extern
9 | #endif
10 | #endif
11 | #endif
12 |
13 | #import "FBSnapshotTestCase.h"
14 | #import "FBSnapshotTestCasePlatform.h"
15 | #import "FBSnapshotTestController.h"
16 |
17 | FOUNDATION_EXPORT double FBSnapshotTestCaseVersionNumber;
18 | FOUNDATION_EXPORT const unsigned char FBSnapshotTestCaseVersionString[];
19 |
20 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/FBSnapshotTestCase/FBSnapshotTestCase.modulemap:
--------------------------------------------------------------------------------
1 | framework module FBSnapshotTestCase {
2 | umbrella header "FBSnapshotTestCase-umbrella.h"
3 |
4 | export *
5 | module * { export * }
6 | }
7 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/FBSnapshotTestCase/FBSnapshotTestCase.xcconfig:
--------------------------------------------------------------------------------
1 | CONFIGURATION_BUILD_DIR = $PODS_CONFIGURATION_BUILD_DIR/FBSnapshotTestCase
2 | ENABLE_BITCODE = NO
3 | FRAMEWORK_SEARCH_PATHS = $(inherited) "$(PLATFORM_DIR)/Developer/Library/Frameworks"
4 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
5 | HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Public"
6 | OTHER_LDFLAGS = -framework "Foundation" -framework "QuartzCore" -framework "UIKit" -framework "XCTest"
7 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS"
8 | PODS_BUILD_DIR = $BUILD_DIR
9 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
10 | PODS_ROOT = ${SRCROOT}
11 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/FBSnapshotTestCase
12 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
13 | SKIP_INSTALL = YES
14 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/FBSnapshotTestCase/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 | FMWK
17 | CFBundleShortVersionString
18 | 2.1.4
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | ${CURRENT_PROJECT_VERSION}
23 | NSPrincipalClass
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Kamishibai/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 | FMWK
17 | CFBundleShortVersionString
18 | 0.1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | ${CURRENT_PROJECT_VERSION}
23 | NSPrincipalClass
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Kamishibai/Kamishibai-dummy.m:
--------------------------------------------------------------------------------
1 | #import
2 | @interface PodsDummy_Kamishibai : NSObject
3 | @end
4 | @implementation PodsDummy_Kamishibai
5 | @end
6 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Kamishibai/Kamishibai-prefix.pch:
--------------------------------------------------------------------------------
1 | #ifdef __OBJC__
2 | #import
3 | #else
4 | #ifndef FOUNDATION_EXPORT
5 | #if defined(__cplusplus)
6 | #define FOUNDATION_EXPORT extern "C"
7 | #else
8 | #define FOUNDATION_EXPORT extern
9 | #endif
10 | #endif
11 | #endif
12 |
13 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Kamishibai/Kamishibai-umbrella.h:
--------------------------------------------------------------------------------
1 | #ifdef __OBJC__
2 | #import
3 | #else
4 | #ifndef FOUNDATION_EXPORT
5 | #if defined(__cplusplus)
6 | #define FOUNDATION_EXPORT extern "C"
7 | #else
8 | #define FOUNDATION_EXPORT extern
9 | #endif
10 | #endif
11 | #endif
12 |
13 |
14 | FOUNDATION_EXPORT double KamishibaiVersionNumber;
15 | FOUNDATION_EXPORT const unsigned char KamishibaiVersionString[];
16 |
17 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Kamishibai/Kamishibai.modulemap:
--------------------------------------------------------------------------------
1 | framework module Kamishibai {
2 | umbrella header "Kamishibai-umbrella.h"
3 |
4 | export *
5 | module * { export * }
6 | }
7 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Kamishibai/Kamishibai.xcconfig:
--------------------------------------------------------------------------------
1 | CONFIGURATION_BUILD_DIR = $PODS_CONFIGURATION_BUILD_DIR/Kamishibai
2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
3 | HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Public"
4 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS"
5 | PODS_BUILD_DIR = $BUILD_DIR
6 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
7 | PODS_ROOT = ${SRCROOT}
8 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/../..
9 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
10 | SKIP_INSTALL = YES
11 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-Kamishibai_Example/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 | FMWK
17 | CFBundleShortVersionString
18 | 1.0.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | ${CURRENT_PROJECT_VERSION}
23 | NSPrincipalClass
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-Kamishibai_Example/Pods-Kamishibai_Example-acknowledgements.markdown:
--------------------------------------------------------------------------------
1 | # Acknowledgements
2 | This application makes use of the following third party libraries:
3 |
4 | ## Kamishibai
5 |
6 | Copyright (c) 2017 ksk.matsuo@gmail.com
7 |
8 | Permission is hereby granted, free of charge, to any person obtaining a copy
9 | of this software and associated documentation files (the "Software"), to deal
10 | in the Software without restriction, including without limitation the rights
11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 | copies of the Software, and to permit persons to whom the Software is
13 | furnished to do so, subject to the following conditions:
14 |
15 | The above copyright notice and this permission notice shall be included in
16 | all copies or substantial portions of the Software.
17 |
18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 | THE SOFTWARE.
25 |
26 | Generated by CocoaPods - https://cocoapods.org
27 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-Kamishibai_Example/Pods-Kamishibai_Example-acknowledgements.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreferenceSpecifiers
6 |
7 |
8 | FooterText
9 | This application makes use of the following third party libraries:
10 | Title
11 | Acknowledgements
12 | Type
13 | PSGroupSpecifier
14 |
15 |
16 | FooterText
17 | Copyright (c) 2017 ksk.matsuo@gmail.com <ksk.matsuo@gmail.com>
18 |
19 | Permission is hereby granted, free of charge, to any person obtaining a copy
20 | of this software and associated documentation files (the "Software"), to deal
21 | in the Software without restriction, including without limitation the rights
22 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
23 | copies of the Software, and to permit persons to whom the Software is
24 | furnished to do so, subject to the following conditions:
25 |
26 | The above copyright notice and this permission notice shall be included in
27 | all copies or substantial portions of the Software.
28 |
29 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
30 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
31 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
32 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
33 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
34 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
35 | THE SOFTWARE.
36 |
37 | License
38 | MIT
39 | Title
40 | Kamishibai
41 | Type
42 | PSGroupSpecifier
43 |
44 |
45 | FooterText
46 | Generated by CocoaPods - https://cocoapods.org
47 | Title
48 |
49 | Type
50 | PSGroupSpecifier
51 |
52 |
53 | StringsTable
54 | Acknowledgements
55 | Title
56 | Acknowledgements
57 |
58 |
59 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-Kamishibai_Example/Pods-Kamishibai_Example-dummy.m:
--------------------------------------------------------------------------------
1 | #import
2 | @interface PodsDummy_Pods_Kamishibai_Example : NSObject
3 | @end
4 | @implementation PodsDummy_Pods_Kamishibai_Example
5 | @end
6 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-Kamishibai_Example/Pods-Kamishibai_Example-frameworks.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | set -e
3 |
4 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
5 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
6 |
7 | SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}"
8 |
9 | # This protects against multiple targets copying the same framework dependency at the same time. The solution
10 | # was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html
11 | RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????")
12 |
13 | install_framework()
14 | {
15 | if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then
16 | local source="${BUILT_PRODUCTS_DIR}/$1"
17 | elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then
18 | local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")"
19 | elif [ -r "$1" ]; then
20 | local source="$1"
21 | fi
22 |
23 | local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
24 |
25 | if [ -L "${source}" ]; then
26 | echo "Symlinked..."
27 | source="$(readlink "${source}")"
28 | fi
29 |
30 | # Use filter instead of exclude so missing patterns don't throw errors.
31 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\""
32 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}"
33 |
34 | local basename
35 | basename="$(basename -s .framework "$1")"
36 | binary="${destination}/${basename}.framework/${basename}"
37 | if ! [ -r "$binary" ]; then
38 | binary="${destination}/${basename}"
39 | fi
40 |
41 | # Strip invalid architectures so "fat" simulator / device frameworks work on device
42 | if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then
43 | strip_invalid_archs "$binary"
44 | fi
45 |
46 | # Resign the code if required by the build settings to avoid unstable apps
47 | code_sign_if_enabled "${destination}/$(basename "$1")"
48 |
49 | # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7.
50 | if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then
51 | local swift_runtime_libs
52 | swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u && exit ${PIPESTATUS[0]})
53 | for lib in $swift_runtime_libs; do
54 | echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\""
55 | rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}"
56 | code_sign_if_enabled "${destination}/${lib}"
57 | done
58 | fi
59 | }
60 |
61 | # Copies the dSYM of a vendored framework
62 | install_dsym() {
63 | local source="$1"
64 | if [ -r "$source" ]; then
65 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${DWARF_DSYM_FOLDER_PATH}\""
66 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${DWARF_DSYM_FOLDER_PATH}"
67 | fi
68 | }
69 |
70 | # Signs a framework with the provided identity
71 | code_sign_if_enabled() {
72 | if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then
73 | # Use the current code_sign_identitiy
74 | echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}"
75 | local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS} --preserve-metadata=identifier,entitlements '$1'"
76 |
77 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then
78 | code_sign_cmd="$code_sign_cmd &"
79 | fi
80 | echo "$code_sign_cmd"
81 | eval "$code_sign_cmd"
82 | fi
83 | }
84 |
85 | # Strip invalid architectures
86 | strip_invalid_archs() {
87 | binary="$1"
88 | # Get architectures for current file
89 | archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | rev)"
90 | stripped=""
91 | for arch in $archs; do
92 | if ! [[ "${ARCHS}" == *"$arch"* ]]; then
93 | # Strip non-valid architectures in-place
94 | lipo -remove "$arch" -output "$binary" "$binary" || exit 1
95 | stripped="$stripped $arch"
96 | fi
97 | done
98 | if [[ "$stripped" ]]; then
99 | echo "Stripped $binary of architectures:$stripped"
100 | fi
101 | }
102 |
103 |
104 | if [[ "$CONFIGURATION" == "Debug" ]]; then
105 | install_framework "${BUILT_PRODUCTS_DIR}/Kamishibai/Kamishibai.framework"
106 | fi
107 | if [[ "$CONFIGURATION" == "Release" ]]; then
108 | install_framework "${BUILT_PRODUCTS_DIR}/Kamishibai/Kamishibai.framework"
109 | fi
110 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then
111 | wait
112 | fi
113 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-Kamishibai_Example/Pods-Kamishibai_Example-resources.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | set -e
3 |
4 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
5 |
6 | RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt
7 | > "$RESOURCES_TO_COPY"
8 |
9 | XCASSET_FILES=()
10 |
11 | # This protects against multiple targets copying the same framework dependency at the same time. The solution
12 | # was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html
13 | RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????")
14 |
15 | case "${TARGETED_DEVICE_FAMILY}" in
16 | 1,2)
17 | TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone"
18 | ;;
19 | 1)
20 | TARGET_DEVICE_ARGS="--target-device iphone"
21 | ;;
22 | 2)
23 | TARGET_DEVICE_ARGS="--target-device ipad"
24 | ;;
25 | 3)
26 | TARGET_DEVICE_ARGS="--target-device tv"
27 | ;;
28 | 4)
29 | TARGET_DEVICE_ARGS="--target-device watch"
30 | ;;
31 | *)
32 | TARGET_DEVICE_ARGS="--target-device mac"
33 | ;;
34 | esac
35 |
36 | install_resource()
37 | {
38 | if [[ "$1" = /* ]] ; then
39 | RESOURCE_PATH="$1"
40 | else
41 | RESOURCE_PATH="${PODS_ROOT}/$1"
42 | fi
43 | if [[ ! -e "$RESOURCE_PATH" ]] ; then
44 | cat << EOM
45 | error: Resource "$RESOURCE_PATH" not found. Run 'pod install' to update the copy resources script.
46 | EOM
47 | exit 1
48 | fi
49 | case $RESOURCE_PATH in
50 | *.storyboard)
51 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true
52 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS}
53 | ;;
54 | *.xib)
55 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true
56 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS}
57 | ;;
58 | *.framework)
59 | echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true
60 | mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
61 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true
62 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
63 | ;;
64 | *.xcdatamodel)
65 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\"" || true
66 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom"
67 | ;;
68 | *.xcdatamodeld)
69 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\"" || true
70 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd"
71 | ;;
72 | *.xcmappingmodel)
73 | echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\"" || true
74 | xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm"
75 | ;;
76 | *.xcassets)
77 | ABSOLUTE_XCASSET_FILE="$RESOURCE_PATH"
78 | XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE")
79 | ;;
80 | *)
81 | echo "$RESOURCE_PATH" || true
82 | echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY"
83 | ;;
84 | esac
85 | }
86 |
87 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
88 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
89 | if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then
90 | mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
91 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
92 | fi
93 | rm -f "$RESOURCES_TO_COPY"
94 |
95 | if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "$XCASSET_FILES" ]
96 | then
97 | # Find all other xcassets (this unfortunately includes those of path pods and other targets).
98 | OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d)
99 | while read line; do
100 | if [[ $line != "${PODS_ROOT}*" ]]; then
101 | XCASSET_FILES+=("$line")
102 | fi
103 | done <<<"$OTHER_XCASSETS"
104 |
105 | printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
106 | fi
107 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-Kamishibai_Example/Pods-Kamishibai_Example-umbrella.h:
--------------------------------------------------------------------------------
1 | #ifdef __OBJC__
2 | #import
3 | #else
4 | #ifndef FOUNDATION_EXPORT
5 | #if defined(__cplusplus)
6 | #define FOUNDATION_EXPORT extern "C"
7 | #else
8 | #define FOUNDATION_EXPORT extern
9 | #endif
10 | #endif
11 | #endif
12 |
13 |
14 | FOUNDATION_EXPORT double Pods_Kamishibai_ExampleVersionNumber;
15 | FOUNDATION_EXPORT const unsigned char Pods_Kamishibai_ExampleVersionString[];
16 |
17 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-Kamishibai_Example/Pods-Kamishibai_Example.debug.xcconfig:
--------------------------------------------------------------------------------
1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES
2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/Kamishibai"
3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
4 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks'
5 | OTHER_CFLAGS = $(inherited) -iquote "$PODS_CONFIGURATION_BUILD_DIR/Kamishibai/Kamishibai.framework/Headers"
6 | OTHER_LDFLAGS = $(inherited) -framework "Kamishibai"
7 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS"
8 | PODS_BUILD_DIR = $BUILD_DIR
9 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
10 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/.
11 | PODS_ROOT = ${SRCROOT}/Pods
12 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-Kamishibai_Example/Pods-Kamishibai_Example.modulemap:
--------------------------------------------------------------------------------
1 | framework module Pods_Kamishibai_Example {
2 | umbrella header "Pods-Kamishibai_Example-umbrella.h"
3 |
4 | export *
5 | module * { export * }
6 | }
7 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-Kamishibai_Example/Pods-Kamishibai_Example.release.xcconfig:
--------------------------------------------------------------------------------
1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES
2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/Kamishibai"
3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
4 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks'
5 | OTHER_CFLAGS = $(inherited) -iquote "$PODS_CONFIGURATION_BUILD_DIR/Kamishibai/Kamishibai.framework/Headers"
6 | OTHER_LDFLAGS = $(inherited) -framework "Kamishibai"
7 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS"
8 | PODS_BUILD_DIR = $BUILD_DIR
9 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
10 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/.
11 | PODS_ROOT = ${SRCROOT}/Pods
12 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-Kamishibai_Tests/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 | FMWK
17 | CFBundleShortVersionString
18 | 1.0.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | ${CURRENT_PROJECT_VERSION}
23 | NSPrincipalClass
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-Kamishibai_Tests/Pods-Kamishibai_Tests-acknowledgements.markdown:
--------------------------------------------------------------------------------
1 | # Acknowledgements
2 | This application makes use of the following third party libraries:
3 |
4 | ## FBSnapshotTestCase
5 |
6 | BSD License
7 |
8 | For the FBSnapshotTestCase software
9 |
10 | Copyright (c) 2013, Facebook, Inc.
11 | All rights reserved.
12 |
13 | Redistribution and use in source and binary forms, with or without
14 | modification, are permitted provided that the following conditions are met:
15 |
16 | * Redistributions of source code must retain the above copyright notice,
17 | this list of conditions and the following disclaimer.
18 | * Redistributions in binary form must reproduce the above copyright notice,
19 | this list of conditions and the following disclaimer in the documentation
20 | and/or other materials provided with the distribution.
21 | * Neither the name Facebook nor the names of its contributors may be used to
22 | endorse or promote products derived from this software without specific
23 | prior written permission.
24 |
25 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
26 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
28 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
29 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
31 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
32 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
33 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
34 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35 |
36 | Generated by CocoaPods - https://cocoapods.org
37 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-Kamishibai_Tests/Pods-Kamishibai_Tests-acknowledgements.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreferenceSpecifiers
6 |
7 |
8 | FooterText
9 | This application makes use of the following third party libraries:
10 | Title
11 | Acknowledgements
12 | Type
13 | PSGroupSpecifier
14 |
15 |
16 | FooterText
17 | BSD License
18 |
19 | For the FBSnapshotTestCase software
20 |
21 | Copyright (c) 2013, Facebook, Inc.
22 | All rights reserved.
23 |
24 | Redistribution and use in source and binary forms, with or without
25 | modification, are permitted provided that the following conditions are met:
26 |
27 | * Redistributions of source code must retain the above copyright notice,
28 | this list of conditions and the following disclaimer.
29 | * Redistributions in binary form must reproduce the above copyright notice,
30 | this list of conditions and the following disclaimer in the documentation
31 | and/or other materials provided with the distribution.
32 | * Neither the name Facebook nor the names of its contributors may be used to
33 | endorse or promote products derived from this software without specific
34 | prior written permission.
35 |
36 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
37 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
38 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
39 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
40 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
41 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
42 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
43 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
44 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
45 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
46 |
47 | License
48 | BSD
49 | Title
50 | FBSnapshotTestCase
51 | Type
52 | PSGroupSpecifier
53 |
54 |
55 | FooterText
56 | Generated by CocoaPods - https://cocoapods.org
57 | Title
58 |
59 | Type
60 | PSGroupSpecifier
61 |
62 |
63 | StringsTable
64 | Acknowledgements
65 | Title
66 | Acknowledgements
67 |
68 |
69 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-Kamishibai_Tests/Pods-Kamishibai_Tests-dummy.m:
--------------------------------------------------------------------------------
1 | #import
2 | @interface PodsDummy_Pods_Kamishibai_Tests : NSObject
3 | @end
4 | @implementation PodsDummy_Pods_Kamishibai_Tests
5 | @end
6 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-Kamishibai_Tests/Pods-Kamishibai_Tests-frameworks.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | set -e
3 |
4 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
5 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
6 |
7 | SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}"
8 |
9 | # This protects against multiple targets copying the same framework dependency at the same time. The solution
10 | # was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html
11 | RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????")
12 |
13 | install_framework()
14 | {
15 | if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then
16 | local source="${BUILT_PRODUCTS_DIR}/$1"
17 | elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then
18 | local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")"
19 | elif [ -r "$1" ]; then
20 | local source="$1"
21 | fi
22 |
23 | local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
24 |
25 | if [ -L "${source}" ]; then
26 | echo "Symlinked..."
27 | source="$(readlink "${source}")"
28 | fi
29 |
30 | # Use filter instead of exclude so missing patterns don't throw errors.
31 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\""
32 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}"
33 |
34 | local basename
35 | basename="$(basename -s .framework "$1")"
36 | binary="${destination}/${basename}.framework/${basename}"
37 | if ! [ -r "$binary" ]; then
38 | binary="${destination}/${basename}"
39 | fi
40 |
41 | # Strip invalid architectures so "fat" simulator / device frameworks work on device
42 | if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then
43 | strip_invalid_archs "$binary"
44 | fi
45 |
46 | # Resign the code if required by the build settings to avoid unstable apps
47 | code_sign_if_enabled "${destination}/$(basename "$1")"
48 |
49 | # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7.
50 | if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then
51 | local swift_runtime_libs
52 | swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u && exit ${PIPESTATUS[0]})
53 | for lib in $swift_runtime_libs; do
54 | echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\""
55 | rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}"
56 | code_sign_if_enabled "${destination}/${lib}"
57 | done
58 | fi
59 | }
60 |
61 | # Copies the dSYM of a vendored framework
62 | install_dsym() {
63 | local source="$1"
64 | if [ -r "$source" ]; then
65 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${DWARF_DSYM_FOLDER_PATH}\""
66 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${DWARF_DSYM_FOLDER_PATH}"
67 | fi
68 | }
69 |
70 | # Signs a framework with the provided identity
71 | code_sign_if_enabled() {
72 | if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then
73 | # Use the current code_sign_identitiy
74 | echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}"
75 | local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS} --preserve-metadata=identifier,entitlements '$1'"
76 |
77 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then
78 | code_sign_cmd="$code_sign_cmd &"
79 | fi
80 | echo "$code_sign_cmd"
81 | eval "$code_sign_cmd"
82 | fi
83 | }
84 |
85 | # Strip invalid architectures
86 | strip_invalid_archs() {
87 | binary="$1"
88 | # Get architectures for current file
89 | archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | rev)"
90 | stripped=""
91 | for arch in $archs; do
92 | if ! [[ "${ARCHS}" == *"$arch"* ]]; then
93 | # Strip non-valid architectures in-place
94 | lipo -remove "$arch" -output "$binary" "$binary" || exit 1
95 | stripped="$stripped $arch"
96 | fi
97 | done
98 | if [[ "$stripped" ]]; then
99 | echo "Stripped $binary of architectures:$stripped"
100 | fi
101 | }
102 |
103 |
104 | if [[ "$CONFIGURATION" == "Debug" ]]; then
105 | install_framework "${BUILT_PRODUCTS_DIR}/FBSnapshotTestCase/FBSnapshotTestCase.framework"
106 | fi
107 | if [[ "$CONFIGURATION" == "Release" ]]; then
108 | install_framework "${BUILT_PRODUCTS_DIR}/FBSnapshotTestCase/FBSnapshotTestCase.framework"
109 | fi
110 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then
111 | wait
112 | fi
113 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-Kamishibai_Tests/Pods-Kamishibai_Tests-resources.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | set -e
3 |
4 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
5 |
6 | RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt
7 | > "$RESOURCES_TO_COPY"
8 |
9 | XCASSET_FILES=()
10 |
11 | # This protects against multiple targets copying the same framework dependency at the same time. The solution
12 | # was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html
13 | RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????")
14 |
15 | case "${TARGETED_DEVICE_FAMILY}" in
16 | 1,2)
17 | TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone"
18 | ;;
19 | 1)
20 | TARGET_DEVICE_ARGS="--target-device iphone"
21 | ;;
22 | 2)
23 | TARGET_DEVICE_ARGS="--target-device ipad"
24 | ;;
25 | 3)
26 | TARGET_DEVICE_ARGS="--target-device tv"
27 | ;;
28 | 4)
29 | TARGET_DEVICE_ARGS="--target-device watch"
30 | ;;
31 | *)
32 | TARGET_DEVICE_ARGS="--target-device mac"
33 | ;;
34 | esac
35 |
36 | install_resource()
37 | {
38 | if [[ "$1" = /* ]] ; then
39 | RESOURCE_PATH="$1"
40 | else
41 | RESOURCE_PATH="${PODS_ROOT}/$1"
42 | fi
43 | if [[ ! -e "$RESOURCE_PATH" ]] ; then
44 | cat << EOM
45 | error: Resource "$RESOURCE_PATH" not found. Run 'pod install' to update the copy resources script.
46 | EOM
47 | exit 1
48 | fi
49 | case $RESOURCE_PATH in
50 | *.storyboard)
51 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true
52 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS}
53 | ;;
54 | *.xib)
55 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true
56 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS}
57 | ;;
58 | *.framework)
59 | echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true
60 | mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
61 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true
62 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
63 | ;;
64 | *.xcdatamodel)
65 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\"" || true
66 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom"
67 | ;;
68 | *.xcdatamodeld)
69 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\"" || true
70 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd"
71 | ;;
72 | *.xcmappingmodel)
73 | echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\"" || true
74 | xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm"
75 | ;;
76 | *.xcassets)
77 | ABSOLUTE_XCASSET_FILE="$RESOURCE_PATH"
78 | XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE")
79 | ;;
80 | *)
81 | echo "$RESOURCE_PATH" || true
82 | echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY"
83 | ;;
84 | esac
85 | }
86 |
87 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
88 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
89 | if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then
90 | mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
91 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
92 | fi
93 | rm -f "$RESOURCES_TO_COPY"
94 |
95 | if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "$XCASSET_FILES" ]
96 | then
97 | # Find all other xcassets (this unfortunately includes those of path pods and other targets).
98 | OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d)
99 | while read line; do
100 | if [[ $line != "${PODS_ROOT}*" ]]; then
101 | XCASSET_FILES+=("$line")
102 | fi
103 | done <<<"$OTHER_XCASSETS"
104 |
105 | printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
106 | fi
107 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-Kamishibai_Tests/Pods-Kamishibai_Tests-umbrella.h:
--------------------------------------------------------------------------------
1 | #ifdef __OBJC__
2 | #import
3 | #else
4 | #ifndef FOUNDATION_EXPORT
5 | #if defined(__cplusplus)
6 | #define FOUNDATION_EXPORT extern "C"
7 | #else
8 | #define FOUNDATION_EXPORT extern
9 | #endif
10 | #endif
11 | #endif
12 |
13 |
14 | FOUNDATION_EXPORT double Pods_Kamishibai_TestsVersionNumber;
15 | FOUNDATION_EXPORT const unsigned char Pods_Kamishibai_TestsVersionString[];
16 |
17 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-Kamishibai_Tests/Pods-Kamishibai_Tests.debug.xcconfig:
--------------------------------------------------------------------------------
1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES
2 | FRAMEWORK_SEARCH_PATHS = $(inherited) $(PLATFORM_DIR)/Developer/Library/Frameworks "$PODS_CONFIGURATION_BUILD_DIR/FBSnapshotTestCase" "$PODS_CONFIGURATION_BUILD_DIR/Kamishibai"
3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
4 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks'
5 | OTHER_CFLAGS = $(inherited) -iquote "$PODS_CONFIGURATION_BUILD_DIR/FBSnapshotTestCase/FBSnapshotTestCase.framework/Headers" $(inherited) -iquote "$PODS_CONFIGURATION_BUILD_DIR/Kamishibai/Kamishibai.framework/Headers"
6 | OTHER_LDFLAGS = $(inherited) -framework "FBSnapshotTestCase"
7 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS"
8 | PODS_BUILD_DIR = $BUILD_DIR
9 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
10 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/.
11 | PODS_ROOT = ${SRCROOT}/Pods
12 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-Kamishibai_Tests/Pods-Kamishibai_Tests.modulemap:
--------------------------------------------------------------------------------
1 | framework module Pods_Kamishibai_Tests {
2 | umbrella header "Pods-Kamishibai_Tests-umbrella.h"
3 |
4 | export *
5 | module * { export * }
6 | }
7 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-Kamishibai_Tests/Pods-Kamishibai_Tests.release.xcconfig:
--------------------------------------------------------------------------------
1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES
2 | FRAMEWORK_SEARCH_PATHS = $(inherited) $(PLATFORM_DIR)/Developer/Library/Frameworks "$PODS_CONFIGURATION_BUILD_DIR/FBSnapshotTestCase" "$PODS_CONFIGURATION_BUILD_DIR/Kamishibai"
3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
4 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks'
5 | OTHER_CFLAGS = $(inherited) -iquote "$PODS_CONFIGURATION_BUILD_DIR/FBSnapshotTestCase/FBSnapshotTestCase.framework/Headers" $(inherited) -iquote "$PODS_CONFIGURATION_BUILD_DIR/Kamishibai/Kamishibai.framework/Headers"
6 | OTHER_LDFLAGS = $(inherited) -framework "FBSnapshotTestCase"
7 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS"
8 | PODS_BUILD_DIR = $BUILD_DIR
9 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
10 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/.
11 | PODS_ROOT = ${SRCROOT}/Pods
12 |
--------------------------------------------------------------------------------
/Example/Tests/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 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 |
24 |
25 |
--------------------------------------------------------------------------------
/Example/Tests/KamishibaiTests.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 | import XCTest
3 | @testable import Kamishibai_Example
4 |
5 | class KamishibaiTests: XCTestCase {
6 |
7 | override func setUp() {
8 | super.setUp()
9 | // Put setup code here. This method is called before the invocation of each test method in the class.
10 | }
11 |
12 | override func tearDown() {
13 | // Put teardown code here. This method is called after the invocation of each test method in the class.
14 | super.tearDown()
15 | }
16 |
17 | func testTransition() {
18 | // This is an example of a functional test case.
19 | // app.
20 | }
21 |
22 | // func testPerformanceExample() {
23 | // // This is an example of a performance test case.
24 | // self.measure() {
25 | // // Put the code you want to measure the time of here.
26 | // }
27 | // }
28 |
29 | }
30 |
31 |
--------------------------------------------------------------------------------
/Kamishibai.podspec:
--------------------------------------------------------------------------------
1 | #
2 | # Be sure to run `pod lib lint Kamishibai.podspec' to ensure this is a
3 | # valid spec before submitting.
4 | #
5 | # Any lines starting with a # are optional, but their use is encouraged
6 | # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html
7 | #
8 |
9 | Pod::Spec.new do |s|
10 | s.name = 'Kamishibai'
11 | s.version = '0.1.1'
12 | s.summary = 'Kamishibai makes easy to create long tutorial.'
13 | s.description = <<-DESC
14 | Manage progress of tutorial
15 | Support presenting transitioning of UIViewController
16 | Support push/pop transitioning of NavigationController
17 | Focus with animation where you want
18 | Support custom guide view
19 | DESC
20 |
21 | s.homepage = 'https://github.com/Matzo/Kamishibai'
22 | s.license = { :type => 'MIT', :file => 'LICENSE' }
23 | s.author = { 'ksk.matsuo@gmail.com' => 'ksk.matsuo@gmail.com' }
24 | s.source = { :git => 'https://github.com/Matzo/Kamishibai.git', :tag => s.version.to_s }
25 | s.social_media_url = 'https://twitter.com/ksk_matsuo'
26 |
27 | s.ios.deployment_target = '9.0'
28 |
29 | s.source_files = 'Kamishibai/Classes/**/*'
30 | end
31 |
--------------------------------------------------------------------------------
/Kamishibai/Assets/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Matzo/Kamishibai/0cf9ff85792be0587302db22f08eb8793249cc63/Kamishibai/Assets/.gitkeep
--------------------------------------------------------------------------------
/Kamishibai/Classes/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Matzo/Kamishibai/0cf9ff85792be0587302db22f08eb8793249cc63/Kamishibai/Classes/.gitkeep
--------------------------------------------------------------------------------
/Kamishibai/Classes/Kamishibai.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Kamishibai.swift
3 | // Hello
4 | //
5 | // Created by Keisuke Matsuo on 2017/08/12.
6 | //
7 | //
8 |
9 | import Foundation
10 |
11 | public protocol KamishibaiSceneIdentifierType {
12 | var intValue: Int { get }
13 | // static var count: Int { get }
14 | }
15 |
16 | public extension KamishibaiSceneIdentifierType where Self: RawRepresentable, Self.RawValue == Int {
17 | var intValue: Int {
18 | return rawValue
19 | }
20 | }
21 |
22 | public protocol KamishibaiCustomViewAnimation: class {
23 | func show(animated: Bool, fulfill: @escaping () -> Void)
24 | func hide(animated: Bool, fulfill: @escaping () -> Void)
25 | }
26 |
27 | public func == (l: KamishibaiSceneIdentifierType?, r: KamishibaiSceneIdentifierType?) -> Bool {
28 | guard let l = l, let r = r else { return false }
29 | return l.intValue == r.intValue
30 | }
31 |
32 | public class Kamishibai {
33 |
34 | // MARK: Properties
35 | public weak var currentViewController: UIViewController?
36 | public var userInfo = [AnyHashable: Any]()
37 | public weak var currentTodo: KamishibaiScene?
38 | public var scenes = [KamishibaiScene]()
39 | public var completion: (() -> Void)?
40 | public let transition = KamishibaiTransitioning()
41 | public var focus = KamishibaiFocusViewController.create()
42 |
43 | // MARK: Initialization
44 | public init(initialViewController: UIViewController) {
45 | currentViewController = initialViewController
46 | focus.kamishibai = self
47 | }
48 |
49 | // MARK: Private Methods
50 | func nextTodo() -> KamishibaiScene? {
51 | guard let current = currentTodo else { return nil }
52 | guard let startIndex = scenes.index(where: { $0 == current }) else { return nil }
53 | guard scenes.count > startIndex + 1 else { return nil }
54 |
55 | for i in (startIndex + 1).. index + 1 {
182 | self.scenes.insert(scene, at: index + 1)
183 | } else {
184 | self.scenes.append(scene)
185 | }
186 | }
187 |
188 | public func clean() {
189 | completion = nil
190 | userInfo.removeAll()
191 | currentTodo = nil
192 | scenes.removeAll()
193 | focus.dismiss(animated: true, completion: { [weak self] in
194 | self?.focus.clean()
195 | self?.focus = KamishibaiFocusViewController.create()
196 | })
197 | }
198 | }
199 |
200 |
--------------------------------------------------------------------------------
/Kamishibai/Classes/KamishibaiFocusView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // KamishibaiFocusView.swift
3 | // Hello
4 | //
5 | // Created by Keisuke Matsuo on 2017/08/12.
6 | //
7 | //
8 |
9 | import UIKit
10 |
11 | public class KamishibaiFocusView: UIView {
12 |
13 | // MARK: Properties
14 | public var focus: FocusType?
15 | public var animationDuration: TimeInterval = 0.5
16 | var maskLayer: CAShapeLayer = {
17 | let layer = CAShapeLayer()
18 | layer.fillRule = kCAFillRuleEvenOdd
19 | layer.fillColor = UIColor.black.cgColor
20 | return layer
21 | }()
22 |
23 | // MARK: Initializing
24 | override init(frame: CGRect) {
25 | super.init(frame: frame)
26 | setup()
27 | }
28 |
29 | required public init?(coder aDecoder: NSCoder) {
30 | fatalError("init(coder:) has not been implemented")
31 | }
32 |
33 | override public func layoutSubviews() {
34 | super.layoutSubviews()
35 | maskLayer.frame = self.bounds
36 | }
37 |
38 | func setup() {
39 | backgroundColor = UIColor.black.withAlphaComponent(0.55)
40 | layer.mask = maskLayer
41 | isUserInteractionEnabled = false
42 | }
43 |
44 | // MARK: Public Methods
45 | public func appear(focus: FocusType, completion: (() -> Void)? = nil) {
46 | CATransaction.begin()
47 | CATransaction.setCompletionBlock(completion)
48 |
49 | layer.add(alphaAnimation(animationDuration, from: 0.0, to: 1.0), forKey: nil)
50 | maskLayer.add(appearAnimation(animationDuration, focus: focus), forKey: nil)
51 | self.focus = focus
52 |
53 | CATransaction.commit()
54 | }
55 |
56 | public func move(to focus: FocusType, completion: (() -> Void)? = nil) {
57 | CATransaction.begin()
58 | CATransaction.setCompletionBlock(completion)
59 |
60 | maskLayer.add(moveAnimation(animationDuration, focus: focus), forKey: nil)
61 | self.focus = focus
62 |
63 | CATransaction.commit()
64 | }
65 |
66 | public func disappear(completion: (() -> Void)? = nil) {
67 | CATransaction.begin()
68 | CATransaction.setCompletionBlock(completion)
69 |
70 | layer.add(alphaAnimation(animationDuration, to: 0.0), forKey: nil)
71 | self.focus = nil
72 |
73 | CATransaction.commit()
74 | }
75 |
76 | // MARK: Private Methods
77 |
78 | // MARK: CAAnimation
79 | func maskPath(_ path: UIBezierPath) -> UIBezierPath {
80 | let screenRect = UIScreen.main.bounds
81 | return [path].reduce(UIBezierPath(rect: screenRect)) {
82 | $0.append($1)
83 | return $0
84 | }
85 | }
86 |
87 | func appearAnimation(_ duration: TimeInterval, focus: FocusType) -> CAAnimation {
88 | let beginPath = maskPath(focus.initialPath != nil ? focus.initialPath! : focus.path)
89 | let endPath = maskPath(focus.path)
90 | return pathAnimation(duration, beginPath: beginPath, endPath: endPath)
91 | }
92 |
93 | func disappearAnimation(_ duration: TimeInterval) -> CAAnimation {
94 | return alphaAnimation(duration, from: 1.0, to: 0.0)
95 | }
96 |
97 | func moveAnimation(_ duration: TimeInterval, focus: FocusType) -> CAAnimation {
98 | let endPath = maskPath(focus.path)
99 | return pathAnimation(duration, beginPath: nil, endPath: endPath)
100 | }
101 |
102 | func alphaAnimation(_ duration: TimeInterval, from: CGFloat? = nil, to: CGFloat) -> CAAnimation {
103 | let animation = CABasicAnimation(keyPath: "opacity")
104 | animation.duration = duration
105 | animation.timingFunction = CAMediaTimingFunction(controlPoints: 0.66, 0, 0.33, 1)
106 | if let from = from {
107 | animation.fromValue = from
108 | }
109 | animation.toValue = to
110 | animation.isRemovedOnCompletion = false
111 | animation.fillMode = kCAFillModeForwards
112 | return animation
113 | }
114 |
115 | func pathAnimation(_ duration: TimeInterval, beginPath: UIBezierPath?, endPath: UIBezierPath) -> CAAnimation {
116 | let animation = CABasicAnimation(keyPath: "path")
117 | animation.duration = duration
118 | animation.timingFunction = CAMediaTimingFunction(controlPoints: 0.66, 0, 0.33, 1)
119 | if let path = beginPath {
120 | animation.fromValue = path.cgPath
121 | }
122 | animation.toValue = endPath.cgPath
123 | animation.isRemovedOnCompletion = false
124 | animation.fillMode = kCAFillModeForwards
125 | return animation
126 | }
127 | }
128 |
129 | public protocol FocusType {
130 | var frame: CGRect { get }
131 | var initialPath: UIBezierPath? { get }
132 | var path: UIBezierPath { get }
133 | }
134 |
135 | public struct Focus {
136 | public struct Rect: FocusType {
137 | public var frame: CGRect
138 | public var initialFrame: CGRect?
139 | public var initialPath: UIBezierPath? {
140 | if let initialFrame = initialFrame {
141 | return UIBezierPath(rect: initialFrame)
142 | }
143 | return nil
144 | }
145 | public var path: UIBezierPath {
146 | return UIBezierPath(rect: frame)
147 | }
148 | public init(frame: CGRect, initialFrame: CGRect? = nil) {
149 | self.frame = frame
150 | self.initialFrame = initialFrame
151 | }
152 | }
153 | public struct RoundRect: FocusType {
154 | public var frame: CGRect
155 | public var initialFrame: CGRect?
156 | public var cornerRadius: CGFloat
157 | public var initialPath: UIBezierPath? {
158 | if let initialFrame = initialFrame {
159 | return UIBezierPath(roundedRect: initialFrame, cornerRadius: cornerRadius)
160 | }
161 | return nil
162 | }
163 | public var path: UIBezierPath {
164 | return UIBezierPath(roundedRect: frame, cornerRadius: cornerRadius)
165 | }
166 | public init(frame: CGRect, initialFrame: CGRect? = nil, cornerRadius: CGFloat) {
167 | self.frame = frame
168 | self.initialFrame = initialFrame
169 | self.cornerRadius = cornerRadius
170 | }
171 | }
172 | public struct Oval: FocusType {
173 | public var frame: CGRect
174 | public var initialFrame: CGRect?
175 | public var initialPath: UIBezierPath? {
176 | if let initialFrame = initialFrame {
177 | return UIBezierPath(ovalIn: initialFrame)
178 | }
179 | return nil
180 | }
181 | public var path: UIBezierPath {
182 | return UIBezierPath(ovalIn: frame)
183 | }
184 | public init(frame: CGRect, initialFrame: CGRect? = nil) {
185 | self.frame = frame
186 | self.initialFrame = initialFrame
187 | }
188 | }
189 | }
190 |
--------------------------------------------------------------------------------
/Kamishibai/Classes/KamishibaiFocusViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // KamishibaiFocusViewController.swift
3 | // Hello
4 | //
5 | // Created by Keisuke Matsuo on 2017/08/12.
6 | //
7 | //
8 |
9 | import UIKit
10 |
11 | public enum FocusAccesoryViewPosition {
12 | case topRight(CGPoint)
13 | case bottomRight(CGPoint)
14 | case center(CGPoint)
15 | case point(CGPoint)
16 | }
17 |
18 | public class KamishibaiFocusViewController: UIViewController {
19 |
20 | // MARK: Properties
21 | let focusView = KamishibaiFocusView(frame: CGRect.zero)
22 | var customViews = [UIView]()
23 | let transitioning = KamishibaiFocusTransitioning(state: .presenting)
24 | weak var kamishibai: Kamishibai?
25 | public var isFocusing: Bool {
26 | return self.view.superview != nil
27 | }
28 |
29 | // MARK: UIViewController Lifecycle
30 | override public func viewDidLoad() {
31 | super.viewDidLoad()
32 | setupViews()
33 | }
34 |
35 | var first = true
36 | override public func viewDidLayoutSubviews() {
37 | super.viewDidLayoutSubviews()
38 | focusView.frame = view.bounds
39 | }
40 |
41 | // MARK: Initializing
42 | func setupViews() {
43 | view.addSubview(focusView)
44 | view.backgroundColor = UIColor.clear
45 | // view.isUserInteractionEnabled = false
46 | }
47 |
48 | // MARK: Public Methods
49 | public func on(view: UIView? = nil, focus: FocusType, completion: (() -> Void)? = nil) {
50 | guard let targetView = view ?? kamishibai?.currentViewController?.view else { return }
51 |
52 | let focusBlock = {
53 | if let _ = self.focusView.focus {
54 | self.focusView.move(to: focus, completion: completion)
55 | } else {
56 | self.focusView.appear(focus: focus, completion: completion)
57 | }
58 | }
59 |
60 | if self.view.superview == nil {
61 | present(onView: targetView, completion: {
62 | focusBlock()
63 | })
64 | } else {
65 | focusBlock()
66 | }
67 | }
68 |
69 | override public func dismiss(animated flag: Bool, completion: (() -> Swift.Void)? = nil) {
70 | hideAllCustomViews()
71 | let duration = isFocusing ? focusView.animationDuration : 0
72 | UIView.animate(withDuration: duration, delay: 0,
73 | usingSpringWithDamping: 1, initialSpringVelocity: 1, options: .curveLinear, animations: {
74 | self.view.alpha = 0
75 | }) { (_) in
76 | self.view.alpha = 1
77 | self.view.removeFromSuperview()
78 | self.clean()
79 | completion?()
80 | }
81 | }
82 |
83 | public func addCustomView(view: UIView, position: FocusAccesoryViewPosition, completion: @escaping () -> Void = {}) {
84 | self.customViews.append(view)
85 | self.view.addSubview(view)
86 | view.sizeToFit()
87 | switch position {
88 | case .topRight(let adjust):
89 | view.frame.origin.x = self.view.bounds.size.width - view.frame.size.width + adjust.x
90 | if let focus = focusView.focus {
91 | view.frame.origin.y = focus.frame.minY - view.frame.size.height + adjust.y
92 | }
93 | case .bottomRight(let adjust):
94 | view.frame.origin.x = self.view.bounds.size.width - view.frame.size.width + adjust.x
95 | if let focus = focusView.focus {
96 | view.frame.origin.y = focus.frame.maxY + adjust.y
97 | }
98 | case .center(let adjust):
99 | if let focus = focusView.focus {
100 | view.center = CGPoint(x: focus.frame.midX + adjust.x, y: focus.frame.midY + adjust.y)
101 | } else {
102 | view.center = CGPoint(x: self.view.frame.midX + adjust.x, y: self.view.frame.midY + adjust.y)
103 | }
104 | case .point(let point):
105 | view.frame.origin = point
106 | }
107 |
108 | if let animate = view as? KamishibaiCustomViewAnimation {
109 | animate.show(animated: true, fulfill: completion)
110 | }
111 | }
112 |
113 | public func hideAllCustomViews() {
114 | customViews.forEach { (view) in
115 | if let animate = view as? KamishibaiCustomViewAnimation {
116 | animate.hide(animated: true, fulfill: {})
117 | }
118 | }
119 | }
120 |
121 | public func clean() {
122 | customViews.forEach { (view) in
123 | view.removeFromSuperview()
124 | }
125 | customViews.removeAll()
126 | focusView.disappear()
127 | focusView.maskLayer.path = nil
128 | }
129 |
130 | // MARK: Private Methods
131 | func present(onView view: UIView, completion: (() -> Void)? = nil) {
132 | focusView.focus = nil
133 | view.addSubview(self.view)
134 | completion?()
135 | }
136 |
137 | // MARK: Class Methods
138 | static func create() -> KamishibaiFocusViewController {
139 | let vc = KamishibaiFocusViewController()
140 | vc.transitioningDelegate = vc
141 | vc.modalPresentationStyle = .overCurrentContext
142 | return vc
143 | }
144 | }
145 |
146 | // MARK: - Transitioning
147 | extension KamishibaiFocusViewController: UIViewControllerTransitioningDelegate {
148 | public func animationController(forPresented presented: UIViewController, presenting: UIViewController,
149 | source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
150 | transitioning.state = .presenting
151 | return transitioning
152 | }
153 | public func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
154 | transitioning.state = .dismissing
155 | return transitioning
156 | }
157 | }
158 |
159 | enum KamishibaiFocusTransitioningState {
160 | case presenting
161 | case dismissing
162 | }
163 |
164 | class KamishibaiFocusTransitioning: NSObject, UIViewControllerAnimatedTransitioning {
165 | var state: KamishibaiFocusTransitioningState
166 |
167 | init(state: KamishibaiFocusTransitioningState) {
168 | self.state = state
169 | }
170 |
171 | func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
172 | switch self.state {
173 | case .presenting: return 0.5
174 | case .dismissing: return 0.5
175 | }
176 | }
177 |
178 | func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
179 | guard let toVC = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to) else { return }
180 | guard let fromVC = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from) else { return }
181 | let duration = self.transitionDuration(using: transitionContext)
182 | let containerView = transitionContext.containerView
183 |
184 | switch self.state {
185 | case .presenting:
186 | containerView.addSubview(toVC.view)
187 | toVC.view.alpha = 0
188 |
189 | UIView.animate(withDuration: duration, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 1,
190 | options: [.beginFromCurrentState], animations:
191 | { () -> Void in
192 | toVC.view.alpha = 1
193 | }, completion: { (done) -> Void in
194 | transitionContext.completeTransition(true)
195 | })
196 |
197 | case .dismissing:
198 | containerView.addSubview(fromVC.view)
199 | if fromVC.modalPresentationStyle != .overCurrentContext {
200 | containerView.insertSubview(toVC.view, at: 0)
201 | }
202 |
203 | fromVC.view.alpha = 1
204 | UIView.animate(withDuration: duration, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 1,
205 | options: [.beginFromCurrentState], animations:
206 | { () -> Void in
207 | fromVC.view.alpha = 0
208 | }, completion: { (done) -> Void in
209 | transitionContext.completeTransition(true)
210 | })
211 |
212 | }
213 | }
214 | }
215 |
--------------------------------------------------------------------------------
/Kamishibai/Classes/KamishibaiScene.swift:
--------------------------------------------------------------------------------
1 | //
2 | // KamishibaiScene.swift
3 | // Pods
4 | //
5 | // Created by Keisuke Matsuo on 2017/08/12.
6 | //
7 | //
8 |
9 | import Foundation
10 |
11 | public typealias KamishibaiSceneBlock = ((KamishibaiScene) -> Void)
12 | public class KamishibaiScene: NSObject {
13 |
14 | // MARK: Properties
15 | public weak var kamishibai: Kamishibai?
16 | public var identifier: KamishibaiSceneIdentifierType?
17 | public var transition: KamishibaiTransitioningType?
18 | public var sceneBlock: KamishibaiSceneBlock
19 |
20 | var fulfillGestures: [UIGestureRecognizer] = []
21 | var isFinished: Bool = false
22 | var tapInRect: CGRect?
23 |
24 | // MARK: Initialization
25 | public init(id: KamishibaiSceneIdentifierType? = nil,
26 | transition: KamishibaiTransitioningType? = nil,
27 | scene: @escaping KamishibaiSceneBlock) {
28 | self.identifier = id
29 | self.transition = transition
30 | self.sceneBlock = scene
31 | }
32 |
33 | // MARK: Public Methods
34 | public func fulfill() {
35 | kamishibai?.fulfill(scene: self)
36 | disposeGestures()
37 | }
38 | public func fulfillWhenTap(view: UIView, inRect: CGRect? = nil) {
39 | // addTapGesture to view
40 | let tap = UITapGestureRecognizer(target: self, action: #selector(KamishibaiScene.didTapFulfill(gesture:)))
41 | view.addGestureRecognizer(tap)
42 | self.fulfillGestures.append(tap)
43 | tapInRect = inRect
44 | }
45 | public func fulfillWhenTapFocus() {
46 | guard let kamishibai = kamishibai else { return }
47 | guard let focus = kamishibai.focus.focusView.focus else { return }
48 | fulfillWhenTap(view: kamishibai.focus.view, inRect: focus.frame)
49 | }
50 |
51 | // MARK: Private Methods
52 | func disposeGestures() {
53 | fulfillGestures.forEach { (gesture) in
54 | gesture.view?.removeGestureRecognizer(gesture)
55 | }
56 | fulfillGestures.removeAll()
57 | }
58 |
59 | @objc func didTapFulfill(gesture: UITapGestureRecognizer) {
60 | if let inRect = tapInRect, let view = gesture.view {
61 | let point = gesture.location(in: view)
62 | if inRect.contains(point) {
63 | fulfill()
64 | }
65 | } else {
66 | fulfill()
67 | }
68 | }
69 | }
70 |
71 | public func == (l: KamishibaiScene, r: KamishibaiScene) -> Bool {
72 | if l === r {
73 | return true
74 | }
75 | guard let lId = l.identifier, let rId = r.identifier else { return false }
76 | return lId == rId
77 | }
78 |
--------------------------------------------------------------------------------
/Kamishibai/Classes/KamishibaiTransitioning.swift:
--------------------------------------------------------------------------------
1 | //
2 | // KamishibaiTransitioning.swift
3 | // Pods
4 | //
5 | // Created by Keisuke Matsuo on 2017/08/12.
6 | //
7 | //
8 |
9 | import Foundation
10 |
11 | public typealias NextViewController = UIViewController
12 | public typealias FulfillTransitioning = ((NextViewController) -> Void)
13 | public enum KamishibaiTransitioningType {
14 | case push(UIViewController)
15 | case pop
16 | case popToRoot
17 | case present(UIViewController)
18 | case dismiss
19 | case custom((FulfillTransitioning) -> Void)
20 | }
21 |
22 | public class KamishibaiTransitioning: NSObject {
23 | weak var originalNavigationDelegate: UINavigationControllerDelegate?
24 | var fulfill: FulfillTransitioning?
25 |
26 | // MARK: Public Methods
27 | public func transision(fromVC: UIViewController, type: KamishibaiTransitioningType, animated: Bool, completion: @escaping (NextViewController) -> Void) {
28 | switch type {
29 | case .push(let toVC):
30 | push(fromVC: fromVC, toVC: toVC, animated: animated, fulfill: completion)
31 | case .pop:
32 | pop(fromVC: fromVC, animated: animated, fulfill: completion)
33 | case .popToRoot:
34 | popToRoot(fromVC: fromVC, animated: animated, fulfill: completion)
35 | case .present(let toVC):
36 | present(viewController: toVC, fromVC: fromVC, animated: animated, fulfill: completion)
37 | case .dismiss:
38 | dismiss(fromVC: fromVC, animated: animated, fulfill: completion)
39 | case .custom(let fulfill):
40 | fulfill(completion)
41 | }
42 | }
43 |
44 | // MARK: Private Methods
45 | func push(fromVC: UIViewController, toVC: UIViewController, animated: Bool,
46 | fulfill: @escaping FulfillTransitioning) {
47 | guard let navi = fromVC as? UINavigationController ?? fromVC.navigationController else {
48 | fulfill(fromVC)
49 | return
50 | }
51 | self.fulfill = fulfill
52 | prepareDelegate(fromVC: fromVC)
53 | navi.pushViewController(toVC, animated: animated)
54 | }
55 |
56 | func pop(fromVC: UIViewController, animated: Bool, fulfill: @escaping FulfillTransitioning) {
57 | guard let navi = fromVC as? UINavigationController ?? fromVC.navigationController else {
58 | fulfill(fromVC)
59 | return
60 | }
61 | self.fulfill = fulfill
62 | prepareDelegate(fromVC: fromVC)
63 | navi.popViewController(animated: animated)
64 | }
65 |
66 | func popToRoot(fromVC: UIViewController, animated: Bool, fulfill: @escaping FulfillTransitioning) {
67 | guard let navi = fromVC as? UINavigationController ?? fromVC.navigationController else {
68 | fulfill(fromVC)
69 | return
70 | }
71 | self.fulfill = fulfill
72 | prepareDelegate(fromVC: fromVC)
73 | navi.popToRootViewController(animated: animated)
74 | }
75 |
76 | func prepareDelegate(fromVC: UIViewController) {
77 | if let originalDelegate = fromVC.navigationController?.delegate {
78 | originalNavigationDelegate = originalDelegate
79 | }
80 | fromVC.navigationController?.delegate = self
81 | }
82 |
83 | func present(viewController: UIViewController, fromVC: UIViewController, animated: Bool, fulfill: @escaping FulfillTransitioning) {
84 | fromVC.present(viewController, animated: animated) {
85 | fulfill(viewController)
86 | }
87 | }
88 |
89 | func dismiss(fromVC: UIViewController, animated: Bool, fulfill: @escaping FulfillTransitioning) {
90 | let parent = frontViewController(fromVC)
91 | fromVC.dismiss(animated: animated) {
92 | fulfill(parent)
93 | }
94 | }
95 |
96 | func frontViewController(_ vc: UIViewController) -> UIViewController {
97 | guard let presentingVC = vc.presentingViewController else { return vc }
98 | if let navi = presentingVC as? UINavigationController {
99 | return navi.topViewController ?? vc
100 | }
101 | if let tab = presentingVC as? UITabBarController, let vcs = tab.viewControllers {
102 | return vcs[tab.selectedIndex]
103 | }
104 | return presentingVC
105 | }
106 | }
107 |
108 | extension KamishibaiTransitioning: UINavigationControllerDelegate {
109 | public func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) {
110 | originalNavigationDelegate?.navigationController?(navigationController, willShow: viewController, animated: animated)
111 | }
112 | public func navigationController(_ navigationController: UINavigationController, didShow viewController: UIViewController, animated: Bool) {
113 | if let originalDelegate = originalNavigationDelegate {
114 | navigationController.delegate = originalDelegate
115 | originalDelegate.navigationController?(navigationController, didShow: viewController, animated: animated)
116 | }
117 | resetNavigationDelegate(navigationController)
118 |
119 | if let fulfill = self.fulfill, let topVC = navigationController.topViewController {
120 | fulfill(topVC)
121 | }
122 | }
123 |
124 | public func navigationControllerSupportedInterfaceOrientations(_ navigationController: UINavigationController) -> UIInterfaceOrientationMask {
125 | return originalNavigationDelegate?.navigationControllerSupportedInterfaceOrientations?(navigationController)
126 | ?? UIApplication.shared.keyWindow?.rootViewController?.supportedInterfaceOrientations
127 | ?? .all
128 | }
129 |
130 | public func navigationControllerPreferredInterfaceOrientationForPresentation(_ navigationController: UINavigationController) -> UIInterfaceOrientation {
131 | return originalNavigationDelegate?.navigationControllerPreferredInterfaceOrientationForPresentation?(navigationController)
132 | ?? UIApplication.shared.keyWindow?.rootViewController?.preferredInterfaceOrientationForPresentation
133 | ?? .unknown
134 | }
135 |
136 | public func navigationController(_ navigationController: UINavigationController, interactionControllerFor animationController: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
137 | return originalNavigationDelegate?.navigationController?(navigationController, interactionControllerFor:animationController)
138 | }
139 |
140 | public func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationControllerOperation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
141 | return originalNavigationDelegate?.navigationController?(navigationController, animationControllerFor: operation, from: fromVC, to: toVC)
142 | }
143 |
144 | func resetNavigationDelegate(_ navigationController: UINavigationController) {
145 | navigationController.delegate = originalNavigationDelegate
146 | originalNavigationDelegate = nil
147 | }
148 | }
149 |
150 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Matzo
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Kamishibai
2 |
3 | [](https://travis-ci.org/ksk.matsuo@gmail.com/Kamishibai)
4 | [](http://cocoapods.org/pods/Kamishibai)
5 | [](http://cocoapods.org/pods/Kamishibai)
6 | [](http://cocoapods.org/pods/Kamishibai)
7 | 
8 |
9 | Kamishibai makes easy to create long tutorial.
10 |
11 | ## Features
12 |
13 | 
14 |
15 | - Manage progress of tutorial
16 | - Support presenting transitioning of UIViewController
17 | - Support push/pop transitioning of NavigationController
18 | - Focus with animation where you want
19 | - Support custom guide view
20 |
21 | ## Example
22 |
23 | To run the example project, clone the repo, and run `pod install` from the Example directory first.
24 |
25 | ``` swift
26 | // create Kamishibai instance
27 | let kamishibai = Kamishibai(initialViewController: self)
28 |
29 | // append first scene and focus CGRect(x: 0, y: 100, width: 50, height: 50)
30 | kamishibai.append(KamishibaiScene(scene: { (scene) in
31 | guard let vc = scene.kamishibai?.currentViewController as? UIViewController else { return }
32 | let frame = CGRect(x: 0, y: 100, width: 50, height: 50)
33 | scene.kamishibai?.focus.on(view: vc.navigationController?.view, focus: Focus.Rect(frame: frame))
34 | scene.fulfillWhenTapFocus()
35 | }))
36 |
37 | // append second scene and add customized view
38 | kamishibai.append(KamishibaiScene(transition: .push(SecondViewController.create()), scene: { (scene) in
39 | let guide = SampleGuideView.create()
40 | scene.kamishibai?.focus.addCustomView(view: guide, position: .bottomRight(CGPoint.zero))
41 | scene.fulfillWhenTap(view: guide.button)
42 | }))
43 |
44 | //
45 | kamishibai.startStory()
46 | ```
47 |
48 | ## Requirements
49 | - iOS 9.0
50 | - Swift 4.0
51 |
52 | ## Installation
53 |
54 | **Cocoapods**
55 |
56 | Kamishibai is available through [CocoaPods](http://cocoapods.org). To install
57 | it, simply add the following line to your Podfile:
58 |
59 | ```ruby
60 | pod "Kamishibai"
61 | ```
62 |
63 | **Carthage**
64 |
65 | To integrate into your Xcode project using Carthage, specify it in your Cartfile:
66 |
67 | ```ruby
68 | github "Matzo/Kamishibai"
69 | ```
70 |
71 |
72 | ## Author
73 |
74 | ksk.matsuo@gmail.com
75 |
76 | ## License
77 |
78 | Kamishibai is available under the MIT license. See the LICENSE file for more info.
79 |
--------------------------------------------------------------------------------
/Screenshots/Kamishibai1.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Matzo/Kamishibai/0cf9ff85792be0587302db22f08eb8793249cc63/Screenshots/Kamishibai1.gif
--------------------------------------------------------------------------------
/_Pods.xcodeproj:
--------------------------------------------------------------------------------
1 | Example/Pods/Pods.xcodeproj
--------------------------------------------------------------------------------