├── .codeclimate.yml ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── PULL_REQUEST_TEMPLATE │ └── simple_pull_request.md └── workflows │ └── continuous-integration.yml ├── .gitignore ├── .ruby-version ├── .slather.yml ├── .swiftlint.yml ├── AUTHORS ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Documentation └── migrating_to_2.0.0.md ├── Examples ├── App Extensions Example │ └── Sources │ │ ├── Core │ │ ├── AppDelegate.swift │ │ ├── Storyboards │ │ │ └── Base.lproj │ │ │ │ ├── LaunchScreen.storyboard │ │ │ │ └── Main.storyboard │ │ └── View Controllers │ │ │ └── ViewController.swift │ │ ├── KeyboardExtension │ │ ├── Info.plist │ │ ├── KeyboardViewController.swift │ │ └── UI │ │ │ └── Keyboard.xib │ │ └── Supporting Files │ │ ├── Assets.xcassets │ │ └── AppIcon.appiconset │ │ │ ├── AppEx-1024.png │ │ │ ├── Appex-20.png │ │ │ ├── Appex-20@2x-1.png │ │ │ ├── Appex-20@2x.png │ │ │ ├── Appex-20@3x.png │ │ │ ├── Appex-29.png │ │ │ ├── Appex-29@2x-1.png │ │ │ ├── Appex-29@2x.png │ │ │ ├── Appex-29@3x.png │ │ │ ├── Appex-40.png │ │ │ ├── Appex-40@2x-1.png │ │ │ ├── Appex-40@2x.png │ │ │ ├── Appex-40@3x.png │ │ │ ├── Appex-60@2x.png │ │ │ ├── Appex-60@3x.png │ │ │ ├── Appex-76.png │ │ │ ├── Appex-76@2x.png │ │ │ ├── Appex-83,5@2x.png │ │ │ └── Contents.json │ │ └── Info.plist ├── Example │ ├── Snapshot Tests │ │ ├── DefaultExampleSnapshotTests.swift │ │ ├── Extensions │ │ │ ├── DeviceKit+Extensions.swift │ │ │ ├── UIDeviceOrientation+Convenience.swift │ │ │ ├── UIImage+Extensions.swift │ │ │ └── XCUIApplication+Launch.swift │ │ └── Supporting Files │ │ │ ├── Info.plist │ │ │ └── Snapshots │ │ │ └── ReferenceImages_64 │ │ │ └── InstructionsSnapshotTests.DefaultExampleSnapshotTests │ │ │ ├── testFlowInController_controller_0_portrait_iPad__10th_generation__17_0@2x.png │ │ │ ├── testFlowInController_controller_0_portrait_iPhone16_1_17_0@3x.png │ │ │ ├── testFlowInController_controller_0_portrait_iPhone_SE__3rd_generation__17_0@2x.png │ │ │ ├── testFlowInController_controller_1_portrait_iPad__10th_generation__17_0@2x.png │ │ │ ├── testFlowInController_controller_1_portrait_iPhone16_1_17_0@3x.png │ │ │ ├── testFlowInController_controller_1_portrait_iPhone_SE__3rd_generation__17_0@2x.png │ │ │ ├── testFlowInController_controller_2_portrait_iPad__10th_generation__17_0@2x.png │ │ │ ├── testFlowInController_controller_2_portrait_iPhone16_1_17_0@3x.png │ │ │ ├── testFlowInController_controller_2_portrait_iPhone_SE__3rd_generation__17_0@2x.png │ │ │ ├── testFlowInController_controller_3_portrait_iPad__10th_generation__17_0@2x.png │ │ │ ├── testFlowInController_controller_3_portrait_iPhone16_1_17_0@3x.png │ │ │ ├── testFlowInController_controller_3_portrait_iPhone_SE__3rd_generation__17_0@2x.png │ │ │ ├── testFlowInController_controller_4_portrait_iPad__10th_generation__17_0@2x.png │ │ │ ├── testFlowInController_controller_4_portrait_iPhone16_1_17_0@3x.png │ │ │ ├── testFlowInController_controller_4_portrait_iPhone_SE__3rd_generation__17_0@2x.png │ │ │ ├── testFlowInIndependentWindow_independentWindow_0_portrait_iPad__10th_generation__17_0@2x.png │ │ │ ├── testFlowInIndependentWindow_independentWindow_0_portrait_iPhone16_1_17_0@3x.png │ │ │ ├── testFlowInIndependentWindow_independentWindow_0_portrait_iPhone_SE__3rd_generation__17_0@2x.png │ │ │ ├── testFlowInIndependentWindow_independentWindow_1_portrait_iPad__10th_generation__17_0@2x.png │ │ │ ├── testFlowInIndependentWindow_independentWindow_1_portrait_iPhone16_1_17_0@3x.png │ │ │ ├── testFlowInIndependentWindow_independentWindow_1_portrait_iPhone_SE__3rd_generation__17_0@2x.png │ │ │ ├── testFlowInIndependentWindow_independentWindow_2_portrait_iPad__10th_generation__17_0@2x.png │ │ │ ├── testFlowInIndependentWindow_independentWindow_2_portrait_iPhone16_1_17_0@3x.png │ │ │ ├── testFlowInIndependentWindow_independentWindow_2_portrait_iPhone_SE__3rd_generation__17_0@2x.png │ │ │ ├── testFlowInIndependentWindow_independentWindow_3_portrait_iPad__10th_generation__17_0@2x.png │ │ │ ├── testFlowInIndependentWindow_independentWindow_3_portrait_iPhone16_1_17_0@3x.png │ │ │ ├── testFlowInIndependentWindow_independentWindow_3_portrait_iPhone_SE__3rd_generation__17_0@2x.png │ │ │ ├── testFlowInIndependentWindow_independentWindow_4_portrait_iPad__10th_generation__17_0@2x.png │ │ │ ├── testFlowInIndependentWindow_independentWindow_4_portrait_iPhone16_1_17_0@3x.png │ │ │ ├── testFlowInIndependentWindow_independentWindow_4_portrait_iPhone_SE__3rd_generation__17_0@2x.png │ │ │ ├── testFlowInWindow_sameWindow_0_portrait_iPad__10th_generation__17_0@2x.png │ │ │ ├── testFlowInWindow_sameWindow_0_portrait_iPhone16_1_17_0@3x.png │ │ │ ├── testFlowInWindow_sameWindow_0_portrait_iPhone_SE__3rd_generation__17_0@2x.png │ │ │ ├── testFlowInWindow_sameWindow_1_portrait_iPad__10th_generation__17_0@2x.png │ │ │ ├── testFlowInWindow_sameWindow_1_portrait_iPhone16_1_17_0@3x.png │ │ │ ├── testFlowInWindow_sameWindow_1_portrait_iPhone_SE__3rd_generation__17_0@2x.png │ │ │ ├── testFlowInWindow_sameWindow_2_portrait_iPad__10th_generation__17_0@2x.png │ │ │ ├── testFlowInWindow_sameWindow_2_portrait_iPhone16_1_17_0@3x.png │ │ │ ├── testFlowInWindow_sameWindow_2_portrait_iPhone_SE__3rd_generation__17_0@2x.png │ │ │ ├── testFlowInWindow_sameWindow_3_portrait_iPad__10th_generation__17_0@2x.png │ │ │ ├── testFlowInWindow_sameWindow_3_portrait_iPhone16_1_17_0@3x.png │ │ │ ├── testFlowInWindow_sameWindow_3_portrait_iPhone_SE__3rd_generation__17_0@2x.png │ │ │ ├── testFlowInWindow_sameWindow_4_portrait_iPad__10th_generation__17_0@2x.png │ │ │ ├── testFlowInWindow_sameWindow_4_portrait_iPhone16_1_17_0@3x.png │ │ │ ├── testFlowInWindow_sameWindow_4_portrait_iPhone_SE__3rd_generation__17_0@2x.png │ │ │ ├── testRotationInController_controller_0_portrait_iPad__10th_generation__17_0@2x.png │ │ │ ├── testRotationInController_controller_0_portrait_iPhone16_1_17_0@3x.png │ │ │ ├── testRotationInController_controller_0_portrait_iPhone_SE__3rd_generation__17_0@2x.png │ │ │ ├── testRotationInController_controller_1_landscapeLeft_iPad__10th_generation__17_0@2x.png │ │ │ ├── testRotationInController_controller_1_landscapeLeft_iPhone16_1_17_0@3x.png │ │ │ ├── testRotationInController_controller_1_landscapeLeft_iPhone_SE__3rd_generation__17_0@2x.png │ │ │ ├── testRotationInController_controller_2_portrait_iPad__10th_generation__17_0@2x.png │ │ │ ├── testRotationInController_controller_2_portrait_iPhone16_1_17_0@3x.png │ │ │ ├── testRotationInController_controller_2_portrait_iPhone_SE__3rd_generation__17_0@2x.png │ │ │ ├── testRotationInController_controller_3_landscapeLeft_iPad__10th_generation__17_0@2x.png │ │ │ ├── testRotationInController_controller_3_landscapeLeft_iPhone16_1_17_0@3x.png │ │ │ ├── testRotationInController_controller_3_landscapeLeft_iPhone_SE__3rd_generation__17_0@2x.png │ │ │ ├── testRotationInController_controller_4_portrait_iPad__10th_generation__17_0@2x.png │ │ │ ├── testRotationInController_controller_4_portrait_iPhone16_1_17_0@3x.png │ │ │ ├── testRotationInController_controller_4_portrait_iPhone_SE__3rd_generation__17_0@2x.png │ │ │ ├── testRotationInIndependentWindow_independentWindow_0_portrait_iPad__10th_generation__17_0@2x.png │ │ │ ├── testRotationInIndependentWindow_independentWindow_0_portrait_iPhone16_1_17_0@3x.png │ │ │ ├── testRotationInIndependentWindow_independentWindow_0_portrait_iPhone_SE__3rd_generation__17_0@2x.png │ │ │ ├── testRotationInIndependentWindow_independentWindow_1_landscapeLeft_iPad__10th_generation__17_0@2x.png │ │ │ ├── testRotationInIndependentWindow_independentWindow_1_landscapeLeft_iPhone16_1_17_0@3x.png │ │ │ ├── testRotationInIndependentWindow_independentWindow_1_landscapeLeft_iPhone_SE__3rd_generation__17_0@2x.png │ │ │ ├── testRotationInIndependentWindow_independentWindow_2_portrait_iPad__10th_generation__17_0@2x.png │ │ │ ├── testRotationInIndependentWindow_independentWindow_2_portrait_iPhone16_1_17_0@3x.png │ │ │ ├── testRotationInIndependentWindow_independentWindow_2_portrait_iPhone_SE__3rd_generation__17_0@2x.png │ │ │ ├── testRotationInIndependentWindow_independentWindow_3_landscapeLeft_iPad__10th_generation__17_0@2x.png │ │ │ ├── testRotationInIndependentWindow_independentWindow_3_landscapeLeft_iPhone16_1_17_0@3x.png │ │ │ ├── testRotationInIndependentWindow_independentWindow_3_landscapeLeft_iPhone_SE__3rd_generation__17_0@2x.png │ │ │ ├── testRotationInIndependentWindow_independentWindow_4_portrait_iPad__10th_generation__17_0@2x.png │ │ │ ├── testRotationInIndependentWindow_independentWindow_4_portrait_iPhone16_1_17_0@3x.png │ │ │ ├── testRotationInIndependentWindow_independentWindow_4_portrait_iPhone_SE__3rd_generation__17_0@2x.png │ │ │ ├── testRotationInWindow_sameWindow_0_portrait_iPad__10th_generation__17_0@2x.png │ │ │ ├── testRotationInWindow_sameWindow_0_portrait_iPhone16_1_17_0@3x.png │ │ │ ├── testRotationInWindow_sameWindow_0_portrait_iPhone_SE__3rd_generation__17_0@2x.png │ │ │ ├── testRotationInWindow_sameWindow_1_landscapeLeft_iPad__10th_generation__17_0@2x.png │ │ │ ├── testRotationInWindow_sameWindow_1_landscapeLeft_iPhone16_1_17_0@3x.png │ │ │ ├── testRotationInWindow_sameWindow_1_landscapeLeft_iPhone_SE__3rd_generation__17_0@2x.png │ │ │ ├── testRotationInWindow_sameWindow_2_portrait_iPad__10th_generation__17_0@2x.png │ │ │ ├── testRotationInWindow_sameWindow_2_portrait_iPhone16_1_17_0@3x.png │ │ │ ├── testRotationInWindow_sameWindow_2_portrait_iPhone_SE__3rd_generation__17_0@2x.png │ │ │ ├── testRotationInWindow_sameWindow_3_landscapeLeft_iPad__10th_generation__17_0@2x.png │ │ │ ├── testRotationInWindow_sameWindow_3_landscapeLeft_iPhone16_1_17_0@3x.png │ │ │ ├── testRotationInWindow_sameWindow_3_landscapeLeft_iPhone_SE__3rd_generation__17_0@2x.png │ │ │ ├── testRotationInWindow_sameWindow_4_portrait_iPad__10th_generation__17_0@2x.png │ │ │ ├── testRotationInWindow_sameWindow_4_portrait_iPhone16_1_17_0@3x.png │ │ │ └── testRotationInWindow_sameWindow_4_portrait_iPhone_SE__3rd_generation__17_0@2x.png │ ├── Sources │ │ ├── Core │ │ │ ├── AccessibilityIdentifiers.swift │ │ │ ├── AppDelegate.swift │ │ │ ├── Custom Views │ │ │ │ ├── CustomCoachMarkArrowView.swift │ │ │ │ ├── CustomCoachMarkBodyView.swift │ │ │ │ ├── TransparentCoachMarkArrowView.swift │ │ │ │ └── TransparentCoachMarkBodyView.swift │ │ │ ├── Extensions.swift │ │ │ ├── SceneDelegate.swift │ │ │ ├── Storyboards │ │ │ │ ├── Base.lproj │ │ │ │ │ ├── Basic Examples.storyboard │ │ │ │ │ ├── Delegation.storyboard │ │ │ │ │ ├── LaunchScreen.storyboard │ │ │ │ │ ├── Main.storyboard │ │ │ │ │ └── PausingResuming.storyboard │ │ │ │ └── Development.storyboard │ │ │ └── View Controllers │ │ │ │ ├── Basic Examples │ │ │ │ ├── BlurringOverlayViewController.swift │ │ │ │ ├── CustomViewController.swift │ │ │ │ ├── DefaultViewController.swift │ │ │ │ ├── LargeCutoutViewController.swift │ │ │ │ ├── MixedCoachMarksViewController.swift │ │ │ │ ├── NextPositionViewController.swift │ │ │ │ └── OnlyHintsViewController.swift │ │ │ │ ├── Debug │ │ │ │ ├── BackgroundNetworkingViewController.swift │ │ │ │ └── TestFlowViewController.swift │ │ │ │ ├── Delegate Examples │ │ │ │ ├── ConfigurationViewController.swift │ │ │ │ ├── DelegateViewController.swift │ │ │ │ ├── KeyboardViewController.swift │ │ │ │ ├── PausingCodeViewController.swift │ │ │ │ └── TransitionFromCodeViewController.swift │ │ │ │ ├── InstructionsNavigationController.swift │ │ │ │ ├── MainTableViewController.swift │ │ │ │ └── ProfileViewController.swift │ │ └── Supporting Files │ │ │ ├── Assets.xcassets │ │ │ ├── AppIcon.appiconset │ │ │ │ ├── Contents.json │ │ │ │ ├── Example-20.png │ │ │ │ ├── Example-20@2x-1.png │ │ │ │ ├── Example-20@2x.png │ │ │ │ ├── Example-20@3x.png │ │ │ │ ├── Example-29.png │ │ │ │ ├── Example-29@2x-1.png │ │ │ │ ├── Example-29@2x.png │ │ │ │ ├── Example-29@3x.png │ │ │ │ ├── Example-40.png │ │ │ │ ├── Example-40@2x-1.png │ │ │ │ ├── Example-40@2x.png │ │ │ │ ├── Example-40@3x.png │ │ │ │ ├── Example-60@2x.png │ │ │ │ ├── Example-60@3x.png │ │ │ │ ├── Example-76.png │ │ │ │ ├── Example-76@2x.png │ │ │ │ ├── Example-83,5@2x.png │ │ │ │ └── ExampleAppStore-1024.png │ │ │ ├── Contents.json │ │ │ ├── arrow-bottom.imageset │ │ │ │ ├── Contents.json │ │ │ │ ├── arrow-bottom.png │ │ │ │ ├── arrow-bottom@2x.png │ │ │ │ └── arrow-bottom@3x.png │ │ │ ├── arrow-top.imageset │ │ │ │ ├── Contents.json │ │ │ │ ├── arrow-top.png │ │ │ │ ├── arrow-top@2x.png │ │ │ │ └── arrow-top@3x.png │ │ │ ├── button-background-highlighted.imageset │ │ │ │ ├── Contents.json │ │ │ │ ├── button-background-highlighted.png │ │ │ │ ├── button-background-highlighted@2x.png │ │ │ │ └── button-background-highlighted@3x.png │ │ │ ├── button-background.imageset │ │ │ │ ├── Contents.json │ │ │ │ ├── button-background.png │ │ │ │ ├── button-background@2x.png │ │ │ │ └── button-background@3x.png │ │ │ ├── coach-mark-bottom-plate.imageset │ │ │ │ ├── Contents.json │ │ │ │ ├── coach-mark-bottom-plate.png │ │ │ │ ├── coach-mark-bottom-plate@2x.png │ │ │ │ └── coach-mark-bottom-plate@3x.png │ │ │ ├── coach-mark-top-plate.imageset │ │ │ │ ├── Contents.json │ │ │ │ ├── coach-mark-top-plate.png │ │ │ │ ├── coach-mark-top-plate@2x.png │ │ │ │ └── coach-mark-top-plate@3x.png │ │ │ ├── face-female.imageset │ │ │ │ ├── Contents.json │ │ │ │ ├── face-female.png │ │ │ │ ├── face-female@2x.png │ │ │ │ └── face-female@3x.png │ │ │ └── face-male.imageset │ │ │ │ ├── Contents.json │ │ │ │ ├── face-male.png │ │ │ │ ├── face-male@2x.png │ │ │ │ └── face-male@3x.png │ │ │ ├── Info.plist │ │ │ ├── ar.lproj │ │ │ └── Localizable.strings │ │ │ └── en.lproj │ │ │ └── Localizable.strings │ ├── UI Tests │ │ ├── CustomExampleTests.swift │ │ ├── DefaultExampleTests.swift │ │ ├── Extensions │ │ │ └── XCUIApplication+Launch.swift │ │ ├── Supporting Files │ │ │ └── Info.plist │ │ └── TransitionExampleTests.swift │ └── Unit Tests │ │ ├── Core │ │ ├── Internal │ │ │ └── CoachMarksViewControllerTests.swift │ │ └── Public │ │ │ ├── CoachMarkTests.swift │ │ │ └── CoachMarksControllerTests.swift │ │ ├── Helpers │ │ ├── CoachMarkHelperTests.swift │ │ ├── CoachMarkInnerLayoutHelperTests.swift │ │ └── CoachMarkLayoutHelperTests.swift │ │ ├── Managers │ │ └── Internal │ │ │ ├── CoachMarkDisplayManagerTests.swift │ │ │ ├── OverlayManagerTests.swift │ │ │ ├── OverlayStyleManager │ │ │ ├── BlurringOverlayStyleManagerTest.swift │ │ │ └── TranslucentOverlayStyleManagerTest.swift │ │ │ └── SkipViewDisplayManagerTests.swift │ │ ├── Protocols │ │ └── Internal │ │ │ └── DataSource │ │ │ ├── DataSourceBaseTest.swift │ │ │ ├── DataSourceCoachMarkAtTest.swift │ │ │ └── DataSourceNumberOfCoachMarksTest.swift │ │ ├── Supporting Files │ │ └── Info.plist │ │ └── Views │ │ ├── MaskViewTests.swift │ │ ├── OverlaySnapshotViewTests.swift │ │ ├── OverlayViewTests.swift │ │ └── PassthroughGenericTests.swift ├── Instructions App Extensions Example.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── xcshareddata │ │ └── xcschemes │ │ └── Instructions App Extensions Example.xcscheme ├── Instructions Example.xcodeproj │ ├── project.pbxproj │ └── xcshareddata │ │ └── xcschemes │ │ └── Instructions Example.xcscheme ├── InstructionsExample.entitlements ├── Snapshot.xctestplan └── Unit+UI.xctestplan ├── Gemfile ├── Gemfile.lock ├── Instructions.podspec ├── Instructions.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── xcshareddata │ └── xcschemes │ ├── Instructions App Extensions.xcscheme │ └── Instructions.xcscheme ├── Instructions.xcworkspace ├── contents.xcworkspacedata └── xcshareddata │ ├── IDETemplateMacros.plist │ ├── IDEWorkspaceChecks.plist │ ├── WorkspaceSettings.xcsettings │ └── swiftpm │ └── Package.resolved ├── InstructionsAppExtensions.podspec ├── LICENSE ├── Package.swift ├── README.md ├── Sources └── Instructions │ ├── Core │ ├── Internal │ │ └── CoachMarksViewController.swift │ └── Public │ │ ├── CoachMark.swift │ │ ├── CoachMarksController.swift │ │ └── Extensions │ │ └── CoachMarksController+Proxy.swift │ ├── Extra │ ├── Default Views │ │ ├── CoachMarkArrowDefaultView.swift │ │ ├── CoachMarkBackgroundStyle.swift │ │ ├── CoachMarkBodyDefaultView.swift │ │ └── CoachMarkSkipDefaultView.swift │ └── Internal │ │ ├── CoachMarkBodyBackgroundView.swift │ │ └── CoachMarkComponent.swift │ ├── Helpers │ ├── Internal │ │ ├── AccessibilityIdentifiers.swift │ │ ├── CoachMarkInnerLayoutHelper.swift │ │ ├── CoachMarkLayoutHelper.swift │ │ ├── Enums │ │ │ └── CoachMarkPosition.swift │ │ ├── ErrorMessage.swift │ │ └── Extensions │ │ │ ├── UIApplication+WindowScene.swift │ │ │ └── UIView+Layout.swift │ └── Public │ │ ├── CoachMark Animation │ │ ├── CoachMarkAnimationManagement.swift │ │ ├── CoachMarkAnimationManager.swift │ │ └── CoachMarkTransitionManager.swift │ │ ├── CoachMarkCoordinateConverter.swift │ │ ├── CoachMarkHelper.swift │ │ └── Enums │ │ ├── CoachMarkArrowOrientation.swift │ │ └── CoachMarkNextLabelPosition.swift │ ├── Instructions.swift │ ├── Managers │ ├── Internal │ │ ├── CoachMarkDisplayManager.swift │ │ ├── OverlayStyleManager │ │ │ ├── BlurringOverlayStyleManager.swift │ │ │ ├── OverlayStyleManager.swift │ │ │ └── TranslucentOverlayStyleManager.swift │ │ └── SkipViewDisplayManager.swift │ └── Public │ │ ├── FlowManager.swift │ │ └── OverlayManager.swift │ ├── Protocols and Enums │ ├── Internal │ │ └── CoachMarkViewControllerDelegate.swift │ └── Public │ │ ├── CoachMarkArrowView.swift │ │ ├── CoachMarkBodyHighlightArrowDelegate.swift │ │ ├── CoachMarkBodyView.swift │ │ ├── CoachMarkSkipView.swift │ │ ├── CoachMarksControllerDataSource.swift │ │ ├── Delegates │ │ ├── CoachMarksControllerAnimationDelegate.swift │ │ └── CoachMarksControllerDelegate.swift │ │ └── Enums │ │ ├── ConfigurationChange.swift │ │ ├── PauseStyle.swift │ │ ├── PresentationContext.swift │ │ └── ViewControllerConfiguration.swift │ ├── Supporting Files │ ├── Info.plist │ ├── InfoAppExtensions.plist │ └── Instructions.h │ └── Views │ ├── CoachMarkView.swift │ ├── MaskView.swift │ ├── OverlaySnapshotView.swift │ ├── OverlayView.swift │ └── PassthroughViews.swift └── fastlane ├── Appfile ├── Fastfile └── README.md /.codeclimate.yml: -------------------------------------------------------------------------------- 1 | exclude_patterns: 2 | - Examples/ -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | --- 5 | 6 | ## Describe the bug 7 | 8 | 9 | ## To Reproduce 10 | 11 | 12 | ## Expected behavior 13 | 14 | 15 | ## Screenshots 16 | 17 | 18 | ## Environment 19 | - Device: 20 | - iOS version: 21 | - Instructions version: 22 | - Dependency Manager: 23 | 24 | ## Additional context 25 | 26 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | --- 5 | 6 | ## Is your feature request related to a problem? Please describe. 7 | A clear and concise description of what the problem is. (e. g. I'm always frustrated when […]) 8 | 9 | ## Describe the solution you'd like 10 | A clear and concise description of what you want to happen. 11 | 12 | ## Describe alternatives you've considered 13 | A clear and concise description of any alternative solutions or features you've considered. 14 | 15 | ## Additional context 16 | Add any other context or screenshots about the feature request here. 17 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE/simple_pull_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Pull Request 3 | about: Contribute to Instructions 4 | --- 5 | 6 | ## Checklist 7 | - [ ] I have read the [guidelines for contributing](../CONTRIBUTING.md). 8 | - [ ] I have updated README.md, describing the new feature I'm adding (if applicable). 9 | - [ ] I have checked that my code additions did fail neither code linting checks nor unit/UI test. 10 | 11 | ## Description 12 | 13 | -------------------------------------------------------------------------------- /.ruby-version: -------------------------------------------------------------------------------- 1 | 3.1.2 2 | -------------------------------------------------------------------------------- /.slather.yml: -------------------------------------------------------------------------------- 1 | coverage_service: html 2 | xcodeproj: Instructions.xcodeproj 3 | workspace: Instructions.xcworkspace 4 | scheme: Instructions 5 | source_directory: Sources 6 | output_directory: Tests 7 | ignore: 8 | - Examples/* -------------------------------------------------------------------------------- /.swiftlint.yml: -------------------------------------------------------------------------------- 1 | disabled_rules: 2 | - todo 3 | - identifier_name 4 | 5 | included: 6 | - Sources 7 | - Examples 8 | 9 | excluded: 10 | - Examples/Carthage 11 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Instructions was originally created in 2015 by 2 | Frédéric Maquin 3 | 4 | The list of MUCH-APPRECIATED CONTRIBUTORS can be found at 5 | 6 | https://github.com/ephread/Instructions/graphs/contributors 7 | -------------------------------------------------------------------------------- /Documentation/migrating_to_2.0.0.md: -------------------------------------------------------------------------------- 1 | # Migrating from Instructions 1.4.0 to 2.0.0 2 | 3 | Instructions 2.0.0 brings a few breaking changes that are listed below. 4 | 5 | ## `CoachMarksControllerDataSource` 6 | 7 | **1.4.0** 8 | 9 | ```swift 10 | func coachMarksController( 11 | _ coachMarksController: CoachMarksController, 12 | coachMarkViewsAt index: Int, 13 | madeFrom coachMark: CoachMark 14 | ) -> (bodyView: CoachMarkBodyView, arrowView: CoachMarkArrowView?) 15 | ``` 16 | **2.0.0** 17 | 18 | ```swift 19 | func coachMarksController( 20 | _ coachMarksController: CoachMarksController, 21 | coachMarkViewsAt index: Int, 22 | madeFrom coachMark: CoachMark 23 | ) -> (bodyView: UIView & CoachMarkBodyView, arrowView: (UIView & CoachMarkArrowView)?) 24 | ``` 25 | 26 | ## `CoachMark` 27 | 28 | **1.4.0** 29 | 30 | ```swift 31 | public var displayOverCutoutPath: Bool = false 32 | public var disableOverlayTap: Bool = true 33 | public var allowTouchInsideCutoutPath: Bool = false 34 | ``` 35 | **2.0.0** 36 | 37 | ```swift 38 | public var isDisplayedOverCutoutPath: Bool = false 39 | public var isOverlayInteractionEnabled: Bool = true 40 | public var isUserInteractionEnabledInsideCutoutPath: Bool = false 41 | ``` 42 | ⚠️ migrating `disableOverlayTap` to `isOverlayInteractionEnabled` requires inverting its boolean value. 43 | 44 | ## `OverlayManager` (`CoachMarkController.overlay`) 45 | 46 | **1.4.0** 47 | 48 | ```swift 49 | public var color: UIColor 50 | public var allowTap: Bool 51 | public var allowTouchInsideCutoutPath: Bool 52 | public var forwardTouchEvents: Bool 53 | ``` 54 | **2.0.0** 55 | 56 | ```swift 57 | public var backgroundColor: UIColor 58 | public var isUserInteractionEnabled: Bool 59 | public var isUserInteractionEnabledInsideCutoutPath: Bool 60 | public var areTouchEventsForwarded: Bool 61 | ``` 62 | 63 | ## `FlowManager` (`CoachMarkController.flow`) 64 | 65 | **1.4.0** 66 | 67 | ```swift 68 | public var started: Bool 69 | public var paused: Bool 70 | ``` 71 | **2.0.0** 72 | 73 | ```swift 74 | public var isStarted: Bool 75 | public var isPaused: Bool 76 | ``` 77 | -------------------------------------------------------------------------------- /Examples/App Extensions Example/Sources/Core/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016-present Frédéric Maquin and contributors. 2 | // Licensed under the terms of the MIT License. 3 | 4 | import UIKit 5 | 6 | @UIApplicationMain 7 | class AppDelegate: UIResponder, UIApplicationDelegate { 8 | 9 | var window: UIWindow? 10 | 11 | func application( 12 | _ application: UIApplication, 13 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 14 | ) -> Bool { 15 | return true 16 | } 17 | 18 | func applicationWillResignActive(_ application: UIApplication) { } 19 | func applicationDidEnterBackground(_ application: UIApplication) { } 20 | func applicationWillEnterForeground(_ application: UIApplication) { } 21 | func applicationDidBecomeActive(_ application: UIApplication) { } 22 | func applicationWillTerminate(_ application: UIApplication) { } 23 | } 24 | -------------------------------------------------------------------------------- /Examples/App Extensions Example/Sources/Core/Storyboards/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /Examples/App Extensions Example/Sources/Core/View Controllers/ViewController.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016-present Frédéric Maquin and contributors. 2 | // Licensed under the terms of the MIT License. 3 | 4 | import UIKit 5 | import Instructions 6 | 7 | class ViewController: UIViewController, CoachMarksControllerDataSource { 8 | 9 | @IBOutlet var rectangleView: UIView? 10 | 11 | let coachMarksController = CoachMarksController() 12 | 13 | override func viewDidLoad() { 14 | super.viewDidLoad() 15 | 16 | self.coachMarksController.dataSource = self 17 | 18 | let skipView = CoachMarkSkipDefaultView() 19 | skipView.setTitle("Skip", for: .normal) 20 | 21 | self.coachMarksController.skipView = skipView 22 | } 23 | 24 | override func viewDidAppear(_ animated: Bool) { 25 | super.viewDidAppear(animated) 26 | 27 | self.coachMarksController.start(in: .newWindow(over: self, 28 | at: UIWindow.Level.statusBar + 1)) 29 | } 30 | 31 | override func didReceiveMemoryWarning() { 32 | super.didReceiveMemoryWarning() 33 | // Dispose of any resources that can be recreated. 34 | } 35 | 36 | func numberOfCoachMarks(for coachMarkController: CoachMarksController) 37 | -> Int { 38 | return 1 39 | } 40 | 41 | func coachMarksController(_ coachMarksController: CoachMarksController, coachMarkAt: Int) 42 | -> CoachMark { 43 | return coachMarksController.helper.makeCoachMark(for: rectangleView) 44 | } 45 | 46 | func coachMarksController( 47 | _ coachMarksController: CoachMarksController, 48 | coachMarkViewsAt: Int, 49 | madeFrom coachMark: CoachMark 50 | ) -> (bodyView: (UIView & CoachMarkBodyView), arrowView: (UIView & CoachMarkArrowView)?) { 51 | let coachViews = coachMarksController.helper.makeDefaultCoachViews( 52 | withArrow: true, 53 | arrowOrientation: coachMark.arrowOrientation 54 | ) 55 | 56 | coachViews.bodyView.hintLabel.text = "Hello! I'm a Coach Mark!" 57 | coachViews.bodyView.nextLabel.text = "Ok!" 58 | 59 | return (bodyView: coachViews.bodyView, arrowView: coachViews.arrowView) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Examples/App Extensions Example/Sources/KeyboardExtension/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | KeyboardExtension 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | XPC! 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | NSExtension 26 | 27 | NSExtensionAttributes 28 | 29 | IsASCIICapable 30 | 31 | PrefersRightToLeft 32 | 33 | PrimaryLanguage 34 | en-US 35 | RequestsOpenAccess 36 | 37 | 38 | NSExtensionPointIdentifier 39 | com.apple.keyboard-service 40 | NSExtensionPrincipalClass 41 | $(PRODUCT_MODULE_NAME).KeyboardViewController 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /Examples/App Extensions Example/Sources/Supporting Files/Assets.xcassets/AppIcon.appiconset/AppEx-1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/App Extensions Example/Sources/Supporting Files/Assets.xcassets/AppIcon.appiconset/AppEx-1024.png -------------------------------------------------------------------------------- /Examples/App Extensions Example/Sources/Supporting Files/Assets.xcassets/AppIcon.appiconset/Appex-20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/App Extensions Example/Sources/Supporting Files/Assets.xcassets/AppIcon.appiconset/Appex-20.png -------------------------------------------------------------------------------- /Examples/App Extensions Example/Sources/Supporting Files/Assets.xcassets/AppIcon.appiconset/Appex-20@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/App Extensions Example/Sources/Supporting Files/Assets.xcassets/AppIcon.appiconset/Appex-20@2x-1.png -------------------------------------------------------------------------------- /Examples/App Extensions Example/Sources/Supporting Files/Assets.xcassets/AppIcon.appiconset/Appex-20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/App Extensions Example/Sources/Supporting Files/Assets.xcassets/AppIcon.appiconset/Appex-20@2x.png -------------------------------------------------------------------------------- /Examples/App Extensions Example/Sources/Supporting Files/Assets.xcassets/AppIcon.appiconset/Appex-20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/App Extensions Example/Sources/Supporting Files/Assets.xcassets/AppIcon.appiconset/Appex-20@3x.png -------------------------------------------------------------------------------- /Examples/App Extensions Example/Sources/Supporting Files/Assets.xcassets/AppIcon.appiconset/Appex-29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/App Extensions Example/Sources/Supporting Files/Assets.xcassets/AppIcon.appiconset/Appex-29.png -------------------------------------------------------------------------------- /Examples/App Extensions Example/Sources/Supporting Files/Assets.xcassets/AppIcon.appiconset/Appex-29@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/App Extensions Example/Sources/Supporting Files/Assets.xcassets/AppIcon.appiconset/Appex-29@2x-1.png -------------------------------------------------------------------------------- /Examples/App Extensions Example/Sources/Supporting Files/Assets.xcassets/AppIcon.appiconset/Appex-29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/App Extensions Example/Sources/Supporting Files/Assets.xcassets/AppIcon.appiconset/Appex-29@2x.png -------------------------------------------------------------------------------- /Examples/App Extensions Example/Sources/Supporting Files/Assets.xcassets/AppIcon.appiconset/Appex-29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/App Extensions Example/Sources/Supporting Files/Assets.xcassets/AppIcon.appiconset/Appex-29@3x.png -------------------------------------------------------------------------------- /Examples/App Extensions Example/Sources/Supporting Files/Assets.xcassets/AppIcon.appiconset/Appex-40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/App Extensions Example/Sources/Supporting Files/Assets.xcassets/AppIcon.appiconset/Appex-40.png -------------------------------------------------------------------------------- /Examples/App Extensions Example/Sources/Supporting Files/Assets.xcassets/AppIcon.appiconset/Appex-40@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/App Extensions Example/Sources/Supporting Files/Assets.xcassets/AppIcon.appiconset/Appex-40@2x-1.png -------------------------------------------------------------------------------- /Examples/App Extensions Example/Sources/Supporting Files/Assets.xcassets/AppIcon.appiconset/Appex-40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/App Extensions Example/Sources/Supporting Files/Assets.xcassets/AppIcon.appiconset/Appex-40@2x.png -------------------------------------------------------------------------------- /Examples/App Extensions Example/Sources/Supporting Files/Assets.xcassets/AppIcon.appiconset/Appex-40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/App Extensions Example/Sources/Supporting Files/Assets.xcassets/AppIcon.appiconset/Appex-40@3x.png -------------------------------------------------------------------------------- /Examples/App Extensions Example/Sources/Supporting Files/Assets.xcassets/AppIcon.appiconset/Appex-60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/App Extensions Example/Sources/Supporting Files/Assets.xcassets/AppIcon.appiconset/Appex-60@2x.png -------------------------------------------------------------------------------- /Examples/App Extensions Example/Sources/Supporting Files/Assets.xcassets/AppIcon.appiconset/Appex-60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/App Extensions Example/Sources/Supporting Files/Assets.xcassets/AppIcon.appiconset/Appex-60@3x.png -------------------------------------------------------------------------------- /Examples/App Extensions Example/Sources/Supporting Files/Assets.xcassets/AppIcon.appiconset/Appex-76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/App Extensions Example/Sources/Supporting Files/Assets.xcassets/AppIcon.appiconset/Appex-76.png -------------------------------------------------------------------------------- /Examples/App Extensions Example/Sources/Supporting Files/Assets.xcassets/AppIcon.appiconset/Appex-76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/App Extensions Example/Sources/Supporting Files/Assets.xcassets/AppIcon.appiconset/Appex-76@2x.png -------------------------------------------------------------------------------- /Examples/App Extensions Example/Sources/Supporting Files/Assets.xcassets/AppIcon.appiconset/Appex-83,5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/App Extensions Example/Sources/Supporting Files/Assets.xcassets/AppIcon.appiconset/Appex-83,5@2x.png -------------------------------------------------------------------------------- /Examples/App Extensions Example/Sources/Supporting Files/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Appex-20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Appex-20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Appex-29@2x.png", 19 | "scale" : "2x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Appex-29@3x.png", 25 | "scale" : "3x" 26 | }, 27 | { 28 | "size" : "40x40", 29 | "idiom" : "iphone", 30 | "filename" : "Appex-40@2x.png", 31 | "scale" : "2x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Appex-40@3x.png", 37 | "scale" : "3x" 38 | }, 39 | { 40 | "size" : "60x60", 41 | "idiom" : "iphone", 42 | "filename" : "Appex-60@2x.png", 43 | "scale" : "2x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Appex-60@3x.png", 49 | "scale" : "3x" 50 | }, 51 | { 52 | "size" : "20x20", 53 | "idiom" : "ipad", 54 | "filename" : "Appex-20.png", 55 | "scale" : "1x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Appex-20@2x-1.png", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "size" : "29x29", 65 | "idiom" : "ipad", 66 | "filename" : "Appex-29.png", 67 | "scale" : "1x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Appex-29@2x-1.png", 73 | "scale" : "2x" 74 | }, 75 | { 76 | "size" : "40x40", 77 | "idiom" : "ipad", 78 | "filename" : "Appex-40.png", 79 | "scale" : "1x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Appex-40@2x-1.png", 85 | "scale" : "2x" 86 | }, 87 | { 88 | "size" : "76x76", 89 | "idiom" : "ipad", 90 | "filename" : "Appex-76.png", 91 | "scale" : "1x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Appex-76@2x.png", 97 | "scale" : "2x" 98 | }, 99 | { 100 | "size" : "83.5x83.5", 101 | "idiom" : "ipad", 102 | "filename" : "Appex-83,5@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "1024x1024", 107 | "idiom" : "ios-marketing", 108 | "filename" : "AppEx-1024.png", 109 | "scale" : "1x" 110 | } 111 | ], 112 | "info" : { 113 | "version" : 1, 114 | "author" : "xcode" 115 | } 116 | } -------------------------------------------------------------------------------- /Examples/App Extensions Example/Sources/Supporting Files/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | AppEx 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | LSRequiresIPhoneOS 26 | 27 | UILaunchStoryboardName 28 | LaunchScreen 29 | UIMainStoryboardFile 30 | Main 31 | UIRequiredDeviceCapabilities 32 | 33 | armv7 34 | 35 | UISupportedInterfaceOrientations 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationLandscapeLeft 39 | UIInterfaceOrientationLandscapeRight 40 | 41 | UISupportedInterfaceOrientations~ipad 42 | 43 | UIInterfaceOrientationPortrait 44 | UIInterfaceOrientationPortraitUpsideDown 45 | UIInterfaceOrientationLandscapeLeft 46 | UIInterfaceOrientationLandscapeRight 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Extensions/DeviceKit+Extensions.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021-present Frédéric Maquin and contributors. 2 | // Licensed under the terms of the MIT License. 3 | 4 | import UIKit 5 | import DeviceKit 6 | 7 | extension Device { 8 | private var xSeries1stGenOled: [Device] { 9 | [.iPhoneX, .iPhoneXS, .iPhoneXSMax, .iPhone11Pro, .iPhone11ProMax] 10 | } 11 | 12 | private var xSeries1stGenLcd: [Device] { 13 | [.iPhoneXR, .iPhone11] 14 | } 15 | 16 | private var xSeries2ndGenMini: [Device] { 17 | [.iPhone12Mini, .iPhone13Mini] 18 | } 19 | 20 | private var xSeries2ndGen: [Device] { 21 | [.iPhone12, .iPhone12Pro, .iPhone12ProMax, 22 | .iPhone13, .iPhone13Pro, .iPhone13ProMax, 23 | .iPhone14, .iPhone14Plus] 24 | } 25 | 26 | private var xSeries3ndGen: [Device] { 27 | [.iPhone14Pro, .iPhone14ProMax] 28 | } 29 | 30 | func homeIndicatorHeight(for orientation: UIDeviceOrientation) -> CGFloat { 31 | guard hasRoundedDisplayCorners else { return 0 } 32 | 33 | guard !isPad else { return 20 } 34 | 35 | if orientation.isLandscape { 36 | return 21 37 | } 38 | 39 | if orientation.isPortrait { 40 | return 34 41 | } 42 | 43 | return 0 44 | } 45 | 46 | func statusBarHeight(for orientation: UIDeviceOrientation) -> CGFloat { 47 | guard hasRoundedDisplayCorners else { 48 | if !isPad && orientation.isLandscape { 49 | return 0 50 | } 51 | 52 | return 20 53 | } 54 | 55 | guard !isPad else { return 24 } 56 | guard orientation.isPortrait else { return 0 } 57 | 58 | if (xSeries1stGenOled + xSeries1stGenOled.map(Device.simulator)).contains(self) { 59 | return 44 60 | } 61 | 62 | if (xSeries1stGenLcd + xSeries1stGenLcd.map(Device.simulator)).contains(self) { 63 | return 48 64 | } 65 | 66 | if (xSeries2ndGenMini + xSeries2ndGenMini.map(Device.simulator)).contains(self) { 67 | return 50 68 | } 69 | 70 | if (xSeries2ndGen + xSeries2ndGen.map(Device.simulator)).contains(self) { 71 | return 47 72 | } 73 | 74 | if (xSeries3ndGen + xSeries3ndGen.map(Device.simulator)).contains(self) { 75 | return 59 76 | } 77 | 78 | return 0 79 | } 80 | } 81 | 82 | extension Device { 83 | var snapshotDescription: String { 84 | let name: String 85 | if case .simulator(let device) = Device.current { 86 | name = device.safeDescription 87 | } else { 88 | name = Device.current.safeDescription 89 | } 90 | 91 | let deviceVersion = Device.current.systemVersion?.replacingOccurrences(of: ".", with: "_") 92 | 93 | return [name, deviceVersion].compactMap { $0 }.joined(separator: "_") 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Extensions/UIDeviceOrientation+Convenience.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021-present Frédéric Maquin and contributors. 2 | // Licensed under the terms of the MIT License. 3 | 4 | import UIKit 5 | 6 | extension UIDeviceOrientation { 7 | var isLandscape: Bool { [.landscapeLeft, .landscapeRight].contains(self) } 8 | var isPortrait: Bool { [.portrait, .portraitUpsideDown, .unknown].contains(self) } 9 | } 10 | -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Extensions/UIImage+Extensions.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021-present Frédéric Maquin and contributors. 2 | // Licensed under the terms of the MIT License. 3 | 4 | import UIKit 5 | import DeviceKit 6 | 7 | public extension UIImage { 8 | var dimensions: String { "\(Int(size.width))x\(Int(size.height))" } 9 | 10 | func cropped(using orientation: UIDeviceOrientation) -> UIImage? { 11 | let scale = imageRendererFormat.scale 12 | 13 | let statusBarHeight = Device.current.statusBarHeight(for: orientation) * scale 14 | let homeIndicatorHeight = Device.current.homeIndicatorHeight(for: orientation) * scale 15 | 16 | guard let rawImage = cgImage else { return nil } 17 | 18 | let rect: CGRect 19 | 20 | switch orientation { 21 | case .portrait: 22 | rect = CGRect( 23 | x: 0, 24 | y: Int(statusBarHeight), 25 | width: rawImage.width, 26 | height: rawImage.height - Int(homeIndicatorHeight) - Int(statusBarHeight) 27 | ) 28 | case .portraitUpsideDown: 29 | rect = CGRect( 30 | x: 0, 31 | y: Int(homeIndicatorHeight), 32 | width: rawImage.width, 33 | height: rawImage.height - Int(statusBarHeight) - Int(homeIndicatorHeight) 34 | ) 35 | case .landscapeLeft: 36 | rect = CGRect( 37 | x: Int(homeIndicatorHeight), 38 | y: 0, 39 | width: rawImage.width - Int(statusBarHeight) - Int(homeIndicatorHeight), 40 | height: rawImage.height 41 | ) 42 | case .landscapeRight: 43 | rect = CGRect( 44 | x: Int(statusBarHeight), 45 | y: 0, 46 | width: rawImage.width - Int(homeIndicatorHeight) - Int(statusBarHeight), 47 | height: rawImage.height 48 | ) 49 | default: 50 | return nil 51 | } 52 | 53 | guard let croppedRawImage = rawImage.cropping(to: rect) else { return nil } 54 | 55 | return UIImage( 56 | cgImage: croppedRawImage, 57 | scale: imageRendererFormat.scale, 58 | orientation: imageOrientation 59 | ) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Extensions/XCUIApplication+Launch.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present Frédéric Maquin and contributors. 2 | // Licensed under the terms of the MIT License. 3 | 4 | import XCTest 5 | 6 | extension XCUIApplication { 7 | func launchWithoutStatusBar() { 8 | launchArguments.append("--SnapshotTests") 9 | launch() 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Supporting Files/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 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 | -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testFlowInController_controller_0_portrait_iPad__10th_generation__17_0@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testFlowInController_controller_0_portrait_iPad__10th_generation__17_0@2x.png -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testFlowInController_controller_0_portrait_iPhone16_1_17_0@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testFlowInController_controller_0_portrait_iPhone16_1_17_0@3x.png -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testFlowInController_controller_0_portrait_iPhone_SE__3rd_generation__17_0@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testFlowInController_controller_0_portrait_iPhone_SE__3rd_generation__17_0@2x.png -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testFlowInController_controller_1_portrait_iPad__10th_generation__17_0@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testFlowInController_controller_1_portrait_iPad__10th_generation__17_0@2x.png -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testFlowInController_controller_1_portrait_iPhone16_1_17_0@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testFlowInController_controller_1_portrait_iPhone16_1_17_0@3x.png -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testFlowInController_controller_1_portrait_iPhone_SE__3rd_generation__17_0@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testFlowInController_controller_1_portrait_iPhone_SE__3rd_generation__17_0@2x.png -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testFlowInController_controller_2_portrait_iPad__10th_generation__17_0@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testFlowInController_controller_2_portrait_iPad__10th_generation__17_0@2x.png -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testFlowInController_controller_2_portrait_iPhone16_1_17_0@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testFlowInController_controller_2_portrait_iPhone16_1_17_0@3x.png -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testFlowInController_controller_2_portrait_iPhone_SE__3rd_generation__17_0@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testFlowInController_controller_2_portrait_iPhone_SE__3rd_generation__17_0@2x.png -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testFlowInController_controller_3_portrait_iPad__10th_generation__17_0@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testFlowInController_controller_3_portrait_iPad__10th_generation__17_0@2x.png -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testFlowInController_controller_3_portrait_iPhone16_1_17_0@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testFlowInController_controller_3_portrait_iPhone16_1_17_0@3x.png -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testFlowInController_controller_3_portrait_iPhone_SE__3rd_generation__17_0@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testFlowInController_controller_3_portrait_iPhone_SE__3rd_generation__17_0@2x.png -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testFlowInController_controller_4_portrait_iPad__10th_generation__17_0@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testFlowInController_controller_4_portrait_iPad__10th_generation__17_0@2x.png -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testFlowInController_controller_4_portrait_iPhone16_1_17_0@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testFlowInController_controller_4_portrait_iPhone16_1_17_0@3x.png -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testFlowInController_controller_4_portrait_iPhone_SE__3rd_generation__17_0@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testFlowInController_controller_4_portrait_iPhone_SE__3rd_generation__17_0@2x.png -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testFlowInIndependentWindow_independentWindow_0_portrait_iPad__10th_generation__17_0@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testFlowInIndependentWindow_independentWindow_0_portrait_iPad__10th_generation__17_0@2x.png -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testFlowInIndependentWindow_independentWindow_0_portrait_iPhone16_1_17_0@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testFlowInIndependentWindow_independentWindow_0_portrait_iPhone16_1_17_0@3x.png -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testFlowInIndependentWindow_independentWindow_0_portrait_iPhone_SE__3rd_generation__17_0@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testFlowInIndependentWindow_independentWindow_0_portrait_iPhone_SE__3rd_generation__17_0@2x.png -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testFlowInIndependentWindow_independentWindow_1_portrait_iPad__10th_generation__17_0@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testFlowInIndependentWindow_independentWindow_1_portrait_iPad__10th_generation__17_0@2x.png -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testFlowInIndependentWindow_independentWindow_1_portrait_iPhone16_1_17_0@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testFlowInIndependentWindow_independentWindow_1_portrait_iPhone16_1_17_0@3x.png -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testFlowInIndependentWindow_independentWindow_1_portrait_iPhone_SE__3rd_generation__17_0@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testFlowInIndependentWindow_independentWindow_1_portrait_iPhone_SE__3rd_generation__17_0@2x.png -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testFlowInIndependentWindow_independentWindow_2_portrait_iPad__10th_generation__17_0@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testFlowInIndependentWindow_independentWindow_2_portrait_iPad__10th_generation__17_0@2x.png -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testFlowInIndependentWindow_independentWindow_2_portrait_iPhone16_1_17_0@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testFlowInIndependentWindow_independentWindow_2_portrait_iPhone16_1_17_0@3x.png -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testFlowInIndependentWindow_independentWindow_2_portrait_iPhone_SE__3rd_generation__17_0@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testFlowInIndependentWindow_independentWindow_2_portrait_iPhone_SE__3rd_generation__17_0@2x.png -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testFlowInIndependentWindow_independentWindow_3_portrait_iPad__10th_generation__17_0@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testFlowInIndependentWindow_independentWindow_3_portrait_iPad__10th_generation__17_0@2x.png -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testFlowInIndependentWindow_independentWindow_3_portrait_iPhone16_1_17_0@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testFlowInIndependentWindow_independentWindow_3_portrait_iPhone16_1_17_0@3x.png -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testFlowInIndependentWindow_independentWindow_3_portrait_iPhone_SE__3rd_generation__17_0@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testFlowInIndependentWindow_independentWindow_3_portrait_iPhone_SE__3rd_generation__17_0@2x.png -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testFlowInIndependentWindow_independentWindow_4_portrait_iPad__10th_generation__17_0@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testFlowInIndependentWindow_independentWindow_4_portrait_iPad__10th_generation__17_0@2x.png -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testFlowInIndependentWindow_independentWindow_4_portrait_iPhone16_1_17_0@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testFlowInIndependentWindow_independentWindow_4_portrait_iPhone16_1_17_0@3x.png -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testFlowInIndependentWindow_independentWindow_4_portrait_iPhone_SE__3rd_generation__17_0@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testFlowInIndependentWindow_independentWindow_4_portrait_iPhone_SE__3rd_generation__17_0@2x.png -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testFlowInWindow_sameWindow_0_portrait_iPad__10th_generation__17_0@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testFlowInWindow_sameWindow_0_portrait_iPad__10th_generation__17_0@2x.png -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testFlowInWindow_sameWindow_0_portrait_iPhone16_1_17_0@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testFlowInWindow_sameWindow_0_portrait_iPhone16_1_17_0@3x.png -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testFlowInWindow_sameWindow_0_portrait_iPhone_SE__3rd_generation__17_0@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testFlowInWindow_sameWindow_0_portrait_iPhone_SE__3rd_generation__17_0@2x.png -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testFlowInWindow_sameWindow_1_portrait_iPad__10th_generation__17_0@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testFlowInWindow_sameWindow_1_portrait_iPad__10th_generation__17_0@2x.png -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testFlowInWindow_sameWindow_1_portrait_iPhone16_1_17_0@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testFlowInWindow_sameWindow_1_portrait_iPhone16_1_17_0@3x.png -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testFlowInWindow_sameWindow_1_portrait_iPhone_SE__3rd_generation__17_0@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testFlowInWindow_sameWindow_1_portrait_iPhone_SE__3rd_generation__17_0@2x.png -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testFlowInWindow_sameWindow_2_portrait_iPad__10th_generation__17_0@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testFlowInWindow_sameWindow_2_portrait_iPad__10th_generation__17_0@2x.png -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testFlowInWindow_sameWindow_2_portrait_iPhone16_1_17_0@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testFlowInWindow_sameWindow_2_portrait_iPhone16_1_17_0@3x.png -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testFlowInWindow_sameWindow_2_portrait_iPhone_SE__3rd_generation__17_0@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testFlowInWindow_sameWindow_2_portrait_iPhone_SE__3rd_generation__17_0@2x.png -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testFlowInWindow_sameWindow_3_portrait_iPad__10th_generation__17_0@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testFlowInWindow_sameWindow_3_portrait_iPad__10th_generation__17_0@2x.png -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testFlowInWindow_sameWindow_3_portrait_iPhone16_1_17_0@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testFlowInWindow_sameWindow_3_portrait_iPhone16_1_17_0@3x.png -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testFlowInWindow_sameWindow_3_portrait_iPhone_SE__3rd_generation__17_0@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testFlowInWindow_sameWindow_3_portrait_iPhone_SE__3rd_generation__17_0@2x.png -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testFlowInWindow_sameWindow_4_portrait_iPad__10th_generation__17_0@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testFlowInWindow_sameWindow_4_portrait_iPad__10th_generation__17_0@2x.png -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testFlowInWindow_sameWindow_4_portrait_iPhone16_1_17_0@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testFlowInWindow_sameWindow_4_portrait_iPhone16_1_17_0@3x.png -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testFlowInWindow_sameWindow_4_portrait_iPhone_SE__3rd_generation__17_0@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testFlowInWindow_sameWindow_4_portrait_iPhone_SE__3rd_generation__17_0@2x.png -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testRotationInController_controller_0_portrait_iPad__10th_generation__17_0@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testRotationInController_controller_0_portrait_iPad__10th_generation__17_0@2x.png -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testRotationInController_controller_0_portrait_iPhone16_1_17_0@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testRotationInController_controller_0_portrait_iPhone16_1_17_0@3x.png -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testRotationInController_controller_0_portrait_iPhone_SE__3rd_generation__17_0@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testRotationInController_controller_0_portrait_iPhone_SE__3rd_generation__17_0@2x.png -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testRotationInController_controller_1_landscapeLeft_iPad__10th_generation__17_0@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testRotationInController_controller_1_landscapeLeft_iPad__10th_generation__17_0@2x.png -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testRotationInController_controller_1_landscapeLeft_iPhone16_1_17_0@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testRotationInController_controller_1_landscapeLeft_iPhone16_1_17_0@3x.png -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testRotationInController_controller_1_landscapeLeft_iPhone_SE__3rd_generation__17_0@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testRotationInController_controller_1_landscapeLeft_iPhone_SE__3rd_generation__17_0@2x.png -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testRotationInController_controller_2_portrait_iPad__10th_generation__17_0@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testRotationInController_controller_2_portrait_iPad__10th_generation__17_0@2x.png -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testRotationInController_controller_2_portrait_iPhone16_1_17_0@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testRotationInController_controller_2_portrait_iPhone16_1_17_0@3x.png -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testRotationInController_controller_2_portrait_iPhone_SE__3rd_generation__17_0@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testRotationInController_controller_2_portrait_iPhone_SE__3rd_generation__17_0@2x.png -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testRotationInController_controller_3_landscapeLeft_iPad__10th_generation__17_0@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testRotationInController_controller_3_landscapeLeft_iPad__10th_generation__17_0@2x.png -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testRotationInController_controller_3_landscapeLeft_iPhone16_1_17_0@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testRotationInController_controller_3_landscapeLeft_iPhone16_1_17_0@3x.png -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testRotationInController_controller_3_landscapeLeft_iPhone_SE__3rd_generation__17_0@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testRotationInController_controller_3_landscapeLeft_iPhone_SE__3rd_generation__17_0@2x.png -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testRotationInController_controller_4_portrait_iPad__10th_generation__17_0@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testRotationInController_controller_4_portrait_iPad__10th_generation__17_0@2x.png -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testRotationInController_controller_4_portrait_iPhone16_1_17_0@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testRotationInController_controller_4_portrait_iPhone16_1_17_0@3x.png -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testRotationInController_controller_4_portrait_iPhone_SE__3rd_generation__17_0@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testRotationInController_controller_4_portrait_iPhone_SE__3rd_generation__17_0@2x.png -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testRotationInIndependentWindow_independentWindow_0_portrait_iPad__10th_generation__17_0@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testRotationInIndependentWindow_independentWindow_0_portrait_iPad__10th_generation__17_0@2x.png -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testRotationInIndependentWindow_independentWindow_0_portrait_iPhone16_1_17_0@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testRotationInIndependentWindow_independentWindow_0_portrait_iPhone16_1_17_0@3x.png -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testRotationInIndependentWindow_independentWindow_0_portrait_iPhone_SE__3rd_generation__17_0@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testRotationInIndependentWindow_independentWindow_0_portrait_iPhone_SE__3rd_generation__17_0@2x.png -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testRotationInIndependentWindow_independentWindow_1_landscapeLeft_iPad__10th_generation__17_0@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testRotationInIndependentWindow_independentWindow_1_landscapeLeft_iPad__10th_generation__17_0@2x.png -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testRotationInIndependentWindow_independentWindow_1_landscapeLeft_iPhone16_1_17_0@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testRotationInIndependentWindow_independentWindow_1_landscapeLeft_iPhone16_1_17_0@3x.png -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testRotationInIndependentWindow_independentWindow_1_landscapeLeft_iPhone_SE__3rd_generation__17_0@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testRotationInIndependentWindow_independentWindow_1_landscapeLeft_iPhone_SE__3rd_generation__17_0@2x.png -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testRotationInIndependentWindow_independentWindow_2_portrait_iPad__10th_generation__17_0@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testRotationInIndependentWindow_independentWindow_2_portrait_iPad__10th_generation__17_0@2x.png -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testRotationInIndependentWindow_independentWindow_2_portrait_iPhone16_1_17_0@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testRotationInIndependentWindow_independentWindow_2_portrait_iPhone16_1_17_0@3x.png -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testRotationInIndependentWindow_independentWindow_2_portrait_iPhone_SE__3rd_generation__17_0@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testRotationInIndependentWindow_independentWindow_2_portrait_iPhone_SE__3rd_generation__17_0@2x.png -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testRotationInIndependentWindow_independentWindow_3_landscapeLeft_iPad__10th_generation__17_0@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testRotationInIndependentWindow_independentWindow_3_landscapeLeft_iPad__10th_generation__17_0@2x.png -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testRotationInIndependentWindow_independentWindow_3_landscapeLeft_iPhone16_1_17_0@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testRotationInIndependentWindow_independentWindow_3_landscapeLeft_iPhone16_1_17_0@3x.png -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testRotationInIndependentWindow_independentWindow_3_landscapeLeft_iPhone_SE__3rd_generation__17_0@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testRotationInIndependentWindow_independentWindow_3_landscapeLeft_iPhone_SE__3rd_generation__17_0@2x.png -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testRotationInIndependentWindow_independentWindow_4_portrait_iPad__10th_generation__17_0@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testRotationInIndependentWindow_independentWindow_4_portrait_iPad__10th_generation__17_0@2x.png -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testRotationInIndependentWindow_independentWindow_4_portrait_iPhone16_1_17_0@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testRotationInIndependentWindow_independentWindow_4_portrait_iPhone16_1_17_0@3x.png -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testRotationInIndependentWindow_independentWindow_4_portrait_iPhone_SE__3rd_generation__17_0@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testRotationInIndependentWindow_independentWindow_4_portrait_iPhone_SE__3rd_generation__17_0@2x.png -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testRotationInWindow_sameWindow_0_portrait_iPad__10th_generation__17_0@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testRotationInWindow_sameWindow_0_portrait_iPad__10th_generation__17_0@2x.png -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testRotationInWindow_sameWindow_0_portrait_iPhone16_1_17_0@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testRotationInWindow_sameWindow_0_portrait_iPhone16_1_17_0@3x.png -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testRotationInWindow_sameWindow_0_portrait_iPhone_SE__3rd_generation__17_0@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testRotationInWindow_sameWindow_0_portrait_iPhone_SE__3rd_generation__17_0@2x.png -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testRotationInWindow_sameWindow_1_landscapeLeft_iPad__10th_generation__17_0@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testRotationInWindow_sameWindow_1_landscapeLeft_iPad__10th_generation__17_0@2x.png -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testRotationInWindow_sameWindow_1_landscapeLeft_iPhone16_1_17_0@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testRotationInWindow_sameWindow_1_landscapeLeft_iPhone16_1_17_0@3x.png -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testRotationInWindow_sameWindow_1_landscapeLeft_iPhone_SE__3rd_generation__17_0@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testRotationInWindow_sameWindow_1_landscapeLeft_iPhone_SE__3rd_generation__17_0@2x.png -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testRotationInWindow_sameWindow_2_portrait_iPad__10th_generation__17_0@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testRotationInWindow_sameWindow_2_portrait_iPad__10th_generation__17_0@2x.png -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testRotationInWindow_sameWindow_2_portrait_iPhone16_1_17_0@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testRotationInWindow_sameWindow_2_portrait_iPhone16_1_17_0@3x.png -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testRotationInWindow_sameWindow_2_portrait_iPhone_SE__3rd_generation__17_0@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testRotationInWindow_sameWindow_2_portrait_iPhone_SE__3rd_generation__17_0@2x.png -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testRotationInWindow_sameWindow_3_landscapeLeft_iPad__10th_generation__17_0@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testRotationInWindow_sameWindow_3_landscapeLeft_iPad__10th_generation__17_0@2x.png -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testRotationInWindow_sameWindow_3_landscapeLeft_iPhone16_1_17_0@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testRotationInWindow_sameWindow_3_landscapeLeft_iPhone16_1_17_0@3x.png -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testRotationInWindow_sameWindow_3_landscapeLeft_iPhone_SE__3rd_generation__17_0@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testRotationInWindow_sameWindow_3_landscapeLeft_iPhone_SE__3rd_generation__17_0@2x.png -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testRotationInWindow_sameWindow_4_portrait_iPad__10th_generation__17_0@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testRotationInWindow_sameWindow_4_portrait_iPad__10th_generation__17_0@2x.png -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testRotationInWindow_sameWindow_4_portrait_iPhone16_1_17_0@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testRotationInWindow_sameWindow_4_portrait_iPhone16_1_17_0@3x.png -------------------------------------------------------------------------------- /Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testRotationInWindow_sameWindow_4_portrait_iPhone_SE__3rd_generation__17_0@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64/InstructionsSnapshotTests.DefaultExampleSnapshotTests/testRotationInWindow_sameWindow_4_portrait_iPhone_SE__3rd_generation__17_0@2x.png -------------------------------------------------------------------------------- /Examples/Example/Sources/Core/AccessibilityIdentifiers.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018-present Frédéric Maquin and contributors. 2 | // Licensed under the terms of the MIT License. 3 | 4 | import Foundation 5 | 6 | struct AccessibilityIdentifiers { 7 | static let next = "AccessibilityIdentifiers.next" 8 | } 9 | -------------------------------------------------------------------------------- /Examples/Example/Sources/Core/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-present Frédéric Maquin and contributors. 2 | // Licensed under the terms of the MIT License. 3 | 4 | import UIKit 5 | 6 | @UIApplicationMain 7 | class AppDelegate: UIResponder, UIApplicationDelegate { 8 | 9 | var window: UIWindow? 10 | var backgroundSessionCompletionHandler: (() -> Void)? 11 | 12 | func application( 13 | _ application: UIApplication, 14 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 15 | ) -> Bool { 16 | // Removing animations for speed and stability. 17 | if CommandLine.arguments.contains("--UITests") || 18 | CommandLine.arguments.contains("--SnapshotTests") { 19 | UIView.setAnimationsEnabled(false) 20 | } 21 | 22 | return true 23 | } 24 | 25 | // MARK: Legacy Lifecycle 26 | func applicationWillResignActive(_ application: UIApplication) { } 27 | func applicationDidEnterBackground(_ application: UIApplication) { } 28 | func applicationWillEnterForeground(_ application: UIApplication) { } 29 | func applicationDidBecomeActive(_ application: UIApplication) { } 30 | func applicationWillTerminate(_ application: UIApplication) { } 31 | 32 | // MARK: Background URLSession 33 | func application(_ application: UIApplication, 34 | handleEventsForBackgroundURLSession identifier: String, 35 | completionHandler: @escaping () -> Void) { 36 | backgroundSessionCompletionHandler = completionHandler 37 | } 38 | 39 | // MARK: UISceneSession Lifecycle 40 | @available(iOS 13.0, *) 41 | func application(_ application: UIApplication, 42 | configurationForConnecting connectingSceneSession: UISceneSession, 43 | options: UIScene.ConnectionOptions) -> UISceneConfiguration { 44 | // Called when a new scene session is being created. 45 | // Use this method to select a configuration to create the new scene with. 46 | return UISceneConfiguration(name: "Default Configuration", 47 | sessionRole: connectingSceneSession.role) 48 | } 49 | 50 | @available(iOS 13.0, *) 51 | func application(_ application: UIApplication, 52 | didDiscardSceneSessions sceneSessions: Set) { 53 | // Called when the user discards a scene session. 54 | // If any sessions were discarded while the application was not running, 55 | // this will be called shortly after application:didFinishLaunchingWithOptions. 56 | // Use this method to release any resources that were specific to the discarded scenes, 57 | // as they will not return. 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /Examples/Example/Sources/Core/Custom Views/CustomCoachMarkArrowView.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-present Frédéric Maquin and contributors. 2 | // Licensed under the terms of the MIT License. 3 | 4 | import UIKit 5 | import Instructions 6 | 7 | // Custom coach mark body (with the secret-like arrow) 8 | internal class CustomCoachMarkArrowView: UIView, CoachMarkArrowView { 9 | // MARK: - Internal properties 10 | var topPlateImage = UIImage(named: "coach-mark-top-plate") 11 | var bottomPlateImage = UIImage(named: "coach-mark-bottom-plate") 12 | var plate = UIImageView() 13 | 14 | var isHighlighted: Bool = false 15 | 16 | // MARK: - Private properties 17 | private var column = UIView() 18 | 19 | // MARK: - Initialization 20 | init(orientation: CoachMarkArrowOrientation) { 21 | super.init(frame: CGRect.zero) 22 | 23 | if orientation == .top { 24 | self.plate.image = topPlateImage 25 | } else { 26 | self.plate.image = bottomPlateImage 27 | } 28 | 29 | self.translatesAutoresizingMaskIntoConstraints = false 30 | self.column.translatesAutoresizingMaskIntoConstraints = false 31 | self.plate.translatesAutoresizingMaskIntoConstraints = false 32 | 33 | self.addSubview(plate) 34 | self.addSubview(column) 35 | 36 | plate.backgroundColor = UIColor.clear 37 | column.backgroundColor = UIColor.white 38 | 39 | plate.fillSuperviewHorizontally() 40 | NSLayoutConstraint.activate([ 41 | column.widthAnchor.constraint(equalToConstant: 3), 42 | column.centerXAnchor.constraint(equalTo: centerXAnchor) 43 | ]) 44 | 45 | if orientation == .top { 46 | NSLayoutConstraint.activate( 47 | NSLayoutConstraint.constraints( 48 | withVisualFormat: "V:|[plate(==5)][column(==10)]|", 49 | options: NSLayoutConstraint.FormatOptions(rawValue: 0), 50 | metrics: nil, 51 | views: ["plate": plate, "column": column] 52 | ) 53 | ) 54 | } else { 55 | NSLayoutConstraint.activate( 56 | NSLayoutConstraint.constraints( 57 | withVisualFormat: "V:|[column(==10)][plate(==5)]|", 58 | options: NSLayoutConstraint.FormatOptions(rawValue: 0), 59 | metrics: nil, 60 | views: ["plate": plate, "column": column] 61 | ) 62 | ) 63 | } 64 | } 65 | 66 | required init(coder aDecoder: NSCoder) { 67 | fatalError("This class does not support NSCoding.") 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /Examples/Example/Sources/Core/Custom Views/TransparentCoachMarkArrowView.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-present Frédéric Maquin and contributors. 2 | // Licensed under the terms of the MIT License. 3 | 4 | import UIKit 5 | import Instructions 6 | 7 | // Transparent coach mark (text without background, cool arrow) 8 | internal class TransparentCoachMarkArrowView: UIImageView, CoachMarkArrowView { 9 | // MARK: - Initialization 10 | init(orientation: CoachMarkArrowOrientation) { 11 | if orientation == .top { 12 | super.init(image: UIImage(named: "arrow-top")) 13 | } else { 14 | super.init(image: UIImage(named: "arrow-bottom")) 15 | } 16 | 17 | self.translatesAutoresizingMaskIntoConstraints = false 18 | 19 | widthAnchor.constraint(equalToConstant: self.image?.size.width ?? 0).isActive = true 20 | heightAnchor.constraint(equalToConstant: self.image?.size.width ?? 0).isActive = true 21 | } 22 | 23 | required init?(coder aDecoder: NSCoder) { 24 | fatalError("This class does not support NSCoding.") 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Examples/Example/Sources/Core/Custom Views/TransparentCoachMarkBodyView.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-present Frédéric Maquin and contributors. 2 | // Licensed under the terms of the MIT License. 3 | 4 | import UIKit 5 | import Instructions 6 | 7 | // Transparent coach mark (text without background, cool arrow) 8 | internal class TransparentCoachMarkBodyView: UIControl, CoachMarkBodyView { 9 | // MARK: - Internal properties 10 | var nextControl: UIControl? { return self } 11 | 12 | weak var highlightArrowDelegate: CoachMarkBodyHighlightArrowDelegate? 13 | 14 | var hintLabel = UITextView() 15 | 16 | // MARK: - Initialization 17 | override init (frame: CGRect) { 18 | super.init(frame: frame) 19 | 20 | self.setupInnerViewHierarchy() 21 | } 22 | 23 | convenience init() { 24 | self.init(frame: CGRect.zero) 25 | } 26 | 27 | required init?(coder aDecoder: NSCoder) { 28 | fatalError("This class does not support NSCoding.") 29 | } 30 | 31 | // MARK: - Private methods 32 | private func setupInnerViewHierarchy() { 33 | self.translatesAutoresizingMaskIntoConstraints = false 34 | 35 | hintLabel.backgroundColor = UIColor.clear 36 | hintLabel.textColor = UIColor.white 37 | hintLabel.font = UIFont.systemFont(ofSize: 15.0) 38 | hintLabel.isScrollEnabled = false 39 | hintLabel.textAlignment = .left 40 | hintLabel.isEditable = false 41 | 42 | hintLabel.translatesAutoresizingMaskIntoConstraints = false 43 | hintLabel.isUserInteractionEnabled = false 44 | 45 | self.addSubview(hintLabel) 46 | hintLabel.fillSuperview() 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Examples/Example/Sources/Core/Extensions.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017-present Frédéric Maquin and contributors. 2 | // Licensed under the terms of the MIT License. 3 | 4 | import UIKit 5 | 6 | extension UIView { 7 | internal func fillSuperview() { 8 | fillSuperviewVertically() 9 | fillSuperviewHorizontally() 10 | } 11 | 12 | internal func fillSuperviewVertically() { 13 | guard let superview = superview else { return } 14 | 15 | self.topAnchor.constraint(equalTo: superview.topAnchor).isActive = true 16 | self.bottomAnchor.constraint(equalTo: superview.bottomAnchor).isActive = true 17 | } 18 | 19 | internal func fillSuperviewHorizontally() { 20 | guard let superview = superview else { return } 21 | 22 | self.leadingAnchor.constraint(equalTo: superview.leadingAnchor).isActive = true 23 | self.trailingAnchor.constraint(equalTo: superview.trailingAnchor).isActive = true 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Examples/Example/Sources/Core/SceneDelegate.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present Frédéric Maquin and contributors. 2 | // Licensed under the terms of the MIT License. 3 | 4 | import UIKit 5 | 6 | @available(iOS 13.0, *) 7 | class SceneDelegate: UIResponder, UIWindowSceneDelegate { 8 | 9 | var window: UIWindow? 10 | 11 | func scene(_ scene: UIScene, willConnectTo session: UISceneSession, 12 | options connectionOptions: UIScene.ConnectionOptions) { 13 | // Use this method to optionally configure and attach the UIWindow `window` to 14 | // the provided UIWindowScene `scene`. If using a storyboard, the `window` property 15 | // will automatically be initialized and attached to the scene. This delegate does not 16 | // imply the connecting scene or session are new 17 | // (see `application:configurationForConnectingSceneSession` instead). 18 | } 19 | 20 | func sceneDidDisconnect(_ scene: UIScene) { 21 | // Called as the scene is being released by the system. 22 | // This occurs shortly after the scene enters the background, or when its session 23 | // is discarded. Release any resources associated with this scene that can be re-created 24 | // the next time the scene connects. The scene may re-connect later, as its session was 25 | // not necessarily discarded (see `application:didDiscardSceneSessions` instead). 26 | } 27 | 28 | func sceneDidBecomeActive(_ scene: UIScene) { 29 | // Called when the scene has moved from an inactive state to an active state. 30 | // Use this method to restart any tasks that were paused (or not yet started) 31 | // when the scene was inactive. 32 | } 33 | 34 | func sceneWillResignActive(_ scene: UIScene) { 35 | // Called when the scene will move from an active state to an inactive state. 36 | // This may occur due to temporary interruptions (ex. an incoming phone call). 37 | } 38 | 39 | func sceneWillEnterForeground(_ scene: UIScene) { 40 | // Called as the scene transitions from the background to the foreground. 41 | // Use this method to undo the changes made on entering the background. 42 | } 43 | 44 | func sceneDidEnterBackground(_ scene: UIScene) { 45 | // Called as the scene transitions from the foreground to the background. 46 | // Use this method to save data, release shared resources, and store enough 47 | // scene-specific state information to restore the scene back to its current state. 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Examples/Example/Sources/Core/View Controllers/Basic Examples/BlurringOverlayViewController.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-present Frédéric Maquin and contributors. 2 | // Licensed under the terms of the MIT License. 3 | 4 | import UIKit 5 | import Instructions 6 | 7 | /// Will display coach marks on top of a blurred background. 8 | internal class BlurringOverlayViewController: DefaultViewController { 9 | // MARK: - View lifecycle 10 | override func viewDidLoad() { 11 | super.viewDidLoad() 12 | 13 | self.coachMarksController.overlay.blurEffectStyle = .regular 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Examples/Example/Sources/Core/View Controllers/Delegate Examples/ConfigurationViewController.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017-present Frédéric Maquin and contributors. 2 | // Licensed under the terms of the MIT License. 3 | 4 | import UIKit 5 | import Instructions 6 | 7 | internal class ConfigurationViewController: DefaultViewController { 8 | 9 | private var instructionsNavigationController: InstructionsNavigationController? { 10 | return navigationController as? InstructionsNavigationController 11 | } 12 | 13 | override func viewDidLoad() { 14 | super.viewDidLoad() 15 | 16 | coachMarksController.statusBarVisibility = .hidden 17 | coachMarksController.rotationStyle = .manual 18 | coachMarksController.interfaceOrientations = 19 | .userDefined(as: supportedInterfaceOrientations) 20 | 21 | instructionsNavigationController?.isLocked = true 22 | } 23 | 24 | override func viewWillDisappear(_ animated: Bool) { 25 | instructionsNavigationController?.isLocked = false 26 | } 27 | 28 | override var shouldAutorotate: Bool { 29 | return instructionsNavigationController?.shouldAutorotate ?? true 30 | } 31 | 32 | override var prefersStatusBarHidden: Bool { 33 | return true 34 | } 35 | 36 | override var supportedInterfaceOrientations: UIInterfaceOrientationMask { 37 | return instructionsNavigationController?.supportedInterfaceOrientations ?? .all 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Examples/Example/Sources/Core/View Controllers/InstructionsNavigationController.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017-present Frédéric Maquin and contributors. 2 | // Licensed under the terms of the MIT License. 3 | 4 | import Foundation 5 | import Instructions 6 | 7 | class InstructionsNavigationController: UINavigationController { 8 | var isLocked = false 9 | 10 | override var shouldAutorotate: Bool { 11 | return !isLocked 12 | } 13 | 14 | override var supportedInterfaceOrientations: UIInterfaceOrientationMask { 15 | return isLocked ? .portrait : .all 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Examples/Example/Sources/Core/View Controllers/MainTableViewController.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017-present Frédéric Maquin and contributors. 2 | // Licensed under the terms of the MIT License. 3 | 4 | import UIKit 5 | 6 | class MainTableViewController: UITableViewController { 7 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) { 8 | if segue.identifier == "PausingOverlay", 9 | let controller = segue.destination as? PausingCodeViewController { 10 | controller.pauseStyle = .hideOverlay 11 | } else if segue.identifier == "PausingInstructions", 12 | let controller = segue.destination as? PausingCodeViewController { 13 | controller.pauseStyle = .hideInstructions 14 | } else if segue.identifier == "IndependentWindowContext", 15 | let controller = segue.destination as? DefaultViewController { 16 | controller.presentationContext = .independentWindow 17 | } else if segue.identifier == "WindowLevel", 18 | let controller = segue.destination as? DefaultViewController { 19 | controller.presentationContext = .independentWindow 20 | controller.windowLevel = UIWindow.Level.statusBar + 1 21 | } else if segue.identifier == "ControllerWindowContext", 22 | let controller = segue.destination as? DefaultViewController { 23 | controller.presentationContext = .controllerWindow 24 | } else if segue.identifier == "ControllerContext", 25 | let controller = segue.destination as? DefaultViewController { 26 | controller.presentationContext = .controller 27 | } else if segue.identifier == "InvisibleOverlay", 28 | let controller = segue.destination as? DefaultViewController { 29 | controller.useInvisibleOverlay = true 30 | } else if segue.identifier == "BlurInControllerWindowContext", 31 | let controller = segue.destination as? BlurringOverlayViewController { 32 | controller.presentationContext = .controllerWindow 33 | } else if segue.identifier == "BlurInControllerContext", 34 | let controller = segue.destination as? BlurringOverlayViewController { 35 | controller.presentationContext = .controller 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Examples/Example/Sources/Supporting Files/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Example-20@2x-1.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Example-20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Example-29@2x-1.png", 19 | "scale" : "2x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Example-29@3x.png", 25 | "scale" : "3x" 26 | }, 27 | { 28 | "size" : "40x40", 29 | "idiom" : "iphone", 30 | "filename" : "Example-40@2x.png", 31 | "scale" : "2x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Example-40@3x.png", 37 | "scale" : "3x" 38 | }, 39 | { 40 | "size" : "60x60", 41 | "idiom" : "iphone", 42 | "filename" : "Example-60@2x.png", 43 | "scale" : "2x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Example-60@3x.png", 49 | "scale" : "3x" 50 | }, 51 | { 52 | "size" : "20x20", 53 | "idiom" : "ipad", 54 | "filename" : "Example-20.png", 55 | "scale" : "1x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Example-20@2x.png", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "size" : "29x29", 65 | "idiom" : "ipad", 66 | "filename" : "Example-29.png", 67 | "scale" : "1x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Example-29@2x.png", 73 | "scale" : "2x" 74 | }, 75 | { 76 | "size" : "40x40", 77 | "idiom" : "ipad", 78 | "filename" : "Example-40.png", 79 | "scale" : "1x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Example-40@2x-1.png", 85 | "scale" : "2x" 86 | }, 87 | { 88 | "size" : "76x76", 89 | "idiom" : "ipad", 90 | "filename" : "Example-76.png", 91 | "scale" : "1x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Example-76@2x.png", 97 | "scale" : "2x" 98 | }, 99 | { 100 | "size" : "83.5x83.5", 101 | "idiom" : "ipad", 102 | "filename" : "Example-83,5@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "1024x1024", 107 | "idiom" : "ios-marketing", 108 | "filename" : "ExampleAppStore-1024.png", 109 | "scale" : "1x" 110 | } 111 | ], 112 | "info" : { 113 | "version" : 1, 114 | "author" : "xcode" 115 | } 116 | } -------------------------------------------------------------------------------- /Examples/Example/Sources/Supporting Files/Assets.xcassets/AppIcon.appiconset/Example-20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Sources/Supporting Files/Assets.xcassets/AppIcon.appiconset/Example-20.png -------------------------------------------------------------------------------- /Examples/Example/Sources/Supporting Files/Assets.xcassets/AppIcon.appiconset/Example-20@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Sources/Supporting Files/Assets.xcassets/AppIcon.appiconset/Example-20@2x-1.png -------------------------------------------------------------------------------- /Examples/Example/Sources/Supporting Files/Assets.xcassets/AppIcon.appiconset/Example-20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Sources/Supporting Files/Assets.xcassets/AppIcon.appiconset/Example-20@2x.png -------------------------------------------------------------------------------- /Examples/Example/Sources/Supporting Files/Assets.xcassets/AppIcon.appiconset/Example-20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Sources/Supporting Files/Assets.xcassets/AppIcon.appiconset/Example-20@3x.png -------------------------------------------------------------------------------- /Examples/Example/Sources/Supporting Files/Assets.xcassets/AppIcon.appiconset/Example-29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Sources/Supporting Files/Assets.xcassets/AppIcon.appiconset/Example-29.png -------------------------------------------------------------------------------- /Examples/Example/Sources/Supporting Files/Assets.xcassets/AppIcon.appiconset/Example-29@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Sources/Supporting Files/Assets.xcassets/AppIcon.appiconset/Example-29@2x-1.png -------------------------------------------------------------------------------- /Examples/Example/Sources/Supporting Files/Assets.xcassets/AppIcon.appiconset/Example-29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Sources/Supporting Files/Assets.xcassets/AppIcon.appiconset/Example-29@2x.png -------------------------------------------------------------------------------- /Examples/Example/Sources/Supporting Files/Assets.xcassets/AppIcon.appiconset/Example-29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Sources/Supporting Files/Assets.xcassets/AppIcon.appiconset/Example-29@3x.png -------------------------------------------------------------------------------- /Examples/Example/Sources/Supporting Files/Assets.xcassets/AppIcon.appiconset/Example-40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Sources/Supporting Files/Assets.xcassets/AppIcon.appiconset/Example-40.png -------------------------------------------------------------------------------- /Examples/Example/Sources/Supporting Files/Assets.xcassets/AppIcon.appiconset/Example-40@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Sources/Supporting Files/Assets.xcassets/AppIcon.appiconset/Example-40@2x-1.png -------------------------------------------------------------------------------- /Examples/Example/Sources/Supporting Files/Assets.xcassets/AppIcon.appiconset/Example-40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Sources/Supporting Files/Assets.xcassets/AppIcon.appiconset/Example-40@2x.png -------------------------------------------------------------------------------- /Examples/Example/Sources/Supporting Files/Assets.xcassets/AppIcon.appiconset/Example-40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Sources/Supporting Files/Assets.xcassets/AppIcon.appiconset/Example-40@3x.png -------------------------------------------------------------------------------- /Examples/Example/Sources/Supporting Files/Assets.xcassets/AppIcon.appiconset/Example-60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Sources/Supporting Files/Assets.xcassets/AppIcon.appiconset/Example-60@2x.png -------------------------------------------------------------------------------- /Examples/Example/Sources/Supporting Files/Assets.xcassets/AppIcon.appiconset/Example-60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Sources/Supporting Files/Assets.xcassets/AppIcon.appiconset/Example-60@3x.png -------------------------------------------------------------------------------- /Examples/Example/Sources/Supporting Files/Assets.xcassets/AppIcon.appiconset/Example-76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Sources/Supporting Files/Assets.xcassets/AppIcon.appiconset/Example-76.png -------------------------------------------------------------------------------- /Examples/Example/Sources/Supporting Files/Assets.xcassets/AppIcon.appiconset/Example-76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Sources/Supporting Files/Assets.xcassets/AppIcon.appiconset/Example-76@2x.png -------------------------------------------------------------------------------- /Examples/Example/Sources/Supporting Files/Assets.xcassets/AppIcon.appiconset/Example-83,5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Sources/Supporting Files/Assets.xcassets/AppIcon.appiconset/Example-83,5@2x.png -------------------------------------------------------------------------------- /Examples/Example/Sources/Supporting Files/Assets.xcassets/AppIcon.appiconset/ExampleAppStore-1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Sources/Supporting Files/Assets.xcassets/AppIcon.appiconset/ExampleAppStore-1024.png -------------------------------------------------------------------------------- /Examples/Example/Sources/Supporting Files/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Examples/Example/Sources/Supporting Files/Assets.xcassets/arrow-bottom.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "arrow-bottom.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "arrow-bottom@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "arrow-bottom@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Examples/Example/Sources/Supporting Files/Assets.xcassets/arrow-bottom.imageset/arrow-bottom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Sources/Supporting Files/Assets.xcassets/arrow-bottom.imageset/arrow-bottom.png -------------------------------------------------------------------------------- /Examples/Example/Sources/Supporting Files/Assets.xcassets/arrow-bottom.imageset/arrow-bottom@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Sources/Supporting Files/Assets.xcassets/arrow-bottom.imageset/arrow-bottom@2x.png -------------------------------------------------------------------------------- /Examples/Example/Sources/Supporting Files/Assets.xcassets/arrow-bottom.imageset/arrow-bottom@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Sources/Supporting Files/Assets.xcassets/arrow-bottom.imageset/arrow-bottom@3x.png -------------------------------------------------------------------------------- /Examples/Example/Sources/Supporting Files/Assets.xcassets/arrow-top.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "arrow-top.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "arrow-top@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "arrow-top@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Examples/Example/Sources/Supporting Files/Assets.xcassets/arrow-top.imageset/arrow-top.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Sources/Supporting Files/Assets.xcassets/arrow-top.imageset/arrow-top.png -------------------------------------------------------------------------------- /Examples/Example/Sources/Supporting Files/Assets.xcassets/arrow-top.imageset/arrow-top@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Sources/Supporting Files/Assets.xcassets/arrow-top.imageset/arrow-top@2x.png -------------------------------------------------------------------------------- /Examples/Example/Sources/Supporting Files/Assets.xcassets/arrow-top.imageset/arrow-top@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Sources/Supporting Files/Assets.xcassets/arrow-top.imageset/arrow-top@3x.png -------------------------------------------------------------------------------- /Examples/Example/Sources/Supporting Files/Assets.xcassets/button-background-highlighted.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "resizing" : { 5 | "mode" : "9-part", 6 | "center" : { 7 | "mode" : "tile", 8 | "width" : 1, 9 | "height" : 1 10 | }, 11 | "cap-insets" : { 12 | "bottom" : 6, 13 | "top" : 5, 14 | "right" : 5, 15 | "left" : 5 16 | } 17 | }, 18 | "idiom" : "universal", 19 | "filename" : "button-background-highlighted.png", 20 | "scale" : "1x" 21 | }, 22 | { 23 | "resizing" : { 24 | "mode" : "9-part", 25 | "center" : { 26 | "mode" : "tile", 27 | "width" : 2, 28 | "height" : 2 29 | }, 30 | "cap-insets" : { 31 | "bottom" : 12, 32 | "top" : 10, 33 | "right" : 10, 34 | "left" : 10 35 | } 36 | }, 37 | "idiom" : "universal", 38 | "filename" : "button-background-highlighted@2x.png", 39 | "scale" : "2x" 40 | }, 41 | { 42 | "resizing" : { 43 | "mode" : "9-part", 44 | "center" : { 45 | "mode" : "tile", 46 | "width" : 1, 47 | "height" : 1 48 | }, 49 | "cap-insets" : { 50 | "bottom" : 18, 51 | "top" : 17, 52 | "right" : 16, 53 | "left" : 16 54 | } 55 | }, 56 | "idiom" : "universal", 57 | "filename" : "button-background-highlighted@3x.png", 58 | "scale" : "3x" 59 | } 60 | ], 61 | "info" : { 62 | "version" : 1, 63 | "author" : "xcode" 64 | } 65 | } -------------------------------------------------------------------------------- /Examples/Example/Sources/Supporting Files/Assets.xcassets/button-background-highlighted.imageset/button-background-highlighted.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Sources/Supporting Files/Assets.xcassets/button-background-highlighted.imageset/button-background-highlighted.png -------------------------------------------------------------------------------- /Examples/Example/Sources/Supporting Files/Assets.xcassets/button-background-highlighted.imageset/button-background-highlighted@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Sources/Supporting Files/Assets.xcassets/button-background-highlighted.imageset/button-background-highlighted@2x.png -------------------------------------------------------------------------------- /Examples/Example/Sources/Supporting Files/Assets.xcassets/button-background-highlighted.imageset/button-background-highlighted@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Sources/Supporting Files/Assets.xcassets/button-background-highlighted.imageset/button-background-highlighted@3x.png -------------------------------------------------------------------------------- /Examples/Example/Sources/Supporting Files/Assets.xcassets/button-background.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "resizing" : { 5 | "mode" : "9-part", 6 | "center" : { 7 | "mode" : "tile", 8 | "width" : 1, 9 | "height" : 1 10 | }, 11 | "cap-insets" : { 12 | "bottom" : 6, 13 | "top" : 5, 14 | "right" : 5, 15 | "left" : 5 16 | } 17 | }, 18 | "idiom" : "universal", 19 | "filename" : "button-background.png", 20 | "scale" : "1x" 21 | }, 22 | { 23 | "resizing" : { 24 | "mode" : "9-part", 25 | "center" : { 26 | "mode" : "tile", 27 | "width" : 2, 28 | "height" : 2 29 | }, 30 | "cap-insets" : { 31 | "bottom" : 12, 32 | "top" : 10, 33 | "right" : 10, 34 | "left" : 10 35 | } 36 | }, 37 | "idiom" : "universal", 38 | "filename" : "button-background@2x.png", 39 | "scale" : "2x" 40 | }, 41 | { 42 | "resizing" : { 43 | "mode" : "9-part", 44 | "center" : { 45 | "mode" : "tile", 46 | "width" : 1, 47 | "height" : 1 48 | }, 49 | "cap-insets" : { 50 | "bottom" : 18, 51 | "top" : 17, 52 | "right" : 16, 53 | "left" : 16 54 | } 55 | }, 56 | "idiom" : "universal", 57 | "filename" : "button-background@3x.png", 58 | "scale" : "3x" 59 | } 60 | ], 61 | "info" : { 62 | "version" : 1, 63 | "author" : "xcode" 64 | } 65 | } -------------------------------------------------------------------------------- /Examples/Example/Sources/Supporting Files/Assets.xcassets/button-background.imageset/button-background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Sources/Supporting Files/Assets.xcassets/button-background.imageset/button-background.png -------------------------------------------------------------------------------- /Examples/Example/Sources/Supporting Files/Assets.xcassets/button-background.imageset/button-background@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Sources/Supporting Files/Assets.xcassets/button-background.imageset/button-background@2x.png -------------------------------------------------------------------------------- /Examples/Example/Sources/Supporting Files/Assets.xcassets/button-background.imageset/button-background@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Sources/Supporting Files/Assets.xcassets/button-background.imageset/button-background@3x.png -------------------------------------------------------------------------------- /Examples/Example/Sources/Supporting Files/Assets.xcassets/coach-mark-bottom-plate.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "resizing" : { 5 | "mode" : "3-part-horizontal", 6 | "center" : { 7 | "mode" : "tile", 8 | "width" : 1 9 | }, 10 | "cap-insets" : { 11 | "right" : 8, 12 | "left" : 8 13 | } 14 | }, 15 | "idiom" : "universal", 16 | "filename" : "coach-mark-bottom-plate.png", 17 | "scale" : "1x" 18 | }, 19 | { 20 | "resizing" : { 21 | "mode" : "3-part-horizontal", 22 | "center" : { 23 | "mode" : "tile", 24 | "width" : 2 25 | }, 26 | "cap-insets" : { 27 | "right" : 16, 28 | "left" : 16 29 | } 30 | }, 31 | "idiom" : "universal", 32 | "filename" : "coach-mark-bottom-plate@2x.png", 33 | "scale" : "2x" 34 | }, 35 | { 36 | "resizing" : { 37 | "mode" : "3-part-horizontal", 38 | "center" : { 39 | "mode" : "tile", 40 | "width" : 1 41 | }, 42 | "cap-insets" : { 43 | "right" : 25, 44 | "left" : 25 45 | } 46 | }, 47 | "idiom" : "universal", 48 | "filename" : "coach-mark-bottom-plate@3x.png", 49 | "scale" : "3x" 50 | } 51 | ], 52 | "info" : { 53 | "version" : 1, 54 | "author" : "xcode" 55 | } 56 | } -------------------------------------------------------------------------------- /Examples/Example/Sources/Supporting Files/Assets.xcassets/coach-mark-bottom-plate.imageset/coach-mark-bottom-plate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Sources/Supporting Files/Assets.xcassets/coach-mark-bottom-plate.imageset/coach-mark-bottom-plate.png -------------------------------------------------------------------------------- /Examples/Example/Sources/Supporting Files/Assets.xcassets/coach-mark-bottom-plate.imageset/coach-mark-bottom-plate@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Sources/Supporting Files/Assets.xcassets/coach-mark-bottom-plate.imageset/coach-mark-bottom-plate@2x.png -------------------------------------------------------------------------------- /Examples/Example/Sources/Supporting Files/Assets.xcassets/coach-mark-bottom-plate.imageset/coach-mark-bottom-plate@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Sources/Supporting Files/Assets.xcassets/coach-mark-bottom-plate.imageset/coach-mark-bottom-plate@3x.png -------------------------------------------------------------------------------- /Examples/Example/Sources/Supporting Files/Assets.xcassets/coach-mark-top-plate.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "resizing" : { 5 | "mode" : "3-part-horizontal", 6 | "center" : { 7 | "mode" : "tile", 8 | "width" : 1 9 | }, 10 | "cap-insets" : { 11 | "right" : 8, 12 | "left" : 8 13 | } 14 | }, 15 | "idiom" : "universal", 16 | "filename" : "coach-mark-top-plate.png", 17 | "scale" : "1x" 18 | }, 19 | { 20 | "resizing" : { 21 | "mode" : "3-part-horizontal", 22 | "center" : { 23 | "mode" : "tile", 24 | "width" : 2 25 | }, 26 | "cap-insets" : { 27 | "right" : 16, 28 | "left" : 16 29 | } 30 | }, 31 | "idiom" : "universal", 32 | "filename" : "coach-mark-top-plate@2x.png", 33 | "scale" : "2x" 34 | }, 35 | { 36 | "resizing" : { 37 | "mode" : "3-part-horizontal", 38 | "center" : { 39 | "mode" : "tile", 40 | "width" : 1 41 | }, 42 | "cap-insets" : { 43 | "right" : 25, 44 | "left" : 25 45 | } 46 | }, 47 | "idiom" : "universal", 48 | "filename" : "coach-mark-top-plate@3x.png", 49 | "scale" : "3x" 50 | } 51 | ], 52 | "info" : { 53 | "version" : 1, 54 | "author" : "xcode" 55 | } 56 | } -------------------------------------------------------------------------------- /Examples/Example/Sources/Supporting Files/Assets.xcassets/coach-mark-top-plate.imageset/coach-mark-top-plate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Sources/Supporting Files/Assets.xcassets/coach-mark-top-plate.imageset/coach-mark-top-plate.png -------------------------------------------------------------------------------- /Examples/Example/Sources/Supporting Files/Assets.xcassets/coach-mark-top-plate.imageset/coach-mark-top-plate@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Sources/Supporting Files/Assets.xcassets/coach-mark-top-plate.imageset/coach-mark-top-plate@2x.png -------------------------------------------------------------------------------- /Examples/Example/Sources/Supporting Files/Assets.xcassets/coach-mark-top-plate.imageset/coach-mark-top-plate@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Sources/Supporting Files/Assets.xcassets/coach-mark-top-plate.imageset/coach-mark-top-plate@3x.png -------------------------------------------------------------------------------- /Examples/Example/Sources/Supporting Files/Assets.xcassets/face-female.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "face-female.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "face-female@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "face-female@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Examples/Example/Sources/Supporting Files/Assets.xcassets/face-female.imageset/face-female.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Sources/Supporting Files/Assets.xcassets/face-female.imageset/face-female.png -------------------------------------------------------------------------------- /Examples/Example/Sources/Supporting Files/Assets.xcassets/face-female.imageset/face-female@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Sources/Supporting Files/Assets.xcassets/face-female.imageset/face-female@2x.png -------------------------------------------------------------------------------- /Examples/Example/Sources/Supporting Files/Assets.xcassets/face-female.imageset/face-female@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Sources/Supporting Files/Assets.xcassets/face-female.imageset/face-female@3x.png -------------------------------------------------------------------------------- /Examples/Example/Sources/Supporting Files/Assets.xcassets/face-male.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "face-male.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "face-male@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "face-male@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Examples/Example/Sources/Supporting Files/Assets.xcassets/face-male.imageset/face-male.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Sources/Supporting Files/Assets.xcassets/face-male.imageset/face-male.png -------------------------------------------------------------------------------- /Examples/Example/Sources/Supporting Files/Assets.xcassets/face-male.imageset/face-male@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Sources/Supporting Files/Assets.xcassets/face-male.imageset/face-male@2x.png -------------------------------------------------------------------------------- /Examples/Example/Sources/Supporting Files/Assets.xcassets/face-male.imageset/face-male@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/Instructions/0f3efa77e781ffd4c1441e580d398e3d60ff3bfe/Examples/Example/Sources/Supporting Files/Assets.xcassets/face-male.imageset/face-male@3x.png -------------------------------------------------------------------------------- /Examples/Example/Sources/Supporting Files/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | Example 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | Instructions 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | LSRequiresIPhoneOS 26 | 27 | NSAppTransportSecurity 28 | 29 | NSExceptionDomains 30 | 31 | ephread.com 32 | 33 | NSIncludesSubdomains 34 | 35 | NSTemporaryExceptionAllowsInsecureHTTPLoads 36 | 37 | NSTemporaryExceptionMinimumTLSVersion 38 | TLSv1.1 39 | 40 | 41 | 42 | UIApplicationSceneManifest 43 | 44 | UIApplicationSupportsMultipleScenes 45 | 46 | UISceneConfigurations 47 | 48 | UIWindowSceneSessionRoleApplication 49 | 50 | 51 | UISceneConfigurationName 52 | Default Configuration 53 | UISceneDelegateClassName 54 | $(PRODUCT_MODULE_NAME).SceneDelegate 55 | UISceneStoryboardFile 56 | Main 57 | 58 | 59 | 60 | 61 | UILaunchStoryboardName 62 | LaunchScreen 63 | UIMainStoryboardFile 64 | Main 65 | UIRequiredDeviceCapabilities 66 | 67 | armv7 68 | 69 | UISupportedInterfaceOrientations 70 | 71 | UIInterfaceOrientationPortrait 72 | UIInterfaceOrientationLandscapeLeft 73 | UIInterfaceOrientationLandscapeRight 74 | 75 | UISupportedInterfaceOrientations~ipad 76 | 77 | UIInterfaceOrientationPortrait 78 | UIInterfaceOrientationPortraitUpsideDown 79 | UIInterfaceOrientationLandscapeLeft 80 | UIInterfaceOrientationLandscapeRight 81 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /Examples/Example/Sources/Supporting Files/ar.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | /* 2 | Localizable.strings 3 | Instructions Example 4 | 5 | Created by Frédéric on 01/04/2020. 6 | Copyright © 2020 Ephread. All rights reserved. 7 | */ 8 | -------------------------------------------------------------------------------- /Examples/Example/Sources/Supporting Files/en.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | /* 2 | Localizable.strings 3 | Instructions Example 4 | 5 | Created by Frédéric on 01/04/2020. 6 | Copyright © 2020 Ephread. All rights reserved. 7 | */ 8 | -------------------------------------------------------------------------------- /Examples/Example/UI Tests/CustomExampleTests.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018-present Frédéric Maquin and contributors. 2 | // Licensed under the terms of the MIT License. 3 | 4 | import XCTest 5 | 6 | class CustomExampleTests: XCTestCase { 7 | 8 | override func setUp() { 9 | super.setUp() 10 | continueAfterFailure = false 11 | XCUIApplication().launchWithAnimationsDisabled() 12 | } 13 | 14 | func testTapOnNextButton() { 15 | let app = XCUIApplication() 16 | app.tables.staticTexts["Custom"].tap() 17 | 18 | let nextButton = app.buttons["AccessibilityIdentifiers.next"] 19 | _ = nextButton.waitForExistence(timeout: 10) 20 | 21 | nextButton.tap() 22 | nextButton.tap() 23 | nextButton.tap() 24 | nextButton.tap() 25 | nextButton.tap() 26 | 27 | app.navigationBars["Custom"].buttons["Instructions"].tap() 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Examples/Example/UI Tests/DefaultExampleTests.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018-present Frédéric Maquin and contributors. 2 | // Licensed under the terms of the MIT License. 3 | import XCTest 4 | 5 | class DefaultExampleTests: XCTestCase { 6 | override func setUp() { 7 | super.setUp() 8 | continueAfterFailure = false 9 | XCUIApplication().launchWithAnimationsDisabled() 10 | } 11 | 12 | func testTapOnBody() { 13 | let app = XCUIApplication() 14 | app.tables.staticTexts["Default"].tap() 15 | 16 | let coachMark = app.otherElements["AccessibilityIdentifiers.coachMarkBody"] 17 | _ = coachMark.waitForExistence(timeout: 10) 18 | 19 | coachMark.tap() 20 | coachMark.tap() 21 | coachMark.tap() 22 | coachMark.tap() 23 | coachMark.tap() 24 | 25 | app.navigationBars["Default"].buttons["Instructions"].tap() 26 | } 27 | 28 | func testTapOnOverlay() { 29 | let app = XCUIApplication() 30 | app.tables.staticTexts["Default"].tap() 31 | 32 | let overlay = app.otherElements["AccessibilityIdentifiers.overlayView"] 33 | _ = overlay.waitForExistence(timeout: 10) 34 | 35 | overlay.tap() 36 | overlay.tap() 37 | overlay.tap() 38 | overlay.tap() 39 | overlay.tap() 40 | 41 | app.navigationBars["Default"].buttons["Instructions"].tap() 42 | } 43 | 44 | func testTapOnSkip() { 45 | let app = XCUIApplication() 46 | app.tables.staticTexts["Default"].tap() 47 | 48 | let skipButton = app.buttons["AccessibilityIdentifiers.skipButton"] 49 | _ = skipButton.waitForExistence(timeout: 10) 50 | skipButton.tap() 51 | 52 | app.navigationBars["Default"].buttons["Instructions"].tap() 53 | } 54 | 55 | func testMixedTaps() { 56 | let app = XCUIApplication() 57 | app.tables.staticTexts["Default"].tap() 58 | 59 | let overlay = app.otherElements["AccessibilityIdentifiers.overlayView"] 60 | let coachMark = app.otherElements["AccessibilityIdentifiers.coachMarkBody"] 61 | 62 | _ = overlay.waitForExistence(timeout: 10) 63 | _ = coachMark.waitForExistence(timeout: 10) 64 | 65 | coachMark.tap() 66 | coachMark.tap() 67 | overlay.tap() 68 | 69 | let skipButton = app.buttons["AccessibilityIdentifiers.skipButton"] 70 | skipButton.tap() 71 | 72 | app.navigationBars["Default"].buttons["Instructions"].tap() 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /Examples/Example/UI Tests/Extensions/XCUIApplication+Launch.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present Frédéric Maquin and contributors. 2 | // Licensed under the terms of the MIT License. 3 | 4 | import XCTest 5 | 6 | extension XCUIApplication { 7 | func launchWithAnimationsDisabled() { 8 | launchArguments.append("--UITests") 9 | launch() 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Examples/Example/UI Tests/Supporting Files/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 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 | -------------------------------------------------------------------------------- /Examples/Example/UI Tests/TransitionExampleTests.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018-present Frédéric Maquin and contributors. 2 | // Licensed under the terms of the MIT License. 3 | 4 | import XCTest 5 | 6 | class TransitionExampleTests: XCTestCase { 7 | #if targetEnvironment(macCatalyst) 8 | #else 9 | let originalOrientation = XCUIDevice.shared.orientation 10 | #endif 11 | override func setUp() { 12 | super.setUp() 13 | continueAfterFailure = false 14 | 15 | XCUIApplication().launchWithAnimationsDisabled() 16 | #if targetEnvironment(macCatalyst) 17 | #else 18 | XCUIDevice.shared.orientation = .portrait 19 | #endif 20 | } 21 | 22 | override func tearDown() { 23 | super.tearDown() 24 | #if targetEnvironment(macCatalyst) 25 | #else 26 | XCUIDevice.shared.orientation = originalOrientation 27 | #endif 28 | } 29 | 30 | func testTapThroughCutout() { 31 | let app = XCUIApplication() 32 | app.tables.staticTexts["Transitioning from code"].tap() 33 | 34 | let overlay = app.otherElements["AccessibilityIdentifiers.overlayView"] 35 | _ = overlay.waitForExistence(timeout: 10) 36 | 37 | let defaultCoordinates = app.coordinate(withNormalizedOffset: CGVector(dx: 0.1, dy: 0.1)) 38 | 39 | defaultCoordinates.tap() 40 | defaultCoordinates.tap() 41 | 42 | // One extra tap, to make sure touch is disabled at this point. 43 | defaultCoordinates.tap() 44 | 45 | let cutoutCoordinates = app.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.5)) 46 | cutoutCoordinates.tap() 47 | 48 | defaultCoordinates.tap() 49 | defaultCoordinates.tap() 50 | defaultCoordinates.tap() 51 | 52 | app.navigationBars["Transition From Code"].buttons["Instructions"].tap() 53 | } 54 | 55 | func testTapThroughOverlay() { 56 | let app = XCUIApplication() 57 | app.tables.staticTexts["Transitioning from code"].tap() 58 | 59 | let overlay = app.otherElements["AccessibilityIdentifiers.overlayView"] 60 | _ = overlay.waitForExistence(timeout: 10) 61 | 62 | overlay.tap() 63 | 64 | let vector = CGVector(dx: 0.8, dy: 0.8) 65 | let coordinate = overlay.coordinate(withNormalizedOffset: vector) 66 | coordinate.tap() 67 | 68 | overlay.tap() 69 | overlay.tap() 70 | 71 | if app.navigationBars["Transition From Code"].buttons["Instructions"].isHittable { 72 | XCTFail("Back button should not be hittable.") 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /Examples/Example/Unit Tests/Helpers/CoachMarkInnerLayoutHelperTests.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016-present Frédéric Maquin and contributors. 2 | // Licensed under the terms of the MIT License. 3 | 4 | import XCTest 5 | @testable import Instructions 6 | 7 | // Since we are not going to test static constraints definitions 8 | // this tests will only make sure that the constraints returned are 9 | // not empty. 10 | class CoachMarkInnerLayoutHelperTests: XCTestCase { 11 | 12 | let layoutHelper = CoachMarkInnerLayoutHelper() 13 | 14 | override func setUp() { 15 | super.setUp() 16 | } 17 | 18 | override func tearDown() { 19 | super.tearDown() 20 | } 21 | 22 | func testThatVerticalConstraintArrayIsNotEmpty() { 23 | let parent = UIView() 24 | let body = UIView() 25 | parent.addSubview(body) 26 | 27 | let constraints = 28 | layoutHelper.verticalConstraints(for: (bodyView: UIView(), arrowView: UIView()), 29 | in: UIView(), 30 | withProperties: (orientation: .bottom, 31 | verticalArrowOffset: 0)) 32 | 33 | XCTAssertFalse(constraints.isEmpty) 34 | } 35 | 36 | func testThatVerticalConstraintArraysAreDifferentDependingOnParameters() { 37 | let parent = UIView() 38 | let body = UIView() 39 | parent.addSubview(body) 40 | 41 | let constraints1 = 42 | layoutHelper.verticalConstraints(for: (bodyView: UIView(), arrowView: UIView()), 43 | in: UIView(), 44 | withProperties: (orientation: .bottom, 45 | verticalArrowOffset: 0)) 46 | 47 | let constraints2 = 48 | layoutHelper.verticalConstraints(for: (bodyView: UIView(), arrowView: UIView()), 49 | in: UIView(), 50 | withProperties: (orientation: .top, 51 | verticalArrowOffset: 0)) 52 | 53 | XCTAssertFalse(constraints1 == constraints2) 54 | } 55 | 56 | // func testThatHorizontalArrowConstraintsAreReturned() { 57 | // let constraint = 58 | // layoutHelper.horizontalArrowConstraints(for: (bodyView: UIView(), arrowView: UIView()), 59 | // withPosition: .center, 60 | // horizontalOffset: 0) 61 | // 62 | // XCTAssertTrue(constraint != nil) 63 | // } 64 | } 65 | -------------------------------------------------------------------------------- /Examples/Example/Unit Tests/Managers/Internal/CoachMarkDisplayManagerTests.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016-present Frédéric Maquin and contributors. 2 | // Licensed under the terms of the MIT License. 3 | 4 | import XCTest 5 | @testable import Instructions 6 | 7 | class CoachMarkDisplayManagerTests: XCTestCase { 8 | 9 | let overlayView = OverlayView() 10 | let overlay = OverlayManager() 11 | let instructionsRootView = UIView() 12 | let coachMarksController = CoachMarksController() 13 | var coachMarkDisplayManager: CoachMarkDisplayManager! 14 | 15 | var viewIsVisibleExpectation: XCTestExpectation? 16 | 17 | override func setUp() { 18 | super.setUp() 19 | 20 | self.overlayView.frame = CGRect(x: 0, y: 0, width: 365, height: 667) 21 | self.instructionsRootView.frame = CGRect(x: 0, y: 0, width: 365, height: 667) 22 | 23 | instructionsRootView.addSubview(overlayView) 24 | 25 | overlay.overlayView = overlayView 26 | 27 | coachMarkDisplayManager = 28 | CoachMarkDisplayManager(coachMarkLayoutHelper: CoachMarkLayoutHelper()) 29 | coachMarkDisplayManager.overlayManager = overlay 30 | } 31 | 32 | override func tearDown() { 33 | super.tearDown() 34 | viewIsVisibleExpectation = nil 35 | } 36 | 37 | func testThatCoachMarkViewIsShown() { 38 | let coachMarkView = CoachMarkView(bodyView: CoachMarkBodyDefaultView(), 39 | coachMarkInnerLayoutHelper: CoachMarkInnerLayoutHelper()) 40 | var coachMark = CoachMark() 41 | coachMark.cutoutPath = UIBezierPath(rect: CGRect(x: 30, y: 30, width: 60, height: 30)) 42 | 43 | self.viewIsVisibleExpectation = self.expectation(description: "viewIsVisible") 44 | 45 | coachMark.computeOrientation(inFrame: self.instructionsRootView.frame) 46 | coachMark.computePointOfInterest() 47 | 48 | self.coachMarkDisplayManager.showNew(coachMarkView: coachMarkView, from: coachMark, 49 | at: 0) { 50 | XCTAssertEqual(coachMarkView.alpha, 1.0) 51 | XCTAssertEqual(coachMarkView.isHidden, false) 52 | 53 | self.viewIsVisibleExpectation?.fulfill() 54 | } 55 | 56 | self.waitForExpectations(timeout: 10) { error in 57 | if let error = error { 58 | print("Error: \(error.localizedDescription)") 59 | } 60 | } 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /Examples/Example/Unit Tests/Managers/Internal/OverlayManagerTests.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018-present Frédéric Maquin and contributors. 2 | // Licensed under the terms of the MIT License. 3 | 4 | import XCTest 5 | @testable import Instructions 6 | 7 | class OverlayManagerTests: XCTestCase { 8 | 9 | var manager: OverlayManager! 10 | 11 | override func setUp() { 12 | manager = OverlayManager() 13 | } 14 | 15 | func testThatTapIsRegistered() { 16 | XCTAssertTrue(manager.overlayView.gestureRecognizers?.isEmpty ?? true) 17 | manager.isUserInteractionEnabled = true 18 | XCTAssertFalse(manager.overlayView.gestureRecognizers?.isEmpty ?? true) 19 | } 20 | 21 | func testPropertyTransfer() { 22 | let cutoutPath = UIBezierPath(rect: CGRect(x: 20, y: 20, width: 30, height: 30)) 23 | 24 | manager.isUserInteractionEnabledInsideCutoutPath = true 25 | manager.cutoutPath = cutoutPath 26 | 27 | XCTAssertTrue(manager.overlayView.allowTouchInsideCutoutPath) 28 | XCTAssertEqual(manager.overlayView.cutoutPath, cutoutPath) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Examples/Example/Unit Tests/Managers/Internal/OverlayStyleManager/BlurringOverlayStyleManagerTest.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017-present Frédéric Maquin and contributors. 2 | // Licensed under the terms of the MIT License. 3 | 4 | import XCTest 5 | @testable import Instructions 6 | 7 | class BlurringOverlayStyleManagerTest: XCTestCase { 8 | 9 | private let overlayView = OverlayView() 10 | private let blurringStyleManager = BlurringOverlayStyleManager(style: .extraLight) 11 | private let snapshotDelegate = SnapshotDelegate() // swiftlint:disable:this weak_delegate 12 | 13 | var viewIsVisibleExpectation: XCTestExpectation? 14 | 15 | override func setUp() { 16 | super.setUp() 17 | overlayView.frame = CGRect(x: 0, y: 0, width: 365, height: 667) 18 | overlayView.alpha = 0.0 19 | 20 | blurringStyleManager.overlayView = overlayView 21 | blurringStyleManager.snapshotDelegate = snapshotDelegate 22 | } 23 | 24 | override func tearDown() { 25 | super.tearDown() 26 | viewIsVisibleExpectation = nil 27 | } 28 | 29 | func testThatOverlayIsShown() { 30 | self.viewIsVisibleExpectation = self.expectation(description: "viewIsVisible") 31 | 32 | blurringStyleManager.showOverlay(true, withDuration: 1.0) { _ in 33 | XCTAssertEqual(self.overlayView.alpha, 1.0) 34 | XCTAssertEqual(self.overlayView.isHidden, false) 35 | 36 | let containsBlur = self.overlayView.holder.subviews 37 | .filter { $0 is UIVisualEffectView } 38 | .count == 2 39 | 40 | XCTAssertTrue(containsBlur) 41 | 42 | self.viewIsVisibleExpectation?.fulfill() 43 | } 44 | 45 | self.waitForExpectations(timeout: 1.1) { error in 46 | if let error = error { 47 | print("Error: \(error.localizedDescription)") 48 | } 49 | } 50 | } 51 | 52 | func testThatOverlayIsHidden() { 53 | viewIsVisibleExpectation = expectation(description: "viewIsVisible") 54 | 55 | blurringStyleManager.showOverlay(false, withDuration: 1.0) { _ in 56 | XCTAssertEqual(self.overlayView.alpha, 0.0) 57 | 58 | XCTAssertTrue(self.overlayView.holder.subviews.count == 1) 59 | 60 | self.viewIsVisibleExpectation?.fulfill() 61 | } 62 | 63 | self.waitForExpectations(timeout: 1.1) { error in 64 | if let error = error { 65 | print("Error: \(error.localizedDescription)") 66 | } 67 | } 68 | } 69 | } 70 | 71 | private class SnapshotDelegate: Snapshottable { 72 | func snapshot() -> UIView? { 73 | return UIView() 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /Examples/Example/Unit Tests/Managers/Internal/OverlayStyleManager/TranslucentOverlayStyleManagerTest.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016-present Frédéric Maquin and contributors. 2 | // Licensed under the terms of the MIT License. 3 | 4 | import XCTest 5 | @testable import Instructions 6 | 7 | class TranslucentOverlayStyleManagerTest: XCTestCase { 8 | 9 | private let overlayView = OverlayView() 10 | private let translucentStyleManager = TranslucentOverlayStyleManager(color: UIColor.gray) 11 | 12 | var viewIsVisibleExpectation: XCTestExpectation? 13 | 14 | override func setUp() { 15 | super.setUp() 16 | overlayView.frame = CGRect(x: 0, y: 0, width: 365, height: 667) 17 | overlayView.alpha = 0.0 18 | 19 | translucentStyleManager.overlayView = overlayView 20 | } 21 | 22 | override func tearDown() { 23 | super.tearDown() 24 | viewIsVisibleExpectation = nil 25 | } 26 | 27 | func testThatOverlayIsShown() { 28 | self.viewIsVisibleExpectation = self.expectation(description: "viewIsVisible") 29 | 30 | translucentStyleManager.showOverlay(true, withDuration: 1.0) { _ in 31 | XCTAssertEqual(self.overlayView.alpha, 1.0) 32 | XCTAssertEqual(self.overlayView.isHidden, false) 33 | XCTAssertEqual(self.overlayView.backgroundColor, UIColor.clear) 34 | XCTAssertEqual(self.overlayView.holder.layer.sublayers?[0].backgroundColor, 35 | UIColor.gray.cgColor) 36 | 37 | self.viewIsVisibleExpectation?.fulfill() 38 | } 39 | 40 | self.waitForExpectations(timeout: 1.1) { error in 41 | if let error = error { 42 | print("Error: \(error.localizedDescription)") 43 | } 44 | } 45 | } 46 | 47 | func testThatOverlayIsHidden() { 48 | viewIsVisibleExpectation = expectation(description: "viewIsVisible") 49 | 50 | translucentStyleManager.showOverlay(false, withDuration: 1.0) { _ in 51 | XCTAssertEqual(self.overlayView.alpha, 0.0) 52 | self.viewIsVisibleExpectation?.fulfill() 53 | } 54 | 55 | self.waitForExpectations(timeout: 1.1) { error in 56 | if let error = error { 57 | print("Error: \(error.localizedDescription)") 58 | } 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Examples/Example/Unit Tests/Protocols/Internal/DataSource/DataSourceBaseTest.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016-present Frédéric Maquin and contributors. 2 | // Licensed under the terms of the MIT License. 3 | 4 | import XCTest 5 | @testable import Instructions 6 | 7 | class DataSourceBaseTest: XCTestCase { 8 | 9 | var coachMarksController = CoachMarksController() 10 | var parentController = UIViewController() 11 | var mockedWindow = UIWindow() 12 | 13 | override func setUp() { 14 | super.setUp() 15 | 16 | UIView.setAnimationsEnabled(false) 17 | self.mockedWindow.addSubview(self.parentController.view) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Examples/Example/Unit Tests/Protocols/Internal/DataSource/DataSourceNumberOfCoachMarksTest.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016-present Frédéric Maquin and contributors. 2 | // Licensed under the terms of the MIT License. 3 | 4 | import XCTest 5 | @testable import Instructions 6 | 7 | class DataSourceNumberOfCoachMarksTest: DataSourceBaseTest, 8 | CoachMarksControllerDataSource { 9 | 10 | var delegateEndExpectation: XCTestExpectation? 11 | 12 | override func setUp() { 13 | super.setUp() 14 | coachMarksController.dataSource = self 15 | } 16 | 17 | override func tearDown() { 18 | super.tearDown() 19 | delegateEndExpectation = nil 20 | } 21 | 22 | func testNumberOfCoachMarksIsCalled() { 23 | delegateEndExpectation = self.expectation(description: "numberOfCoachMarks") 24 | coachMarksController.start(in: .window(over: parentController)) 25 | 26 | waitForExpectations(timeout: 10) { error in 27 | if let error = error { 28 | print("Error: \(error.localizedDescription)") 29 | } 30 | } 31 | } 32 | 33 | func numberOfCoachMarks(for coachMarksController: CoachMarksController) -> Int { 34 | guard let delegateEndExpectation = self.delegateEndExpectation else { 35 | XCTFail("Undefined expectation") 36 | return 0 37 | } 38 | 39 | if delegateEndExpectation.description == "numberOfCoachMarks" { 40 | delegateEndExpectation.fulfill() 41 | } 42 | 43 | return 4 44 | } 45 | 46 | func coachMarksController(_ coachMarksController: CoachMarksController, 47 | coachMarkAt index: Int) -> CoachMark { 48 | return CoachMark() 49 | } 50 | 51 | func coachMarksController( 52 | _ coachMarksController: CoachMarksController, 53 | coachMarkViewsAt index: Int, madeFrom coachMark: CoachMark 54 | ) -> (bodyView: (UIView & CoachMarkBodyView), arrowView: (UIView & CoachMarkArrowView)?) { 55 | return (bodyView: CoachMarkBodyDefaultView(), arrowView: nil) 56 | } 57 | 58 | func coachMarksController(_ coachMarksController: CoachMarksController, 59 | constraintsForSkipView skipView: UIView, 60 | inParent parentView: UIView) -> [NSLayoutConstraint]? { 61 | return nil 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Examples/Example/Unit Tests/Supporting Files/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 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 | -------------------------------------------------------------------------------- /Examples/Example/Unit Tests/Views/MaskViewTests.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018-present Frédéric Maquin and contributors. 2 | // Licensed under the terms of the MIT License. 3 | 4 | import XCTest 5 | @testable import Instructions 6 | 7 | class MaskViewTests: XCTestCase { 8 | func testLayerClass() { 9 | let maskView = MaskView(frame: CGRect(x: 0, y: 0, width: 50, height: 50)) 10 | 11 | XCTAssertTrue(maskView.layer is CAShapeLayer) 12 | XCTAssertEqual(maskView.shapeLayer, maskView.layer) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Examples/Example/Unit Tests/Views/OverlaySnapshotViewTests.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018-present Frédéric Maquin and contributors. 2 | // Licensed under the terms of the MIT License. 3 | 4 | import XCTest 5 | @testable import Instructions 6 | 7 | class OverlaySnapshotViewTests: XCTestCase { 8 | func testHitTest() { 9 | let frame = CGRect(x: 0, y: 0, width: 50, height: 70) 10 | let overlayView = OverlaySnapshotView(frame: frame) 11 | 12 | XCTAssertEqual(overlayView.hitTest(CGPoint(x: 25, y: 25), with: nil), nil) 13 | XCTAssertEqual(overlayView.hitTest(CGPoint(x: 150, y: 150), with: nil), nil) 14 | } 15 | 16 | func testSubviews() { 17 | let frame = CGRect(x: 0, y: 0, width: 50, height: 70) 18 | let overlayView = OverlaySnapshotView(frame: frame) 19 | let overlayBackgroundView = UIView(frame: frame) 20 | let overlayVisualEffectView = UIVisualEffectView(frame: frame) 21 | 22 | overlayView.backgroundView = overlayBackgroundView 23 | overlayView.visualEffectView = overlayVisualEffectView 24 | 25 | XCTAssertEqual(overlayView.backgroundView.superview, overlayView) 26 | XCTAssertEqual(overlayView.visualEffectView.superview, overlayView) 27 | 28 | overlayView.backgroundView = nil 29 | overlayView.visualEffectView = nil 30 | 31 | XCTAssertEqual(overlayBackgroundView.superview, nil) 32 | XCTAssertEqual(overlayVisualEffectView.superview, nil) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Examples/Example/Unit Tests/Views/OverlayViewTests.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018-present Frédéric Maquin and contributors. 2 | // Licensed under the terms of the MIT License. 3 | 4 | import XCTest 5 | @testable import Instructions 6 | 7 | class OverlayViewTests: XCTestCase { 8 | func testCutoutPathHitTestTest() { 9 | let overlayView = OverlayView() 10 | overlayView.frame = CGRect(x: 0, y: 0, width: 100, height: 100) 11 | 12 | XCTAssertEqual(overlayView.hitTest(CGPoint(x: 25, y: 25), with: nil), overlayView) 13 | XCTAssertEqual(overlayView.hitTest(CGPoint(x: 90, y: 90), with: nil), overlayView) 14 | 15 | overlayView.cutoutPath = UIBezierPath(rect: CGRect(x: 20, y: 20, width: 30, height: 30)) 16 | 17 | XCTAssertEqual(overlayView.hitTest(CGPoint(x: 25, y: 25), with: nil), overlayView) 18 | XCTAssertEqual(overlayView.hitTest(CGPoint(x: 90, y: 90), with: nil), overlayView) 19 | 20 | overlayView.allowTouchInsideCutoutPath = true 21 | 22 | XCTAssertEqual(overlayView.hitTest(CGPoint(x: 25, y: 25), with: nil), nil) 23 | XCTAssertEqual(overlayView.hitTest(CGPoint(x: 90, y: 90), with: nil), overlayView) 24 | XCTAssertEqual(overlayView.hitTest(CGPoint(x: 150, y: 150), with: nil), nil) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Examples/Example/Unit Tests/Views/PassthroughGenericTests.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018-present Frédéric Maquin and contributors. 2 | // Licensed under the terms of the MIT License. 3 | 4 | import XCTest 5 | @testable import Instructions 6 | 7 | class PassthroughGenericTests: XCTestCase { 8 | func testHitTestOnRootView() { 9 | let frame = CGRect(x: 0, y: 0, width: 50, height: 70) 10 | let rootView = InstructionsRootView(frame: frame) 11 | let passThroughRootView = InstructionsRootView(frame: frame) 12 | 13 | passThroughRootView.passthrough = true 14 | 15 | XCTAssertEqual(rootView.hitTest(CGPoint(x: 25, y: 25), with: nil), rootView) 16 | XCTAssertEqual(passThroughRootView.hitTest(CGPoint(x: 25, y: 25), with: nil), nil) 17 | 18 | XCTAssertEqual(rootView.hitTest(CGPoint(x: 150, y: 150), with: nil), nil) 19 | XCTAssertEqual(passThroughRootView.hitTest(CGPoint(x: 150, y: 150), with: nil), nil) 20 | } 21 | 22 | func testPassthroughHitTestOnWindow() { 23 | let frame = CGRect(x: 0, y: 0, width: 50, height: 70) 24 | let window = InstructionsWindow(frame: frame) 25 | window.isHidden = false 26 | 27 | XCTAssertEqual(window.hitTest(CGPoint(x: 25, y: 25), with: nil), nil) 28 | XCTAssertEqual(window.hitTest(CGPoint(x: 150, y: 150), with: nil), nil) 29 | } 30 | 31 | func testPassthroughHitTestOnPassthroughView() { 32 | let frame = CGRect(x: 0, y: 0, width: 50, height: 70) 33 | let view = PassthroughView(frame: frame) 34 | 35 | XCTAssertEqual(view.hitTest(CGPoint(x: 25, y: 25), with: nil), nil) 36 | XCTAssertEqual(view.hitTest(CGPoint(x: 150, y: 150), with: nil), nil) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Examples/Instructions App Extensions Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Examples/InstructionsExample.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.network.client 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Examples/Snapshot.xctestplan: -------------------------------------------------------------------------------- 1 | { 2 | "configurations" : [ 3 | { 4 | "id" : "FB93C8F4-28D4-472C-8B4D-1C2BF5034923", 5 | "name" : "Test Snapshots", 6 | "options" : { 7 | 8 | } 9 | }, 10 | { 11 | "id" : "C4D9A363-5498-42F4-BE65-B4BB4DEF2C2D", 12 | "name" : "Generate Snapshots", 13 | "options" : { 14 | "environmentVariableEntries" : [ 15 | { 16 | "key" : "RECORD_MODE", 17 | "value" : "true" 18 | }, 19 | { 20 | "key" : "FB_REFERENCE_IMAGE_DIR", 21 | "value" : "$(SRCROOT)\/Example\/Snapshot Tests\/Supporting Files\/Snapshots\/ReferenceImages" 22 | }, 23 | { 24 | "key" : "IMAGE_DIFF_DIR", 25 | "value" : "$(SRCROOT)\/Example\/Snapshot Tests\/Supporting Files\/Snapshots\/FailureDiffs" 26 | } 27 | ] 28 | } 29 | } 30 | ], 31 | "defaultOptions" : { 32 | "environmentVariableEntries" : [ 33 | { 34 | "key" : "FB_REFERENCE_IMAGE_DIR", 35 | "value" : "$(SRCROOT)\/Example\/Snapshot Tests\/Supporting Files\/Snapshots\/ReferenceImages" 36 | }, 37 | { 38 | "key" : "IMAGE_DIFF_DIR", 39 | "value" : "$(SRCROOT)\/Example\/Snapshot Tests\/Supporting Files\/Snapshots\/FailureDiffs" 40 | } 41 | ], 42 | "targetForVariableExpansion" : { 43 | "containerPath" : "container:Instructions Example.xcodeproj", 44 | "identifier" : "B639B28020D4E71D009996CF", 45 | "name" : "InstructionsSnapshotTests" 46 | } 47 | }, 48 | "testTargets" : [ 49 | { 50 | "parallelizable" : true, 51 | "target" : { 52 | "containerPath" : "container:Instructions Example.xcodeproj", 53 | "identifier" : "B64131082737EE3C006F6D8D", 54 | "name" : "InstructionsSnapshotTests" 55 | } 56 | } 57 | ], 58 | "version" : 1 59 | } 60 | -------------------------------------------------------------------------------- /Examples/Unit+UI.xctestplan: -------------------------------------------------------------------------------- 1 | { 2 | "configurations" : [ 3 | { 4 | "id" : "FB93C8F4-28D4-472C-8B4D-1C2BF5034923", 5 | "name" : "Default", 6 | "options" : { 7 | "environmentVariableEntries" : [ 8 | 9 | ] 10 | } 11 | } 12 | ], 13 | "defaultOptions" : { 14 | "environmentVariableEntries" : [ 15 | 16 | ], 17 | "targetForVariableExpansion" : { 18 | "containerPath" : "container:Instructions Example.xcodeproj", 19 | "identifier" : "B639B28020D4E71D009996CF", 20 | "name" : "InstructionsSnapshotTests" 21 | } 22 | }, 23 | "testTargets" : [ 24 | { 25 | "parallelizable" : true, 26 | "target" : { 27 | "containerPath" : "container:Instructions Example.xcodeproj", 28 | "identifier" : "B6CA4A8320BE7141006F42AC", 29 | "name" : "InstructionsExampleUITests" 30 | } 31 | }, 32 | { 33 | "parallelizable" : true, 34 | "target" : { 35 | "containerPath" : "container:Instructions Example.xcodeproj", 36 | "identifier" : "B6C9D64C20D3B7FC00EBC03A", 37 | "name" : "InstructionsUnitTests" 38 | } 39 | } 40 | ], 41 | "version" : 1 42 | } 43 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | source "https://rubygems.org" 4 | 5 | git_source(:github) { |repo_name| "https://github.com/#{repo_name}" } 6 | 7 | gem "fastlane" 8 | gem 'slather' 9 | -------------------------------------------------------------------------------- /Instructions.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |spec| 2 | spec.cocoapods_version = '>= 1.0' 3 | 4 | spec.name = "Instructions" 5 | spec.version = "2.3.0" 6 | spec.summary = "Create walkthroughs and guided tours (with coach marks) in a simple way, using Swift." 7 | spec.homepage = "https://github.com/ephread/Instructions" 8 | spec.license = 'MIT' 9 | spec.author = { "Frédéric Maquin" => "fred@ephread.com" } 10 | spec.source = { :git => "https://github.com/ephread/Instructions.git", :tag => spec.version.to_s } 11 | 12 | spec.swift_version = '5.0' 13 | spec.platform = :ios, '9.0' 14 | spec.requires_arc = true 15 | 16 | spec.source_files = 'Instructions', 'Sources/**/*.swift' 17 | spec.exclude_files = '**/*+AppExtensions.swift' 18 | end 19 | -------------------------------------------------------------------------------- /Instructions.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Instructions.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Instructions.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 12 | 13 | 16 | 18 | 19 | 21 | 22 | 23 | 26 | 29 | 31 | 32 | 33 | 35 | 36 | 38 | 39 | 41 | 42 | 44 | 45 | 46 | 49 | 51 | 52 | 54 | 55 | 57 | 58 | 59 | 61 | 62 | 64 | 65 | 67 | 68 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /Instructions.xcworkspace/xcshareddata/IDETemplateMacros.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | FILEHEADER 6 | Copyright (c) ___YEAR___-present Frédéric Maquin <fred@ephread.com> and contributors. 7 | // Licensed under the terms of the MIT License. 8 | 9 | 10 | -------------------------------------------------------------------------------- /Instructions.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Instructions.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Instructions.xcworkspace/xcshareddata/swiftpm/Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "pins" : [ 3 | { 4 | "identity" : "devicekit", 5 | "kind" : "remoteSourceControl", 6 | "location" : "https://github.com/devicekit/DeviceKit", 7 | "state" : { 8 | "revision" : "691fe8112cca20ebf0020a1709d4e0205400311c", 9 | "version" : "5.0.0" 10 | } 11 | }, 12 | { 13 | "identity" : "ios-snapshot-test-case", 14 | "kind" : "remoteSourceControl", 15 | "location" : "https://github.com/uber/ios-snapshot-test-case", 16 | "state" : { 17 | "revision" : "7b10770333a961be6e5a41c9ce04b8c6d3990126", 18 | "version" : "8.0.0" 19 | } 20 | } 21 | ], 22 | "version" : 2 23 | } 24 | -------------------------------------------------------------------------------- /InstructionsAppExtensions.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |spec| 2 | spec.cocoapods_version = '>= 1.0' 3 | 4 | spec.name = "InstructionsAppExtensions" 5 | spec.version = "2.3.0" 6 | spec.summary = "[App Extensions Version] Create walkthroughs and guided tours (with coach marks) in a simple way, using Swift." 7 | spec.homepage = "https://github.com/ephread/Instructions" 8 | spec.license = 'MIT' 9 | spec.author = { "Frédéric Maquin" => "fred@ephread.com" } 10 | spec.source = { :git => "https://github.com/ephread/Instructions.git", :tag => spec.version.to_s } 11 | 12 | spec.swift_version = '5.0' 13 | spec.platform = :ios, '9.0' 14 | spec.requires_arc = true 15 | 16 | spec.source_files = 'Instructions', 'Sources/**/*.swift' 17 | spec.exclude_files = '**/*+Regular.swift' 18 | spec.pod_target_xcconfig = {'OTHER_SWIFT_FLAGS' => '-DINSTRUCTIONS_APP_EXTENSIONS'} 19 | end 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015-present Frédéric Maquin and contributors. 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 13 | all 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 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.2 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | import PackageDescription 5 | 6 | let package = Package( 7 | name: "Instructions", 8 | platforms: [ 9 | .iOS(.v9) 10 | ], 11 | products: [ 12 | // Products define the executables and libraries produced by a package, and make them visible to other packages. 13 | .library( 14 | name: "Instructions", 15 | targets: ["Instructions"]), 16 | ], 17 | dependencies: [ 18 | // Dependencies declare other packages that this package depends on. 19 | // .package(url: /* package url */, from: "1.0.0"), 20 | ], 21 | targets: [ 22 | // Targets are the basic building blocks of a package. A target can define a module or a test suite. 23 | // Targets can depend on other targets in this package, and on products in packages which this package depends on. 24 | .target( 25 | name: "Instructions", 26 | dependencies: []), 27 | ] 28 | ) 29 | -------------------------------------------------------------------------------- /Sources/Instructions/Extra/Default Views/CoachMarkBackgroundStyle.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present Frédéric Maquin and contributors. 2 | // Licensed under the terms of the MIT License. 3 | 4 | import UIKit 5 | 6 | public protocol CoachMarkBackgroundStyle { 7 | var innerColor: UIColor { get set } 8 | var borderColor: UIColor { get set } 9 | var highlightedInnerColor: UIColor { get set } 10 | var highlightedBorderColor: UIColor { get set } 11 | } 12 | 13 | public protocol CoachMarkBodyBackgroundStyle: AnyObject, CoachMarkBackgroundStyle { 14 | var isHighlighted: Bool { get set } 15 | var cornerRadius: CGFloat { get set } 16 | } 17 | -------------------------------------------------------------------------------- /Sources/Instructions/Extra/Default Views/CoachMarkSkipDefaultView.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-present Frédéric Maquin and contributors. 2 | // Licensed under the terms of the MIT License. 3 | 4 | import UIKit 5 | 6 | /// A concrete implementation of the coach mark skip view and the 7 | /// default one provided by the library. 8 | public class CoachMarkSkipDefaultView: UIButton, CoachMarkSkipView { 9 | // MARK: Public properties 10 | public var skipControl: UIControl? { 11 | return self 12 | } 13 | 14 | public override var isHighlighted: Bool { 15 | didSet { 16 | bodyBackground.isHighlighted = isHighlighted 17 | } 18 | } 19 | 20 | public var background: CoachMarkBodyBackgroundStyle { return bodyBackground } 21 | public var isStyledByInstructions = true { 22 | didSet { 23 | bodyBackground.isHidden = !isStyledByInstructions 24 | } 25 | } 26 | 27 | // MARK: Private properties 28 | private var bodyBackground = CoachMarkBodyBackgroundView() 29 | 30 | // MARK: Initialization 31 | public override init(frame: CGRect) { 32 | super.init(frame: frame) 33 | accessibilityIdentifier = AccessibilityIdentifiers.skipButton 34 | } 35 | 36 | public convenience init() { 37 | self.init(frame: CGRect.zero) 38 | 39 | setTitleColor(InstructionsColor.coachMarkLabel, for: .normal) 40 | titleLabel?.font = UIFont.systemFont(ofSize: 17.0) 41 | titleLabel?.textAlignment = .center 42 | 43 | bodyBackground.translatesAutoresizingMaskIntoConstraints = false 44 | bodyBackground.isUserInteractionEnabled = false 45 | 46 | addSubview(bodyBackground) 47 | sendSubviewToBack(bodyBackground) 48 | bodyBackground.fillSuperview() 49 | 50 | if #available(iOS 15, *) { 51 | var config = UIButton.Configuration.plain() 52 | config.contentInsets = NSDirectionalEdgeInsets(top: 10.0, leading: 15.0, bottom: 10.0, trailing: 15.0) 53 | self.configuration = config 54 | } else { 55 | contentEdgeInsets = UIEdgeInsets(top: 10.0, left: 15.0, bottom: 10.0, right: 15.0) 56 | } 57 | sizeToFit() 58 | } 59 | 60 | required public init?(coder aDecoder: NSCoder) { 61 | fatalError(ErrorMessage.Fatal.doesNotSupportNSCoding) 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Sources/Instructions/Extra/Internal/CoachMarkComponent.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present Frédéric Maquin and contributors. 2 | // Licensed under the terms of the MIT License. 3 | 4 | import UIKit 5 | 6 | protocol CoachMarkComponent { 7 | var frame: CGRect { get set } 8 | } 9 | 10 | extension CoachMarkComponent { 11 | func makeInnerTrianglePath(orientation: CoachMarkArrowOrientation) -> CGPath { 12 | let path = CGMutablePath() 13 | 14 | if orientation == .top { 15 | path.move(to: CGPoint(x: 0, y: frame.height)) 16 | path.addLine(to: CGPoint(x: frame.width / 2, y: 1)) 17 | path.addLine(to: CGPoint(x: frame.width, y: frame.height)) 18 | } else { 19 | path.move(to: CGPoint(x: 0, y: 0)) 20 | path.addLine(to: CGPoint(x: frame.width / 2, y: frame.height - 1)) 21 | path.addLine(to: CGPoint(x: frame.width, y: 0)) 22 | } 23 | 24 | path.closeSubpath() 25 | 26 | return path 27 | } 28 | 29 | func makeOuterTrianglePath(orientation: CoachMarkArrowOrientation) -> CGPath { 30 | let path = CGMutablePath() 31 | 32 | if orientation == .top { 33 | path.move(to: CGPoint(x: 0, y: frame.height - 1)) 34 | path.addLine(to: CGPoint(x: frame.width / 2, y: 0)) 35 | path.addLine(to: CGPoint(x: frame.width, y: frame.height - 1)) 36 | } else { 37 | path.move(to: CGPoint(x: 0, y: 1)) 38 | path.addLine(to: CGPoint(x: frame.width / 2, y: frame.height)) 39 | path.addLine(to: CGPoint(x: frame.width, y: 1)) 40 | } 41 | 42 | path.closeSubpath() 43 | 44 | return path 45 | } 46 | 47 | func makeInnerRoundedPath(cornerRadius: CGFloat) -> CGPath { 48 | let rect = frame.inset(by: UIEdgeInsets(top: 1, left: 1, bottom: 2, right: 1)) 49 | let bezierPath = UIBezierPath(roundedRect: rect, cornerRadius: cornerRadius) 50 | return bezierPath.cgPath 51 | } 52 | 53 | func makeOuterRoundedPath(cornerRadius: CGFloat) -> CGPath { 54 | let bezierPath = UIBezierPath(roundedRect: frame, cornerRadius: cornerRadius) 55 | return bezierPath.cgPath 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Sources/Instructions/Helpers/Internal/AccessibilityIdentifiers.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018-present Frédéric Maquin and contributors. 2 | // Licensed under the terms of the MIT License. 3 | 4 | import Foundation 5 | 6 | struct AccessibilityIdentifiers { 7 | static let coachMarkNext = "AccessibilityIdentifiers.coachMarkNext" 8 | static let coachMarkHint = "AccessibilityIdentifiers.coachMarkHint" 9 | static let coachMarkBody = "AccessibilityIdentifiers.coachMarkBody" 10 | 11 | static let skipButton = "AccessibilityIdentifiers.skipButton" 12 | static let overlayView = "AccessibilityIdentifiers.overlayView" 13 | 14 | static let window = "AccessibilityIdentifiers.window" 15 | } 16 | -------------------------------------------------------------------------------- /Sources/Instructions/Helpers/Internal/Enums/CoachMarkPosition.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-present Frédéric Maquin and contributors. 2 | // Licensed under the terms of the MIT License. 3 | 4 | import UIKit 5 | 6 | /// Define the horizontal position of the coach mark. 7 | enum CoachMarkPosition { 8 | var layoutAttribute: NSLayoutConstraint.Attribute { 9 | switch self { 10 | case .leading: return .leading 11 | case .center: return .centerX 12 | case .trailing: return .trailing 13 | } 14 | } 15 | 16 | case leading 17 | case center 18 | case trailing 19 | } 20 | 21 | /// Define the horizontal position of the arrow. 22 | typealias ArrowPosition = CoachMarkPosition 23 | -------------------------------------------------------------------------------- /Sources/Instructions/Helpers/Internal/Extensions/UIApplication+WindowScene.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-present Frédéric Maquin and contributors. 2 | // Licensed under the terms of the MIT License. 3 | 4 | import UIKit 5 | 6 | @available(iOS 13.0, *) 7 | extension UIApplication { 8 | var activeScene: UIWindowScene? { 9 | let scene = connectedScenes.filter { $0.activationState == .foregroundActive }.first 10 | return scene as? UIWindowScene 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Sources/Instructions/Helpers/Public/CoachMark Animation/CoachMarkAnimationManagement.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018-present Frédéric Maquin and contributors. 2 | // Licensed under the terms of the MIT License. 3 | 4 | import UIKit 5 | 6 | // MARK: Main protocols 7 | /// Define context in which the transition will execute. Note that here, what "context" means 8 | /// really is "animation parameters" and "coach mark metadata". 9 | public protocol CoachMarkAnimationManagementContext { 10 | var coachMark: CoachMark { get } 11 | var parameters: AnimationParameters { get } 12 | } 13 | 14 | /// Common ground for coach mark animation managers. 15 | protocol CoachMarkAnimationManagement: CoachMarkAnimationManagementContext { 16 | var animations: ((CoachMarkAnimationManagementContext) -> Void)? { get } 17 | var completion: ((Bool) -> Void)? { get } 18 | var animationType: AnimationType { get } 19 | } 20 | 21 | // MARK: Enums 22 | public enum AnimationType { 23 | case regular, keyframe 24 | } 25 | 26 | // MARK: Structs 27 | /// Hold the parameters which will eventually be passed to `UIView.animate`. 28 | public struct AnimationParameters { 29 | /// The total duration of the animations, measured in seconds. 30 | /// If you specify a negative value or 0, the changes are made without animating them. 31 | public var duration: TimeInterval = Constants.coachMarkFadeAnimationDuration 32 | 33 | /// The amount of time (measured in seconds) to wait before beginning the animations. 34 | /// Specify a value of 0 to begin the animations immediately. 35 | public var delay: TimeInterval = 0 36 | 37 | /// A mask of options indicating how you want to perform the animations. 38 | /// For a list of valid constants, see UIView.AnimationOptions. 39 | /// The value of this property will only be used for regular animations. 40 | public var options: UIView.AnimationOptions = [] 41 | 42 | /// A mask of options indicating how you want to perform the animations. 43 | /// For a list of valid constants, see UIView.KeyframeAnimationOptions. 44 | /// The value of this property will only be used for keyframe animations. 45 | public var keyframeOptions: UIView.KeyframeAnimationOptions = [] 46 | } 47 | 48 | internal struct CoachMarkAnimationManagerContext: CoachMarkAnimationManagementContext { 49 | let coachMark: CoachMark 50 | let parameters: AnimationParameters 51 | } 52 | -------------------------------------------------------------------------------- /Sources/Instructions/Helpers/Public/CoachMark Animation/CoachMarkAnimationManager.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018-present Frédéric Maquin and contributors. 2 | // Licensed under the terms of the MIT License. 3 | 4 | import Foundation 5 | 6 | /// An object provided through the animation delegate methods, 7 | /// so that you can customize the idle animation of the given 8 | /// coach mark. You should set animation parameters through the 9 | /// `parameters` property first and then, call `animate`. 10 | public class CoachMarkAnimationManager: CoachMarkAnimationManagement { 11 | // MARK: Internal properties 12 | /// Used to discriminate between regular animations and keyframe-based animations. 13 | internal var animationType: AnimationType = .regular 14 | 15 | /// Animation block 16 | internal var animations: ((CoachMarkAnimationManagementContext) -> Void)? 17 | 18 | /// Completion block 19 | internal var completion: ((Bool) -> Void)? 20 | 21 | // MARK: Public properties 22 | public let coachMark: CoachMark 23 | public var parameters = AnimationParameters() 24 | 25 | // MARK: Lifecycle 26 | init(coachMark: CoachMark) { 27 | self.coachMark = coachMark 28 | } 29 | 30 | // MARK: Public methods 31 | /// Use this method to register the animations which will be used while coach marks will be 32 | /// displayed. A `CoachMarkAnimationManagementContext` will be passed to the animation block 33 | /// so that you will be able to access the animation parameters and the related coach mark. 34 | /// Use this object rather than capturing a reference to `CoachMarkAnimationManager`. 35 | /// 36 | /// - Parameters: 37 | /// - type: type of the animation 38 | /// - animations: animation block 39 | /// - completion: completion block 40 | public func animate(_ type: AnimationType, 41 | animations: @escaping (CoachMarkAnimationManagementContext) -> Void, 42 | completion: ((Bool) -> Void)? = nil) { 43 | self.animations = animations 44 | self.completion = completion 45 | 46 | animationType = type 47 | } 48 | 49 | // MARK: Internal methods 50 | /// Create the context from the current parameters and coach mark. 51 | /// 52 | /// - Returns: new context object. 53 | internal func createContext() -> CoachMarkAnimationManagementContext { 54 | return CoachMarkAnimationManagerContext(coachMark: coachMark, 55 | parameters: parameters) 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Sources/Instructions/Helpers/Public/CoachMark Animation/CoachMarkTransitionManager.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018-present Frédéric Maquin and contributors. 2 | // Licensed under the terms of the MIT License. 3 | 4 | import Foundation 5 | 6 | /// An object provided through the animation delegate methods, 7 | /// so that you can customize the appearance transitions of the given 8 | /// coach mark. You should set animation parameters through the 9 | /// `parameters` property first and then, call `animate`. 10 | public class CoachMarkTransitionManager: CoachMarkAnimationManagement { 11 | // MARK: Internal properties 12 | /// Used to discriminate between regular animations and keyframe-based animations. 13 | internal var animationType: AnimationType = .regular 14 | 15 | /// Block run before the animation block to setup any initial state required 16 | /// by the animation. 17 | internal var initialState: (() -> Void)? 18 | 19 | /// Animation block 20 | internal var animations: ((CoachMarkAnimationManagementContext) -> Void)? 21 | 22 | /// Completion block 23 | internal var completion: ((Bool) -> Void)? 24 | 25 | // MARK: Public properties 26 | public let coachMark: CoachMark 27 | public var parameters = AnimationParameters() 28 | 29 | // MARK: Lifecycle 30 | init(coachMark: CoachMark) { 31 | self.coachMark = coachMark 32 | } 33 | 34 | // MARK: Public methods 35 | /// Use this method to register the animations which will be used to show/hide the 36 | /// coach mark. A `CoachMarkAnimationManagementContext` will be passed to the 37 | /// animation block so that you will be able to access the animation parameters 38 | /// and the related coach mark. Use this object rather than capturing a reference to 39 | /// `CoachMarkAnimationManager`. 40 | /// 41 | /// - Parameters: 42 | /// - type: type of the animation 43 | /// - animations: animation block 44 | /// - fromInitialState: initial state of the animation 45 | /// - completion: completion block 46 | public func animate(_ type: AnimationType, 47 | animations: @escaping (CoachMarkAnimationManagementContext) -> Void, 48 | fromInitialState initialState: (() -> Void)? = nil, 49 | completion: ((Bool) -> Void)? = nil) { 50 | self.initialState = initialState 51 | self.animations = animations 52 | self.completion = completion 53 | 54 | animationType = type 55 | } 56 | 57 | // MARK: Internal methods 58 | /// Create the context from the current parameters and coach mark. 59 | /// 60 | /// - Returns: new context object. 61 | internal func createContext() -> CoachMarkAnimationManagementContext { 62 | return CoachMarkAnimationManagerContext(coachMark: coachMark, 63 | parameters: parameters) 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /Sources/Instructions/Helpers/Public/Enums/CoachMarkArrowOrientation.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-present Frédéric Maquin and contributors. 2 | // Licensed under the terms of the MIT License. 3 | 4 | import UIKit 5 | 6 | /// Available orientations for the arrow. 7 | /// An arrow can either sit at the top of the body and point upwards (.top) or 8 | /// sit at the bottom of the body and point downwards. (.bottom) 9 | public enum CoachMarkArrowOrientation { 10 | case top 11 | case bottom 12 | } 13 | -------------------------------------------------------------------------------- /Sources/Instructions/Helpers/Public/Enums/CoachMarkNextLabelPosition.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-present Frédéric Maquin and contributors. 2 | // Licensed under the terms of the MIT License. 3 | 4 | /// Available positions for the nextLabel. 5 | /// A nextLabel can either be positioned right beside the hintLabel (.trailing / .leading) with a separator in between, or 6 | /// be positioned at the corner of the body (.topTrailing / .topLeading / .bottomTrailing / .bottomLeading) without a separator. 7 | public enum CoachMarkNextLabelPosition { 8 | case trailing 9 | case leading 10 | case topTrailing 11 | case topLeading 12 | case bottomTrailing 13 | case bottomLeading 14 | } 15 | -------------------------------------------------------------------------------- /Sources/Instructions/Managers/Internal/OverlayStyleManager/OverlayStyleManager.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017-present Frédéric Maquin and contributors. 2 | // Licensed under the terms of the MIT License. 3 | 4 | import UIKit 5 | 6 | /// This protocol expected to be implemented by the CoachMarkController. 7 | /// A snapshottable object will return a snapshot view of its content. 8 | /// Useful when dealing with multiple windows. 9 | protocol Snapshottable: AnyObject { 10 | /// Returns: A snapshot of the view hierarchy. 11 | func snapshot() -> UIView? 12 | } 13 | 14 | /// Define a common API for different types of overlay. 15 | /// (Blurring the content behind, simple background color, etc.) 16 | /// Takes care of displaying and animating the overlay / cutout path (doesn't 17 | /// deals with the coach mark view itself). 18 | protocol OverlayStyleManager: AnyObject { 19 | /// The overlay managed by the styleManager. 20 | var overlayView: OverlayView? { get set } 21 | 22 | /// Called when the size of usable screen space will change. 23 | func viewWillTransition() 24 | 25 | /// Called when the size of usable screen space did change. 26 | func viewDidTransition() 27 | 28 | /// Show/hide the overlay. 29 | /// 30 | /// - Parameters: 31 | /// - show: `true` to show the overlay, `false` to hide. 32 | /// - duration: duration of the animation 33 | /// - completion: a block to execute after compleion. 34 | func showOverlay(_ show: Bool, withDuration duration: TimeInterval, 35 | completion: ((Bool) -> Void)?) 36 | 37 | /// Show/hide the cutout. 38 | /// 39 | /// - Parameters: 40 | /// - show: `true` to show the overlay, `false` to hide. 41 | /// - duration: duration of the animation 42 | /// - completion: a block to execute after completion. 43 | func showCutout(_ show: Bool, withDuration duration: TimeInterval, 44 | completion: ((Bool) -> Void)?) 45 | 46 | func updateStyle(with traitCollection: UITraitCollection) 47 | } 48 | -------------------------------------------------------------------------------- /Sources/Instructions/Protocols and Enums/Internal/CoachMarkViewControllerDelegate.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016-present Frédéric Maquin and contributors. 2 | // Licensed under the terms of the MIT License. 3 | 4 | /// Used by the CoachMarksViewController to notify user or system related events. 5 | protocol CoachMarksViewControllerDelegate: AnyObject { 6 | /// The given `coachMarkView` was tapped. 7 | /// 8 | /// - Parameter coachMarkView: the view that was tapped. 9 | func didTap(coachMarkView: CoachMarkView?) 10 | 11 | /// The given `skipView` was tapped. 12 | /// 13 | /// - Parameter skipView: the view that was tapped. 14 | func didTap(skipView: CoachMarkSkipView?) 15 | 16 | /// The delegate should prepare for the upcoming size transition. 17 | func willTransition() 18 | 19 | /// The delegate should perform action, after for the size transition was completed 20 | func didTransition(afterChanging change: ConfigurationChange) 21 | } 22 | -------------------------------------------------------------------------------- /Sources/Instructions/Protocols and Enums/Public/CoachMarkArrowView.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-present Frédéric Maquin and contributors. 2 | // Licensed under the terms of the MIT License. 3 | 4 | import UIKit 5 | 6 | /// A protocol to which all the "arrow views" of a coach mark must conform. 7 | public protocol CoachMarkArrowView: AnyObject { 8 | /// A method to change the arrow highlighted state. 9 | /// If you feel the arrow should mirror the state of the "body view", 10 | /// You will most likely change the background color of the view here. 11 | var isHighlighted: Bool { get set } 12 | } 13 | -------------------------------------------------------------------------------- /Sources/Instructions/Protocols and Enums/Public/CoachMarkBodyHighlightArrowDelegate.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-present Frédéric Maquin and contributors. 2 | // Licensed under the terms of the MIT License. 3 | 4 | import UIKit 5 | 6 | /// Delegate the highlight mechanism of the arrow. This protocol is 7 | /// useful in case the whole body itself is the active control and 8 | /// we want the arrow to looks like it is part of this control. 9 | public protocol CoachMarkBodyHighlightArrowDelegate: AnyObject { 10 | 11 | /// Set whether or not the arrow should get in its 12 | /// highlighted state. 13 | /// 14 | /// - Parameters isHighlighted: `true` if the arrow should be highlighted, `false` otherwise. 15 | func highlightArrow(_ highlighted: Bool) 16 | } 17 | -------------------------------------------------------------------------------- /Sources/Instructions/Protocols and Enums/Public/CoachMarkBodyView.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-present Frédéric Maquin and contributors. 2 | // Licensed under the terms of the MIT License. 3 | 4 | import UIKit 5 | 6 | /// A protocol to which all the "body views" of a coach mark must conform. 7 | public protocol CoachMarkBodyView: AnyObject { 8 | /// The control that will trigger the change between the current coach mark 9 | /// and the next one. 10 | var nextControl: UIControl? { get } 11 | 12 | /// A delegate to call, when the arrow view to mirror the current highlight 13 | /// state of the body view. This is useful in case the entire view is actually a `UIControl`. 14 | /// 15 | /// The `CoachMarkView`, of which the current view must be 16 | /// part, will automatically set itself as the delegate and will take care 17 | /// of forwarding the state to the arrow view. 18 | var highlightArrowDelegate: CoachMarkBodyHighlightArrowDelegate? { get set } 19 | } 20 | -------------------------------------------------------------------------------- /Sources/Instructions/Protocols and Enums/Public/CoachMarkSkipView.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-present Frédéric Maquin and contributors. 2 | // Licensed under the terms of the MIT License. 3 | 4 | import UIKit 5 | 6 | /// A protocol to which all the "skip views" must conform. 7 | public protocol CoachMarkSkipView: AnyObject { 8 | /// The control that will trigger the stop, in the display flow. 9 | var skipControl: UIControl? { get } 10 | var asView: UIView? { get } 11 | } 12 | 13 | public extension CoachMarkSkipView { 14 | var skipControl: UIControl? { 15 | return nil 16 | } 17 | } 18 | 19 | public extension CoachMarkSkipView where Self: UIView { 20 | var asView: UIView? { 21 | return self 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Sources/Instructions/Protocols and Enums/Public/Delegates/CoachMarksControllerAnimationDelegate.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018-present Frédéric Maquin and contributors. 2 | // Licensed under the terms of the MIT License. 3 | 4 | import UIKit 5 | 6 | /// 7 | public protocol CoachMarksControllerAnimationDelegate: AnyObject { 8 | func coachMarksController(_ coachMarksController: CoachMarksController, 9 | fetchAppearanceTransitionOfCoachMark coachMarkView: UIView, 10 | at index: Int, 11 | using manager: CoachMarkTransitionManager) 12 | 13 | func coachMarksController(_ coachMarksController: CoachMarksController, 14 | fetchDisappearanceTransitionOfCoachMark coachMarkView: UIView, 15 | at index: Int, 16 | using manager: CoachMarkTransitionManager) 17 | 18 | func coachMarksController(_ coachMarksController: CoachMarksController, 19 | fetchIdleAnimationOfCoachMark coachMarkView: UIView, 20 | at index: Int, 21 | using manager: CoachMarkAnimationManager) 22 | } 23 | 24 | public extension CoachMarksControllerAnimationDelegate { 25 | func coachMarksController(_ coachMarksController: CoachMarksController, 26 | fetchAppearanceTransitionOfCoachMark coachMarkView: UIView, 27 | at index: Int, 28 | using manager: CoachMarkTransitionManager) { } 29 | 30 | func coachMarksController(_ coachMarksController: CoachMarksController, 31 | fetchDisappearanceTransitionOfCoachMark coachMarkView: UIView, 32 | at index: Int, 33 | using manager: CoachMarkTransitionManager) { } 34 | 35 | func coachMarksController(_ coachMarksController: CoachMarksController, 36 | fetchIdleAnimationOfCoachMark coachMarkView: UIView, 37 | at index: Int, 38 | using manager: CoachMarkAnimationManager) { } 39 | } 40 | 41 | protocol CoachMarksControllerAnimationProxyDelegate: AnyObject { 42 | func fetchAppearanceTransition(OfCoachMark coachMarkView: UIView, 43 | at index: Int, 44 | using manager: CoachMarkTransitionManager) 45 | 46 | func fetchDisappearanceTransition(OfCoachMark coachMarkView: UIView, 47 | at index: Int, 48 | using manager: CoachMarkTransitionManager) 49 | 50 | func fetchIdleAnimationOfCoachMark(OfCoachMark coachMarkView: UIView, 51 | at index: Int, 52 | using manager: CoachMarkAnimationManager) 53 | } 54 | -------------------------------------------------------------------------------- /Sources/Instructions/Protocols and Enums/Public/Enums/ConfigurationChange.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018-present Frédéric Maquin and contributors. 2 | // Licensed under the terms of the MIT License. 3 | 4 | import Foundation 5 | 6 | /// Define the type of change involved during a configuration change. 7 | public enum ConfigurationChange { 8 | case nothing, size, statusBar 9 | } 10 | -------------------------------------------------------------------------------- /Sources/Instructions/Protocols and Enums/Public/Enums/PauseStyle.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018-present Frédéric Maquin and contributors. 2 | // Licensed under the terms of the MIT License. 3 | 4 | import Foundation 5 | 6 | public enum PauseStyle { 7 | case hideInstructions, hideOverlay, hideNothing 8 | } 9 | -------------------------------------------------------------------------------- /Sources/Instructions/Protocols and Enums/Public/Enums/PresentationContext.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018-present Frédéric Maquin and contributors. 2 | // Licensed under the terms of the MIT License. 3 | import UIKit 4 | 5 | // TODO: Remove the override once the bug is fixed. 6 | // swiftlint:disable identifier_name 7 | 8 | public enum PresentationContext { 9 | case newWindow(over: UIViewController, at: UIWindow.Level?) 10 | case currentWindow(of: UIViewController) 11 | case viewController(_: UIViewController) 12 | 13 | public static func window(over: UIViewController) -> PresentationContext { 14 | return newWindow(over: over, at: nil) 15 | } 16 | } 17 | 18 | internal enum PresentationFashion { 19 | case window, viewControllerWindow, viewController 20 | } 21 | -------------------------------------------------------------------------------- /Sources/Instructions/Protocols and Enums/Public/Enums/ViewControllerConfiguration.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018-present Frédéric Maquin and contributors. 2 | // Licensed under the terms of the MIT License. 3 | 4 | import UIKit 5 | 6 | // TODO: Remove the override once the bug is fixed. 7 | // swiftlint:disable identifier_name 8 | 9 | public enum RotationStyle { 10 | case systemDefined 11 | case automatic 12 | case manual 13 | } 14 | 15 | public enum StatusBarVisibility { 16 | case systemDefined 17 | case visible 18 | case hidden 19 | } 20 | 21 | public enum InterfaceOrientations { 22 | case systemDefined 23 | case userDefined(as: UIInterfaceOrientationMask) 24 | } 25 | -------------------------------------------------------------------------------- /Sources/Instructions/Supporting Files/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | com.ephread.Instructions 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | $(MARKETING_VERSION) 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Sources/Instructions/Supporting Files/InfoAppExtensions.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | com.ephread.Instructions.AppExtensions 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | $(MARKETING_VERSION) 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Sources/Instructions/Supporting Files/Instructions.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-present Frédéric Maquin and contributors. 2 | // Licensed under the terms of the MIT License. 3 | 4 | #import 5 | 6 | //! Project version number for Instructions. 7 | FOUNDATION_EXPORT double InstructionsVersionNumber; 8 | 9 | //! Project version string for Instructions. 10 | FOUNDATION_EXPORT const unsigned char InstructionsVersionString[]; 11 | 12 | // In this header, you should import all the public headers of your framework using statements like #import 13 | 14 | 15 | -------------------------------------------------------------------------------- /Sources/Instructions/Views/MaskView.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016-present Frédéric Maquin and contributors. 2 | // Licensed under the terms of the MIT License. 3 | 4 | import UIKit 5 | 6 | class MaskView: UIView { 7 | 8 | override public class var layerClass: AnyClass { 9 | return CAShapeLayer.self 10 | } 11 | 12 | var shapeLayer: CAShapeLayer { 13 | //swiftlint:disable force_cast 14 | return layer as! CAShapeLayer 15 | //swiftlint:enable force_cast 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /Sources/Instructions/Views/OverlaySnapshotView.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016-present Frédéric Maquin and contributors. 2 | // Licensed under the terms of the MIT License. 3 | 4 | import UIKit 5 | 6 | class OverlaySnapshotView: UIView { 7 | var visualEffectView: UIVisualEffectView! { 8 | willSet { 9 | if visualEffectView == nil { return } 10 | visualEffectView.removeFromSuperview() 11 | } 12 | 13 | didSet { 14 | if visualEffectView != nil { 15 | self.addSubview(visualEffectView) 16 | } 17 | } 18 | } 19 | var backgroundView: UIView! { 20 | willSet { 21 | if backgroundView == nil { return } 22 | backgroundView.removeFromSuperview() 23 | } 24 | 25 | didSet { 26 | if backgroundView != nil { 27 | self.addSubview(backgroundView) 28 | } 29 | } 30 | } 31 | 32 | override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { 33 | return nil 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Sources/Instructions/Views/OverlayView.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-present Frédéric Maquin and contributors. 2 | // Licensed under the terms of the MIT License. 3 | 4 | import UIKit 5 | 6 | // Overlay a blocking view on top of the screen and handle the cutout path 7 | // around the point of interest. 8 | class OverlayView: UIView { 9 | internal static let sublayerName = "Instructions.OverlaySublayer" 10 | 11 | var cutoutPath: UIBezierPath? 12 | 13 | let holder: UIView 14 | let ornaments: UIView 15 | 16 | /// Used to temporarily enable touch forwarding inside the cutoutPath. 17 | public var allowTouchInsideCutoutPath: Bool = false 18 | public var forwardTouchEvents: Bool = false 19 | 20 | // MARK: - Initialization 21 | init() { 22 | holder = UIView() 23 | ornaments = UIView() 24 | 25 | super.init(frame: CGRect.zero) 26 | translatesAutoresizingMaskIntoConstraints = false 27 | 28 | holder.translatesAutoresizingMaskIntoConstraints = false 29 | ornaments.translatesAutoresizingMaskIntoConstraints = false 30 | 31 | holder.isUserInteractionEnabled = false 32 | ornaments.isUserInteractionEnabled = false 33 | 34 | addSubview(holder) 35 | addSubview(ornaments) 36 | 37 | holder.fillSuperview() 38 | ornaments.fillSuperview() 39 | 40 | accessibilityIdentifier = AccessibilityIdentifiers.overlayView 41 | } 42 | 43 | required public init?(coder aDecoder: NSCoder) { 44 | fatalError(ErrorMessage.Fatal.doesNotSupportNSCoding) 45 | } 46 | 47 | // MARK: - Internal methods 48 | override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { 49 | let hitView = super.hitTest(point, with: event) 50 | 51 | if hitView == self { 52 | guard !forwardTouchEvents else { return nil } 53 | 54 | guard let cutoutPath = self.cutoutPath else { 55 | return hitView 56 | } 57 | 58 | if !self.allowTouchInsideCutoutPath { 59 | return hitView 60 | } 61 | 62 | if cutoutPath.contains(point) { 63 | return nil 64 | } else { 65 | return hitView 66 | } 67 | } 68 | 69 | return hitView 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /Sources/Instructions/Views/PassthroughViews.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016-present Frédéric Maquin and contributors. 2 | // Licensed under the terms of the MIT License. 3 | 4 | import UIKit 5 | 6 | class InstructionsWindow: UIWindow { 7 | override init(frame: CGRect) { 8 | super.init(frame: frame) 9 | accessibilityIdentifier = AccessibilityIdentifiers.window 10 | } 11 | 12 | required init?(coder aDecoder: NSCoder) { 13 | super.init(coder: aDecoder) 14 | accessibilityIdentifier = AccessibilityIdentifiers.window 15 | } 16 | 17 | @available(iOS 13.0, *) 18 | override init(windowScene: UIWindowScene) { 19 | super.init(windowScene: windowScene) 20 | accessibilityIdentifier = AccessibilityIdentifiers.window 21 | } 22 | 23 | override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { 24 | let hitView = super.hitTest(point, with: event) 25 | 26 | if let hitView = hitView, (hitView == self || insertedViewsToIgnore.contains(hitView)) { 27 | return nil 28 | } 29 | 30 | return hitView 31 | } 32 | 33 | // On iPad Pros, since iOS 13, a blocking view is present in the UIKit 34 | // hierarchy. Here we're simply grabbing all these intermediate UIKit views 35 | // so that we can ignore them in hitTest. 36 | // 37 | // See #234 and https://forums.developer.apple.com/thread/122174 38 | var insertedViewsToIgnore: [UIView] { 39 | return recursiveSubviews(of: self) 40 | } 41 | 42 | func recursiveSubviews(of view: UIView) -> [UIView] { 43 | // We just hit the bottom. 44 | guard !(view is PassthroughView) && !(view is InstructionsRootView) else { 45 | return [] 46 | } 47 | 48 | var subviews = view.subviews.filter { 49 | !($0 is PassthroughView) && !($0 is InstructionsRootView) 50 | } 51 | 52 | for subview in view.subviews { 53 | subviews.append(contentsOf: recursiveSubviews(of: subview)) 54 | } 55 | 56 | return subviews 57 | } 58 | } 59 | 60 | class PassthroughView: UIView { 61 | override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { 62 | let hitView = super.hitTest(point, with: event) 63 | 64 | if hitView == self { 65 | return nil 66 | } 67 | 68 | return hitView 69 | } 70 | } 71 | 72 | /// Top view added to the window, forwarding touch events. 73 | class InstructionsRootView: UIView { 74 | 75 | var passthrough: Bool = false 76 | 77 | override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { 78 | let hitView = super.hitTest(point, with: event) 79 | 80 | if hitView == self && passthrough { 81 | return nil 82 | } 83 | 84 | return hitView 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /fastlane/Appfile: -------------------------------------------------------------------------------- 1 | # app_identifier("[[APP_IDENTIFIER]]") # The bundle identifier of your app 2 | # apple_id("[[APPLE_ID]]") # Your Apple email address 3 | 4 | 5 | # For more information about the Appfile, see: 6 | # https://docs.fastlane.tools/advanced/#appfile 7 | -------------------------------------------------------------------------------- /fastlane/README.md: -------------------------------------------------------------------------------- 1 | fastlane documentation 2 | ---- 3 | 4 | # Installation 5 | 6 | Make sure you have the latest version of the Xcode command line tools installed: 7 | 8 | ```sh 9 | xcode-select --install 10 | ``` 11 | 12 | For _fastlane_ installation instructions, see [Installing _fastlane_](https://docs.fastlane.tools/#installing-fastlane) 13 | 14 | # Available Actions 15 | 16 | ## iOS 17 | 18 | ### ios build_all 19 | 20 | ```sh 21 | [bundle exec] fastlane ios build_all 22 | ``` 23 | 24 | Build all targets 25 | 26 | ### ios build_frameworks 27 | 28 | ```sh 29 | [bundle exec] fastlane ios build_frameworks 30 | ``` 31 | 32 | Build frameworks 33 | 34 | ### ios build_examples 35 | 36 | ```sh 37 | [bundle exec] fastlane ios build_examples 38 | ``` 39 | 40 | Build examples 41 | 42 | ### ios build 43 | 44 | ```sh 45 | [bundle exec] fastlane ios build 46 | ``` 47 | 48 | Build the given scheme 49 | 50 | ### ios generate_snapshots 51 | 52 | ```sh 53 | [bundle exec] fastlane ios generate_snapshots 54 | ``` 55 | 56 | Generate new snapshots for all simulators 57 | 58 | ### ios test_all 59 | 60 | ```sh 61 | [bundle exec] fastlane ios test_all 62 | ``` 63 | 64 | Run full test suite 65 | 66 | ### ios test_snapshots 67 | 68 | ```sh 69 | [bundle exec] fastlane ios test_snapshots 70 | ``` 71 | 72 | Run snapshot tests 73 | 74 | ### ios test_unit_and_ui 75 | 76 | ```sh 77 | [bundle exec] fastlane ios test_unit_and_ui 78 | ``` 79 | 80 | Run Unit & UI tests 81 | 82 | ---- 83 | 84 | This README.md is auto-generated and will be re-generated every time [_fastlane_](https://fastlane.tools) is run. 85 | 86 | More information about _fastlane_ can be found on [fastlane.tools](https://fastlane.tools). 87 | 88 | The documentation of _fastlane_ can be found on [docs.fastlane.tools](https://docs.fastlane.tools). 89 | --------------------------------------------------------------------------------