├── .gitignore ├── Example ├── ContentViewController.storyboard ├── ContentViewController.swift ├── DummyScreenGenerator.swift ├── ScreenSelectorExample.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── xcshareddata │ │ └── xcschemes │ │ └── ScreenSelectorExample.xcscheme ├── ScreenSelectorExample.xcworkspace │ └── contents.xcworkspacedata ├── ScreenSelectorExample │ ├── AppDelegate.swift │ ├── Assets.xcassets │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── Base.lproj │ │ ├── ExtendedViewController.storyboard │ │ └── LaunchScreen.storyboard │ ├── ExtendedViewController.swift │ ├── Info.plist │ ├── ViewController.storyboard │ ├── ViewController.swift │ └── example_bg@2x.jpg ├── ScreenSelectorExampleTests │ ├── Info.plist │ └── ScreenSelectorExampleTests.swift └── ScreenSelectorExampleUITests │ ├── Info.plist │ └── ScreenSelectorExampleUITests.swift ├── LICENSE ├── README.md ├── ScreenSelector.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── xcshareddata │ └── xcschemes │ └── ScreenSelector.xcscheme ├── ScreenSelector ├── DummyScreenGenerator.swift ├── Info.plist ├── ReversibleAnimatedTransitioning.swift ├── ScreenPreviewView.swift ├── ScreenSelector.h ├── ScreenSelectorPreviewable.swift ├── ScreenSelectorTransitioningAnimator.swift ├── ScreenSelectorView.swift ├── ScreenSelectorViewController.swift └── SpecularReflectionLayer.swift ├── ScreenSelectorTests ├── Info.plist └── ScreenSelectorTests.swift └── preview.gif /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io/api/xcode,swift,objective-c,carthage 2 | 3 | ### OSX ### 4 | *.DS_Store 5 | .AppleDouble 6 | .LSOverride 7 | 8 | # Icon must end with two \r 9 | Icon 10 | 11 | 12 | # Thumbnails 13 | ._* 14 | 15 | # Files that might appear in the root of a volume 16 | .DocumentRevisions-V100 17 | .fseventsd 18 | .Spotlight-V100 19 | .TemporaryItems 20 | .Trashes 21 | .VolumeIcon.icns 22 | .com.apple.timemachine.donotpresent 23 | 24 | # Directories potentially created on remote AFP share 25 | .AppleDB 26 | .AppleDesktop 27 | Network Trash Folder 28 | Temporary Items 29 | .apdisk 30 | 31 | ### Xcode ### 32 | # Xcode 33 | # 34 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 35 | 36 | ## Playgrounds 37 | timeline.xctimeline 38 | playground.xcworkspace 39 | 40 | # Carthage 41 | # 42 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 43 | #Carthage/Checkouts 44 | 45 | Carthage/Build 46 | 47 | ### Objective-C ### 48 | # Xcode 49 | # 50 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 51 | 52 | ## Build generated 53 | build/ 54 | DerivedData 55 | 56 | ## Various settings 57 | *.pbxuser 58 | !default.pbxuser 59 | *.mode1v3 60 | !default.mode1v3 61 | *.mode2v3 62 | !default.mode2v3 63 | *.perspectivev3 64 | !default.perspectivev3 65 | xcuserdata 66 | 67 | ## Other 68 | *.xccheckout 69 | *.moved-aside 70 | *.xcuserstate 71 | *.xcscmblueprint 72 | 73 | ## Obj-C/Swift specific 74 | *.hmap 75 | *.ipa 76 | 77 | ### Objective-C Patch ### 78 | *.xcscmblueprint 79 | 80 | 81 | ### Carthage ### 82 | # Carthage - A simple, decentralized dependency manager for Cocoa 83 | Carthage.checkout 84 | Carthage.build -------------------------------------------------------------------------------- /Example/ContentViewController.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /Example/ContentViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContentViewController.swift 3 | // ScreenSelectorExample 4 | // 5 | // Created by Tomonori Ueno on 2016/07/22. 6 | // Copyright © 2016年 Tomonori Ueno. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import ScreenSelector 11 | 12 | final class ContentViewController: UIViewController, ScreenSelectorPreviewable { 13 | 14 | @IBOutlet weak var textLabel: UILabel! 15 | @IBOutlet weak var backgroundImageView: UIImageView? 16 | 17 | var thumbnailImage: UIImage? { 18 | get { 19 | return UIImage(named: "example_bg.jpg") 20 | } 21 | } 22 | 23 | override func viewDidLoad() { 24 | super.viewDidLoad() 25 | let tapRecog = UITapGestureRecognizer(target: self, action: #selector(tapped(_:))) 26 | view.addGestureRecognizer(tapRecog) 27 | } 28 | 29 | func tapped(tapRecog: UITapGestureRecognizer) { 30 | dismissViewControllerAnimated(true, completion: nil) 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /Example/DummyScreenGenerator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DummyScreenGenerator.swift 3 | // ScreenSelectorExample 4 | // 5 | // Created by Tomonori Ueno on 2016/07/26. 6 | // Copyright © 2016年 Tomonori Ueno. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import ScreenSelector 11 | 12 | final class DummyScreenGenerator { 13 | 14 | class func generateViewControllers() -> [ScreenSelectorPreviewable] { 15 | var viewControllers = [ScreenSelectorPreviewable]() 16 | for i in 0.stride(to: 5, by: 1) { 17 | let vc = UIStoryboard.init(name: "ContentViewController", bundle: nil).instantiateInitialViewController() as? ContentViewController 18 | vc?.view.tag = i 19 | vc?.textLabel.text = "\(i)" 20 | vc?.title = "Title \(i)" 21 | if let v = vc as? ScreenSelectorPreviewable { 22 | viewControllers.append(v) 23 | } 24 | } 25 | return viewControllers 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /Example/ScreenSelectorExample.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 2D38501E1D61898500DA1F6A /* ScreenSelector.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = 2D3850181D6188C000DA1F6A /* ScreenSelector.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 11 | 2D38501F1D6189A300DA1F6A /* ScreenSelector.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2D3850181D6188C000DA1F6A /* ScreenSelector.framework */; }; 12 | 2D3850291D61909800DA1F6A /* ContentViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 2D3850261D61909800DA1F6A /* ContentViewController.storyboard */; }; 13 | 2D38502A1D61909800DA1F6A /* ContentViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D3850271D61909800DA1F6A /* ContentViewController.swift */; }; 14 | 2D38502B1D61909800DA1F6A /* DummyScreenGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D3850281D61909800DA1F6A /* DummyScreenGenerator.swift */; }; 15 | 2D38502D1D61BE5600DA1F6A /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D38502C1D61BE5600DA1F6A /* ViewController.swift */; }; 16 | 2D38502F1D61BE6100DA1F6A /* ViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 2D38502E1D61BE6100DA1F6A /* ViewController.storyboard */; }; 17 | 752025501D3F9AA5000096E1 /* example_bg@2x.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 7520254F1D3F9AA5000096E1 /* example_bg@2x.jpg */; }; 18 | 75E4E17E1D3F68A5001496FC /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75E4E17D1D3F68A5001496FC /* AppDelegate.swift */; }; 19 | 75E4E1801D3F68A5001496FC /* ExtendedViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75E4E17F1D3F68A5001496FC /* ExtendedViewController.swift */; }; 20 | 75E4E1831D3F68A5001496FC /* ExtendedViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 75E4E1811D3F68A5001496FC /* ExtendedViewController.storyboard */; }; 21 | 75E4E1851D3F68A5001496FC /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 75E4E1841D3F68A5001496FC /* Assets.xcassets */; }; 22 | 75E4E1881D3F68A5001496FC /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 75E4E1861D3F68A5001496FC /* LaunchScreen.storyboard */; }; 23 | 75E4E1931D3F68A5001496FC /* ScreenSelectorExampleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75E4E1921D3F68A5001496FC /* ScreenSelectorExampleTests.swift */; }; 24 | 75E4E19E1D3F68A5001496FC /* ScreenSelectorExampleUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75E4E19D1D3F68A5001496FC /* ScreenSelectorExampleUITests.swift */; }; 25 | /* End PBXBuildFile section */ 26 | 27 | /* Begin PBXContainerItemProxy section */ 28 | 2D3850171D6188C000DA1F6A /* PBXContainerItemProxy */ = { 29 | isa = PBXContainerItemProxy; 30 | containerPortal = 2D3850121D6188C000DA1F6A /* ScreenSelector.xcodeproj */; 31 | proxyType = 2; 32 | remoteGlobalIDString = 2D384F8E1D6177E200DA1F6A; 33 | remoteInfo = ScreenSelector; 34 | }; 35 | 2D3850191D6188C000DA1F6A /* PBXContainerItemProxy */ = { 36 | isa = PBXContainerItemProxy; 37 | containerPortal = 2D3850121D6188C000DA1F6A /* ScreenSelector.xcodeproj */; 38 | proxyType = 2; 39 | remoteGlobalIDString = 2D384F981D6177E200DA1F6A; 40 | remoteInfo = ScreenSelectorTests; 41 | }; 42 | 2D38501B1D6188DC00DA1F6A /* PBXContainerItemProxy */ = { 43 | isa = PBXContainerItemProxy; 44 | containerPortal = 2D3850121D6188C000DA1F6A /* ScreenSelector.xcodeproj */; 45 | proxyType = 1; 46 | remoteGlobalIDString = 2D384F8D1D6177E200DA1F6A; 47 | remoteInfo = ScreenSelector; 48 | }; 49 | 75E4E18F1D3F68A5001496FC /* PBXContainerItemProxy */ = { 50 | isa = PBXContainerItemProxy; 51 | containerPortal = 75E4E1721D3F68A5001496FC /* Project object */; 52 | proxyType = 1; 53 | remoteGlobalIDString = 75E4E1791D3F68A5001496FC; 54 | remoteInfo = ScreenSelectorExample; 55 | }; 56 | 75E4E19A1D3F68A5001496FC /* PBXContainerItemProxy */ = { 57 | isa = PBXContainerItemProxy; 58 | containerPortal = 75E4E1721D3F68A5001496FC /* Project object */; 59 | proxyType = 1; 60 | remoteGlobalIDString = 75E4E1791D3F68A5001496FC; 61 | remoteInfo = ScreenSelectorExample; 62 | }; 63 | /* End PBXContainerItemProxy section */ 64 | 65 | /* Begin PBXCopyFilesBuildPhase section */ 66 | 2D38501D1D61897800DA1F6A /* Copy Frameworks */ = { 67 | isa = PBXCopyFilesBuildPhase; 68 | buildActionMask = 2147483647; 69 | dstPath = ""; 70 | dstSubfolderSpec = 7; 71 | files = ( 72 | 2D38501E1D61898500DA1F6A /* ScreenSelector.framework in Copy Frameworks */, 73 | ); 74 | name = "Copy Frameworks"; 75 | runOnlyForDeploymentPostprocessing = 0; 76 | }; 77 | 7539D2CF1D463C8C003EAB79 /* Embed Frameworks */ = { 78 | isa = PBXCopyFilesBuildPhase; 79 | buildActionMask = 2147483647; 80 | dstPath = ""; 81 | dstSubfolderSpec = 10; 82 | files = ( 83 | ); 84 | name = "Embed Frameworks"; 85 | runOnlyForDeploymentPostprocessing = 0; 86 | }; 87 | /* End PBXCopyFilesBuildPhase section */ 88 | 89 | /* Begin PBXFileReference section */ 90 | 2D3850121D6188C000DA1F6A /* ScreenSelector.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = ScreenSelector.xcodeproj; path = ../ScreenSelector.xcodeproj; sourceTree = ""; }; 91 | 2D3850261D61909800DA1F6A /* ContentViewController.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = ContentViewController.storyboard; path = ../ContentViewController.storyboard; sourceTree = ""; }; 92 | 2D3850271D61909800DA1F6A /* ContentViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ContentViewController.swift; path = ../ContentViewController.swift; sourceTree = ""; }; 93 | 2D3850281D61909800DA1F6A /* DummyScreenGenerator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = DummyScreenGenerator.swift; path = ../DummyScreenGenerator.swift; sourceTree = ""; }; 94 | 2D38502C1D61BE5600DA1F6A /* ViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 95 | 2D38502E1D61BE6100DA1F6A /* ViewController.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = ViewController.storyboard; sourceTree = ""; }; 96 | 7520254F1D3F9AA5000096E1 /* example_bg@2x.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = "example_bg@2x.jpg"; sourceTree = ""; }; 97 | 75E4E17A1D3F68A5001496FC /* ScreenSelectorExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ScreenSelectorExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; 98 | 75E4E17D1D3F68A5001496FC /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 99 | 75E4E17F1D3F68A5001496FC /* ExtendedViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExtendedViewController.swift; sourceTree = ""; }; 100 | 75E4E1821D3F68A5001496FC /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/ExtendedViewController.storyboard; sourceTree = ""; }; 101 | 75E4E1841D3F68A5001496FC /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 102 | 75E4E1871D3F68A5001496FC /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 103 | 75E4E1891D3F68A5001496FC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 104 | 75E4E18E1D3F68A5001496FC /* ScreenSelectorExampleTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ScreenSelectorExampleTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 105 | 75E4E1921D3F68A5001496FC /* ScreenSelectorExampleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScreenSelectorExampleTests.swift; sourceTree = ""; }; 106 | 75E4E1941D3F68A5001496FC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 107 | 75E4E1991D3F68A5001496FC /* ScreenSelectorExampleUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ScreenSelectorExampleUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 108 | 75E4E19D1D3F68A5001496FC /* ScreenSelectorExampleUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScreenSelectorExampleUITests.swift; sourceTree = ""; }; 109 | 75E4E19F1D3F68A5001496FC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 110 | /* End PBXFileReference section */ 111 | 112 | /* Begin PBXFrameworksBuildPhase section */ 113 | 75E4E1771D3F68A5001496FC /* Frameworks */ = { 114 | isa = PBXFrameworksBuildPhase; 115 | buildActionMask = 2147483647; 116 | files = ( 117 | 2D38501F1D6189A300DA1F6A /* ScreenSelector.framework in Frameworks */, 118 | ); 119 | runOnlyForDeploymentPostprocessing = 0; 120 | }; 121 | 75E4E18B1D3F68A5001496FC /* Frameworks */ = { 122 | isa = PBXFrameworksBuildPhase; 123 | buildActionMask = 2147483647; 124 | files = ( 125 | ); 126 | runOnlyForDeploymentPostprocessing = 0; 127 | }; 128 | 75E4E1961D3F68A5001496FC /* Frameworks */ = { 129 | isa = PBXFrameworksBuildPhase; 130 | buildActionMask = 2147483647; 131 | files = ( 132 | ); 133 | runOnlyForDeploymentPostprocessing = 0; 134 | }; 135 | /* End PBXFrameworksBuildPhase section */ 136 | 137 | /* Begin PBXGroup section */ 138 | 2D384FA81D6179C400DA1F6A /* ScreenSelectorViewController Example */ = { 139 | isa = PBXGroup; 140 | children = ( 141 | 75E4E17F1D3F68A5001496FC /* ExtendedViewController.swift */, 142 | 75E4E1811D3F68A5001496FC /* ExtendedViewController.storyboard */, 143 | ); 144 | name = "ScreenSelectorViewController Example"; 145 | sourceTree = ""; 146 | }; 147 | 2D384FA91D6179D800DA1F6A /* ScreenSelectorView Example */ = { 148 | isa = PBXGroup; 149 | children = ( 150 | 2D38502C1D61BE5600DA1F6A /* ViewController.swift */, 151 | 2D38502E1D61BE6100DA1F6A /* ViewController.storyboard */, 152 | ); 153 | name = "ScreenSelectorView Example"; 154 | sourceTree = ""; 155 | }; 156 | 2D3850131D6188C000DA1F6A /* Products */ = { 157 | isa = PBXGroup; 158 | children = ( 159 | 2D3850181D6188C000DA1F6A /* ScreenSelector.framework */, 160 | 2D38501A1D6188C000DA1F6A /* ScreenSelectorTests.xctest */, 161 | ); 162 | name = Products; 163 | sourceTree = ""; 164 | }; 165 | 7520254D1D3F9A81000096E1 /* Resources */ = { 166 | isa = PBXGroup; 167 | children = ( 168 | 7520254E1D3F9A88000096E1 /* image */, 169 | ); 170 | name = Resources; 171 | sourceTree = ""; 172 | }; 173 | 7520254E1D3F9A88000096E1 /* image */ = { 174 | isa = PBXGroup; 175 | children = ( 176 | 7520254F1D3F9AA5000096E1 /* example_bg@2x.jpg */, 177 | ); 178 | name = image; 179 | sourceTree = ""; 180 | }; 181 | 75E4E1711D3F68A5001496FC = { 182 | isa = PBXGroup; 183 | children = ( 184 | 75E4E17C1D3F68A5001496FC /* ScreenSelectorExample */, 185 | 75E4E1911D3F68A5001496FC /* ScreenSelectorExampleTests */, 186 | 75E4E19C1D3F68A5001496FC /* ScreenSelectorExampleUITests */, 187 | 75E4E17B1D3F68A5001496FC /* Products */, 188 | 2D3850121D6188C000DA1F6A /* ScreenSelector.xcodeproj */, 189 | ); 190 | sourceTree = ""; 191 | }; 192 | 75E4E17B1D3F68A5001496FC /* Products */ = { 193 | isa = PBXGroup; 194 | children = ( 195 | 75E4E17A1D3F68A5001496FC /* ScreenSelectorExample.app */, 196 | 75E4E18E1D3F68A5001496FC /* ScreenSelectorExampleTests.xctest */, 197 | 75E4E1991D3F68A5001496FC /* ScreenSelectorExampleUITests.xctest */, 198 | ); 199 | name = Products; 200 | sourceTree = ""; 201 | }; 202 | 75E4E17C1D3F68A5001496FC /* ScreenSelectorExample */ = { 203 | isa = PBXGroup; 204 | children = ( 205 | 2D3850261D61909800DA1F6A /* ContentViewController.storyboard */, 206 | 2D3850271D61909800DA1F6A /* ContentViewController.swift */, 207 | 2D3850281D61909800DA1F6A /* DummyScreenGenerator.swift */, 208 | 75E4E17D1D3F68A5001496FC /* AppDelegate.swift */, 209 | 2D384FA91D6179D800DA1F6A /* ScreenSelectorView Example */, 210 | 2D384FA81D6179C400DA1F6A /* ScreenSelectorViewController Example */, 211 | 7520254D1D3F9A81000096E1 /* Resources */, 212 | 75E4E1841D3F68A5001496FC /* Assets.xcassets */, 213 | 75E4E1861D3F68A5001496FC /* LaunchScreen.storyboard */, 214 | 75E4E1891D3F68A5001496FC /* Info.plist */, 215 | ); 216 | path = ScreenSelectorExample; 217 | sourceTree = ""; 218 | }; 219 | 75E4E1911D3F68A5001496FC /* ScreenSelectorExampleTests */ = { 220 | isa = PBXGroup; 221 | children = ( 222 | 75E4E1921D3F68A5001496FC /* ScreenSelectorExampleTests.swift */, 223 | 75E4E1941D3F68A5001496FC /* Info.plist */, 224 | ); 225 | path = ScreenSelectorExampleTests; 226 | sourceTree = ""; 227 | }; 228 | 75E4E19C1D3F68A5001496FC /* ScreenSelectorExampleUITests */ = { 229 | isa = PBXGroup; 230 | children = ( 231 | 75E4E19D1D3F68A5001496FC /* ScreenSelectorExampleUITests.swift */, 232 | 75E4E19F1D3F68A5001496FC /* Info.plist */, 233 | ); 234 | path = ScreenSelectorExampleUITests; 235 | sourceTree = ""; 236 | }; 237 | /* End PBXGroup section */ 238 | 239 | /* Begin PBXNativeTarget section */ 240 | 75E4E1791D3F68A5001496FC /* ScreenSelectorExample */ = { 241 | isa = PBXNativeTarget; 242 | buildConfigurationList = 75E4E1A21D3F68A5001496FC /* Build configuration list for PBXNativeTarget "ScreenSelectorExample" */; 243 | buildPhases = ( 244 | 75E4E1761D3F68A5001496FC /* Sources */, 245 | 75E4E1771D3F68A5001496FC /* Frameworks */, 246 | 75E4E1781D3F68A5001496FC /* Resources */, 247 | 7539D2CF1D463C8C003EAB79 /* Embed Frameworks */, 248 | 2D38501D1D61897800DA1F6A /* Copy Frameworks */, 249 | ); 250 | buildRules = ( 251 | ); 252 | dependencies = ( 253 | 2D38501C1D6188DC00DA1F6A /* PBXTargetDependency */, 254 | ); 255 | name = ScreenSelectorExample; 256 | productName = ScreenSelectorExample; 257 | productReference = 75E4E17A1D3F68A5001496FC /* ScreenSelectorExample.app */; 258 | productType = "com.apple.product-type.application"; 259 | }; 260 | 75E4E18D1D3F68A5001496FC /* ScreenSelectorExampleTests */ = { 261 | isa = PBXNativeTarget; 262 | buildConfigurationList = 75E4E1A51D3F68A5001496FC /* Build configuration list for PBXNativeTarget "ScreenSelectorExampleTests" */; 263 | buildPhases = ( 264 | 75E4E18A1D3F68A5001496FC /* Sources */, 265 | 75E4E18B1D3F68A5001496FC /* Frameworks */, 266 | 75E4E18C1D3F68A5001496FC /* Resources */, 267 | ); 268 | buildRules = ( 269 | ); 270 | dependencies = ( 271 | 75E4E1901D3F68A5001496FC /* PBXTargetDependency */, 272 | ); 273 | name = ScreenSelectorExampleTests; 274 | productName = ScreenSelectorExampleTests; 275 | productReference = 75E4E18E1D3F68A5001496FC /* ScreenSelectorExampleTests.xctest */; 276 | productType = "com.apple.product-type.bundle.unit-test"; 277 | }; 278 | 75E4E1981D3F68A5001496FC /* ScreenSelectorExampleUITests */ = { 279 | isa = PBXNativeTarget; 280 | buildConfigurationList = 75E4E1A81D3F68A5001496FC /* Build configuration list for PBXNativeTarget "ScreenSelectorExampleUITests" */; 281 | buildPhases = ( 282 | 75E4E1951D3F68A5001496FC /* Sources */, 283 | 75E4E1961D3F68A5001496FC /* Frameworks */, 284 | 75E4E1971D3F68A5001496FC /* Resources */, 285 | ); 286 | buildRules = ( 287 | ); 288 | dependencies = ( 289 | 75E4E19B1D3F68A5001496FC /* PBXTargetDependency */, 290 | ); 291 | name = ScreenSelectorExampleUITests; 292 | productName = ScreenSelectorExampleUITests; 293 | productReference = 75E4E1991D3F68A5001496FC /* ScreenSelectorExampleUITests.xctest */; 294 | productType = "com.apple.product-type.bundle.ui-testing"; 295 | }; 296 | /* End PBXNativeTarget section */ 297 | 298 | /* Begin PBXProject section */ 299 | 75E4E1721D3F68A5001496FC /* Project object */ = { 300 | isa = PBXProject; 301 | attributes = { 302 | LastSwiftUpdateCheck = 0730; 303 | LastUpgradeCheck = 0730; 304 | ORGANIZATIONNAME = "Tomonori Ueno"; 305 | TargetAttributes = { 306 | 75E4E1791D3F68A5001496FC = { 307 | CreatedOnToolsVersion = 7.3.1; 308 | }; 309 | 75E4E18D1D3F68A5001496FC = { 310 | CreatedOnToolsVersion = 7.3.1; 311 | TestTargetID = 75E4E1791D3F68A5001496FC; 312 | }; 313 | 75E4E1981D3F68A5001496FC = { 314 | CreatedOnToolsVersion = 7.3.1; 315 | TestTargetID = 75E4E1791D3F68A5001496FC; 316 | }; 317 | }; 318 | }; 319 | buildConfigurationList = 75E4E1751D3F68A5001496FC /* Build configuration list for PBXProject "ScreenSelectorExample" */; 320 | compatibilityVersion = "Xcode 3.2"; 321 | developmentRegion = English; 322 | hasScannedForEncodings = 0; 323 | knownRegions = ( 324 | en, 325 | Base, 326 | ); 327 | mainGroup = 75E4E1711D3F68A5001496FC; 328 | productRefGroup = 75E4E17B1D3F68A5001496FC /* Products */; 329 | projectDirPath = ""; 330 | projectReferences = ( 331 | { 332 | ProductGroup = 2D3850131D6188C000DA1F6A /* Products */; 333 | ProjectRef = 2D3850121D6188C000DA1F6A /* ScreenSelector.xcodeproj */; 334 | }, 335 | ); 336 | projectRoot = ""; 337 | targets = ( 338 | 75E4E1791D3F68A5001496FC /* ScreenSelectorExample */, 339 | 75E4E18D1D3F68A5001496FC /* ScreenSelectorExampleTests */, 340 | 75E4E1981D3F68A5001496FC /* ScreenSelectorExampleUITests */, 341 | ); 342 | }; 343 | /* End PBXProject section */ 344 | 345 | /* Begin PBXReferenceProxy section */ 346 | 2D3850181D6188C000DA1F6A /* ScreenSelector.framework */ = { 347 | isa = PBXReferenceProxy; 348 | fileType = wrapper.framework; 349 | path = ScreenSelector.framework; 350 | remoteRef = 2D3850171D6188C000DA1F6A /* PBXContainerItemProxy */; 351 | sourceTree = BUILT_PRODUCTS_DIR; 352 | }; 353 | 2D38501A1D6188C000DA1F6A /* ScreenSelectorTests.xctest */ = { 354 | isa = PBXReferenceProxy; 355 | fileType = wrapper.cfbundle; 356 | path = ScreenSelectorTests.xctest; 357 | remoteRef = 2D3850191D6188C000DA1F6A /* PBXContainerItemProxy */; 358 | sourceTree = BUILT_PRODUCTS_DIR; 359 | }; 360 | /* End PBXReferenceProxy section */ 361 | 362 | /* Begin PBXResourcesBuildPhase section */ 363 | 75E4E1781D3F68A5001496FC /* Resources */ = { 364 | isa = PBXResourcesBuildPhase; 365 | buildActionMask = 2147483647; 366 | files = ( 367 | 75E4E1881D3F68A5001496FC /* LaunchScreen.storyboard in Resources */, 368 | 2D3850291D61909800DA1F6A /* ContentViewController.storyboard in Resources */, 369 | 75E4E1851D3F68A5001496FC /* Assets.xcassets in Resources */, 370 | 752025501D3F9AA5000096E1 /* example_bg@2x.jpg in Resources */, 371 | 75E4E1831D3F68A5001496FC /* ExtendedViewController.storyboard in Resources */, 372 | 2D38502F1D61BE6100DA1F6A /* ViewController.storyboard in Resources */, 373 | ); 374 | runOnlyForDeploymentPostprocessing = 0; 375 | }; 376 | 75E4E18C1D3F68A5001496FC /* Resources */ = { 377 | isa = PBXResourcesBuildPhase; 378 | buildActionMask = 2147483647; 379 | files = ( 380 | ); 381 | runOnlyForDeploymentPostprocessing = 0; 382 | }; 383 | 75E4E1971D3F68A5001496FC /* Resources */ = { 384 | isa = PBXResourcesBuildPhase; 385 | buildActionMask = 2147483647; 386 | files = ( 387 | ); 388 | runOnlyForDeploymentPostprocessing = 0; 389 | }; 390 | /* End PBXResourcesBuildPhase section */ 391 | 392 | /* Begin PBXSourcesBuildPhase section */ 393 | 75E4E1761D3F68A5001496FC /* Sources */ = { 394 | isa = PBXSourcesBuildPhase; 395 | buildActionMask = 2147483647; 396 | files = ( 397 | 2D38502D1D61BE5600DA1F6A /* ViewController.swift in Sources */, 398 | 2D38502B1D61909800DA1F6A /* DummyScreenGenerator.swift in Sources */, 399 | 75E4E1801D3F68A5001496FC /* ExtendedViewController.swift in Sources */, 400 | 2D38502A1D61909800DA1F6A /* ContentViewController.swift in Sources */, 401 | 75E4E17E1D3F68A5001496FC /* AppDelegate.swift in Sources */, 402 | ); 403 | runOnlyForDeploymentPostprocessing = 0; 404 | }; 405 | 75E4E18A1D3F68A5001496FC /* Sources */ = { 406 | isa = PBXSourcesBuildPhase; 407 | buildActionMask = 2147483647; 408 | files = ( 409 | 75E4E1931D3F68A5001496FC /* ScreenSelectorExampleTests.swift in Sources */, 410 | ); 411 | runOnlyForDeploymentPostprocessing = 0; 412 | }; 413 | 75E4E1951D3F68A5001496FC /* Sources */ = { 414 | isa = PBXSourcesBuildPhase; 415 | buildActionMask = 2147483647; 416 | files = ( 417 | 75E4E19E1D3F68A5001496FC /* ScreenSelectorExampleUITests.swift in Sources */, 418 | ); 419 | runOnlyForDeploymentPostprocessing = 0; 420 | }; 421 | /* End PBXSourcesBuildPhase section */ 422 | 423 | /* Begin PBXTargetDependency section */ 424 | 2D38501C1D6188DC00DA1F6A /* PBXTargetDependency */ = { 425 | isa = PBXTargetDependency; 426 | name = ScreenSelector; 427 | targetProxy = 2D38501B1D6188DC00DA1F6A /* PBXContainerItemProxy */; 428 | }; 429 | 75E4E1901D3F68A5001496FC /* PBXTargetDependency */ = { 430 | isa = PBXTargetDependency; 431 | target = 75E4E1791D3F68A5001496FC /* ScreenSelectorExample */; 432 | targetProxy = 75E4E18F1D3F68A5001496FC /* PBXContainerItemProxy */; 433 | }; 434 | 75E4E19B1D3F68A5001496FC /* PBXTargetDependency */ = { 435 | isa = PBXTargetDependency; 436 | target = 75E4E1791D3F68A5001496FC /* ScreenSelectorExample */; 437 | targetProxy = 75E4E19A1D3F68A5001496FC /* PBXContainerItemProxy */; 438 | }; 439 | /* End PBXTargetDependency section */ 440 | 441 | /* Begin PBXVariantGroup section */ 442 | 75E4E1811D3F68A5001496FC /* ExtendedViewController.storyboard */ = { 443 | isa = PBXVariantGroup; 444 | children = ( 445 | 75E4E1821D3F68A5001496FC /* Base */, 446 | ); 447 | name = ExtendedViewController.storyboard; 448 | sourceTree = ""; 449 | }; 450 | 75E4E1861D3F68A5001496FC /* LaunchScreen.storyboard */ = { 451 | isa = PBXVariantGroup; 452 | children = ( 453 | 75E4E1871D3F68A5001496FC /* Base */, 454 | ); 455 | name = LaunchScreen.storyboard; 456 | sourceTree = ""; 457 | }; 458 | /* End PBXVariantGroup section */ 459 | 460 | /* Begin XCBuildConfiguration section */ 461 | 75E4E1A01D3F68A5001496FC /* Debug */ = { 462 | isa = XCBuildConfiguration; 463 | buildSettings = { 464 | ALWAYS_SEARCH_USER_PATHS = NO; 465 | CLANG_ANALYZER_NONNULL = YES; 466 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 467 | CLANG_CXX_LIBRARY = "libc++"; 468 | CLANG_ENABLE_MODULES = YES; 469 | CLANG_ENABLE_OBJC_ARC = YES; 470 | CLANG_WARN_BOOL_CONVERSION = YES; 471 | CLANG_WARN_CONSTANT_CONVERSION = YES; 472 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 473 | CLANG_WARN_EMPTY_BODY = YES; 474 | CLANG_WARN_ENUM_CONVERSION = YES; 475 | CLANG_WARN_INT_CONVERSION = YES; 476 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 477 | CLANG_WARN_UNREACHABLE_CODE = YES; 478 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 479 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 480 | COPY_PHASE_STRIP = NO; 481 | DEBUG_INFORMATION_FORMAT = dwarf; 482 | ENABLE_STRICT_OBJC_MSGSEND = YES; 483 | ENABLE_TESTABILITY = YES; 484 | GCC_C_LANGUAGE_STANDARD = gnu99; 485 | GCC_DYNAMIC_NO_PIC = NO; 486 | GCC_NO_COMMON_BLOCKS = YES; 487 | GCC_OPTIMIZATION_LEVEL = 0; 488 | GCC_PREPROCESSOR_DEFINITIONS = ( 489 | "DEBUG=1", 490 | "$(inherited)", 491 | ); 492 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 493 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 494 | GCC_WARN_UNDECLARED_SELECTOR = YES; 495 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 496 | GCC_WARN_UNUSED_FUNCTION = YES; 497 | GCC_WARN_UNUSED_VARIABLE = YES; 498 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 499 | MTL_ENABLE_DEBUG_INFO = YES; 500 | ONLY_ACTIVE_ARCH = YES; 501 | SDKROOT = iphoneos; 502 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 503 | }; 504 | name = Debug; 505 | }; 506 | 75E4E1A11D3F68A5001496FC /* Release */ = { 507 | isa = XCBuildConfiguration; 508 | buildSettings = { 509 | ALWAYS_SEARCH_USER_PATHS = NO; 510 | CLANG_ANALYZER_NONNULL = YES; 511 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 512 | CLANG_CXX_LIBRARY = "libc++"; 513 | CLANG_ENABLE_MODULES = YES; 514 | CLANG_ENABLE_OBJC_ARC = YES; 515 | CLANG_WARN_BOOL_CONVERSION = YES; 516 | CLANG_WARN_CONSTANT_CONVERSION = YES; 517 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 518 | CLANG_WARN_EMPTY_BODY = YES; 519 | CLANG_WARN_ENUM_CONVERSION = YES; 520 | CLANG_WARN_INT_CONVERSION = YES; 521 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 522 | CLANG_WARN_UNREACHABLE_CODE = YES; 523 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 524 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 525 | COPY_PHASE_STRIP = NO; 526 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 527 | ENABLE_NS_ASSERTIONS = NO; 528 | ENABLE_STRICT_OBJC_MSGSEND = YES; 529 | GCC_C_LANGUAGE_STANDARD = gnu99; 530 | GCC_NO_COMMON_BLOCKS = YES; 531 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 532 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 533 | GCC_WARN_UNDECLARED_SELECTOR = YES; 534 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 535 | GCC_WARN_UNUSED_FUNCTION = YES; 536 | GCC_WARN_UNUSED_VARIABLE = YES; 537 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 538 | MTL_ENABLE_DEBUG_INFO = NO; 539 | SDKROOT = iphoneos; 540 | VALIDATE_PRODUCT = YES; 541 | }; 542 | name = Release; 543 | }; 544 | 75E4E1A31D3F68A5001496FC /* Debug */ = { 545 | isa = XCBuildConfiguration; 546 | buildSettings = { 547 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 548 | FRAMEWORK_SEARCH_PATHS = ( 549 | "$(inherited)", 550 | "$(PROJECT_DIR)/ScreenSelectorExample", 551 | ); 552 | INFOPLIST_FILE = ScreenSelectorExample/Info.plist; 553 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 554 | PRODUCT_BUNDLE_IDENTIFIER = com.tueno.ScreenSelectorExample; 555 | PRODUCT_NAME = "$(TARGET_NAME)"; 556 | }; 557 | name = Debug; 558 | }; 559 | 75E4E1A41D3F68A5001496FC /* Release */ = { 560 | isa = XCBuildConfiguration; 561 | buildSettings = { 562 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 563 | FRAMEWORK_SEARCH_PATHS = ( 564 | "$(inherited)", 565 | "$(PROJECT_DIR)/ScreenSelectorExample", 566 | ); 567 | INFOPLIST_FILE = ScreenSelectorExample/Info.plist; 568 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 569 | PRODUCT_BUNDLE_IDENTIFIER = com.tueno.ScreenSelectorExample; 570 | PRODUCT_NAME = "$(TARGET_NAME)"; 571 | }; 572 | name = Release; 573 | }; 574 | 75E4E1A61D3F68A5001496FC /* Debug */ = { 575 | isa = XCBuildConfiguration; 576 | buildSettings = { 577 | BUNDLE_LOADER = "$(TEST_HOST)"; 578 | INFOPLIST_FILE = ScreenSelectorExampleTests/Info.plist; 579 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 580 | PRODUCT_BUNDLE_IDENTIFIER = com.tueno.ScreenSelectorExampleTests; 581 | PRODUCT_NAME = "$(TARGET_NAME)"; 582 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ScreenSelectorExample.app/ScreenSelectorExample"; 583 | }; 584 | name = Debug; 585 | }; 586 | 75E4E1A71D3F68A5001496FC /* Release */ = { 587 | isa = XCBuildConfiguration; 588 | buildSettings = { 589 | BUNDLE_LOADER = "$(TEST_HOST)"; 590 | INFOPLIST_FILE = ScreenSelectorExampleTests/Info.plist; 591 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 592 | PRODUCT_BUNDLE_IDENTIFIER = com.tueno.ScreenSelectorExampleTests; 593 | PRODUCT_NAME = "$(TARGET_NAME)"; 594 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ScreenSelectorExample.app/ScreenSelectorExample"; 595 | }; 596 | name = Release; 597 | }; 598 | 75E4E1A91D3F68A5001496FC /* Debug */ = { 599 | isa = XCBuildConfiguration; 600 | buildSettings = { 601 | INFOPLIST_FILE = ScreenSelectorExampleUITests/Info.plist; 602 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 603 | PRODUCT_BUNDLE_IDENTIFIER = com.tueno.ScreenSelectorExampleUITests; 604 | PRODUCT_NAME = "$(TARGET_NAME)"; 605 | TEST_TARGET_NAME = ScreenSelectorExample; 606 | }; 607 | name = Debug; 608 | }; 609 | 75E4E1AA1D3F68A5001496FC /* Release */ = { 610 | isa = XCBuildConfiguration; 611 | buildSettings = { 612 | INFOPLIST_FILE = ScreenSelectorExampleUITests/Info.plist; 613 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 614 | PRODUCT_BUNDLE_IDENTIFIER = com.tueno.ScreenSelectorExampleUITests; 615 | PRODUCT_NAME = "$(TARGET_NAME)"; 616 | TEST_TARGET_NAME = ScreenSelectorExample; 617 | }; 618 | name = Release; 619 | }; 620 | /* End XCBuildConfiguration section */ 621 | 622 | /* Begin XCConfigurationList section */ 623 | 75E4E1751D3F68A5001496FC /* Build configuration list for PBXProject "ScreenSelectorExample" */ = { 624 | isa = XCConfigurationList; 625 | buildConfigurations = ( 626 | 75E4E1A01D3F68A5001496FC /* Debug */, 627 | 75E4E1A11D3F68A5001496FC /* Release */, 628 | ); 629 | defaultConfigurationIsVisible = 0; 630 | defaultConfigurationName = Release; 631 | }; 632 | 75E4E1A21D3F68A5001496FC /* Build configuration list for PBXNativeTarget "ScreenSelectorExample" */ = { 633 | isa = XCConfigurationList; 634 | buildConfigurations = ( 635 | 75E4E1A31D3F68A5001496FC /* Debug */, 636 | 75E4E1A41D3F68A5001496FC /* Release */, 637 | ); 638 | defaultConfigurationIsVisible = 0; 639 | defaultConfigurationName = Release; 640 | }; 641 | 75E4E1A51D3F68A5001496FC /* Build configuration list for PBXNativeTarget "ScreenSelectorExampleTests" */ = { 642 | isa = XCConfigurationList; 643 | buildConfigurations = ( 644 | 75E4E1A61D3F68A5001496FC /* Debug */, 645 | 75E4E1A71D3F68A5001496FC /* Release */, 646 | ); 647 | defaultConfigurationIsVisible = 0; 648 | defaultConfigurationName = Release; 649 | }; 650 | 75E4E1A81D3F68A5001496FC /* Build configuration list for PBXNativeTarget "ScreenSelectorExampleUITests" */ = { 651 | isa = XCConfigurationList; 652 | buildConfigurations = ( 653 | 75E4E1A91D3F68A5001496FC /* Debug */, 654 | 75E4E1AA1D3F68A5001496FC /* Release */, 655 | ); 656 | defaultConfigurationIsVisible = 0; 657 | defaultConfigurationName = Release; 658 | }; 659 | /* End XCConfigurationList section */ 660 | }; 661 | rootObject = 75E4E1721D3F68A5001496FC /* Project object */; 662 | } 663 | -------------------------------------------------------------------------------- /Example/ScreenSelectorExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Example/ScreenSelectorExample.xcodeproj/xcshareddata/xcschemes/ScreenSelectorExample.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 43 | 49 | 50 | 51 | 52 | 53 | 59 | 60 | 61 | 62 | 63 | 64 | 74 | 76 | 82 | 83 | 84 | 85 | 86 | 87 | 93 | 95 | 101 | 102 | 103 | 104 | 106 | 107 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /Example/ScreenSelectorExample.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Example/ScreenSelectorExample/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // ScreenSelectorExample 4 | // 5 | // Created by Tomonori Ueno on 2016/07/20. 6 | // Copyright © 2016年 Tomonori Ueno. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { 18 | return true 19 | } 20 | 21 | func applicationWillResignActive(application: UIApplication) { 22 | } 23 | 24 | func applicationDidEnterBackground(application: UIApplication) { 25 | } 26 | 27 | func applicationWillEnterForeground(application: UIApplication) { 28 | } 29 | 30 | func applicationDidBecomeActive(application: UIApplication) { 31 | } 32 | 33 | func applicationWillTerminate(application: UIApplication) { 34 | } 35 | 36 | 37 | } 38 | 39 | -------------------------------------------------------------------------------- /Example/ScreenSelectorExample/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /Example/ScreenSelectorExample/Base.lproj/ExtendedViewController.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /Example/ScreenSelectorExample/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /Example/ScreenSelectorExample/ExtendedViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ExtendedViewController.swift 3 | // ScreenSelectorExample 4 | // 5 | // Created by Tomonori Ueno on 2016/07/20. 6 | // Copyright © 2016年 Tomonori Ueno. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import ScreenSelector 11 | 12 | final class ExtendedViewController: ScreenSelectorViewController { 13 | 14 | @IBOutlet weak var sampleImageView: UIImageView! 15 | 16 | override func viewDidLoad() { 17 | super.viewDidLoad() 18 | self.screenSelectorView.previewReflectionEnabled = true 19 | self.previewableScreens = DummyScreenGenerator.generateViewControllers() 20 | self.screenSelectorView.reloadData() 21 | } 22 | 23 | override func viewDidAppear(animated: Bool) { 24 | super.viewDidAppear(animated) 25 | } 26 | 27 | override func didReceiveMemoryWarning() { 28 | super.didReceiveMemoryWarning() 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /Example/ScreenSelectorExample/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | ViewController 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /Example/ScreenSelectorExample/ViewController.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /Example/ScreenSelectorExample/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // ScreenSelectorExample 4 | // 5 | // Created by Tomonori on 2016/08/15. 6 | // Copyright © 2016年 Tomonori Ueno. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import ScreenSelector 11 | 12 | final class ViewController: UIViewController, ScreenSelectorViewDelegate, ScreenSelectorViewDataSource, UIViewControllerTransitioningDelegate { 13 | 14 | @IBOutlet weak var screenSelectorView: ScreenSelectorView! 15 | private var previewableScreens: [ScreenSelectorPreviewable] = [] 16 | 17 | weak var transitionFromPreviewImageView: UIImageView? 18 | 19 | override func viewDidLoad() { 20 | super.viewDidLoad() 21 | previewableScreens = DummyScreenGenerator.generateViewControllers() 22 | screenSelectorView.delegate = self 23 | screenSelectorView.dataSource = self 24 | } 25 | 26 | override func viewDidAppear(animated: Bool) { 27 | super.viewDidAppear(animated) 28 | } 29 | 30 | // MARK: - ScreenSelectorView Delegate 31 | 32 | func screenSelectorViewDidTapScreenAtIndex(index: Int, previewView: ScreenPreviewViewProtocol) { 33 | transitionFromPreviewImageView = previewView.imageView 34 | if let viewController = previewableScreens[index] as? UIViewController { 35 | viewController.transitioningDelegate = self 36 | presentViewController(viewController, animated: true) { 37 | // Move selected preview to center of screen. 38 | self.screenSelectorView.scrollToIndex(index, animated: false) 39 | } 40 | } 41 | } 42 | 43 | // MARK: - ScreenSelectorView DataSource 44 | 45 | func numberOfScreens() -> Int { 46 | return previewableScreens.count 47 | } 48 | 49 | func previewViewForScreenAtIndex(index: Int) -> ScreenPreviewViewProtocol { 50 | let size = CGSize(width: UIScreen.mainScreen().bounds.width * 0.6, height: UIScreen.mainScreen().bounds.height * 0.6) 51 | let screenView = ScreenPreviewView(frame: CGRect(origin: CGPoint.zero, size: size)) 52 | let previewable = previewableScreens[index] 53 | let previewImage: UIImage? = previewable.thumbnailImage 54 | guard let vc = previewable as? UIViewController else { 55 | fatalError() 56 | } 57 | let titleText: String? = vc.title 58 | screenView.titleLabel.text = titleText 59 | screenView.imageView.image = previewImage 60 | return screenView 61 | } 62 | 63 | // MARK: - 64 | 65 | func animationControllerForPresentedController(presented: UIViewController, 66 | presentingController presenting: UIViewController, 67 | sourceController source: UIViewController) -> UIViewControllerAnimatedTransitioning? { 68 | let animator = ScreenSelectorTransitioningAnimator(isReverse: false, moveFromImageView: transitionFromPreviewImageView) 69 | return animator 70 | } 71 | 72 | func animationControllerForDismissedController(dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? { 73 | let animator = ScreenSelectorTransitioningAnimator(isReverse: true, moveFromImageView: transitionFromPreviewImageView) 74 | return animator 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /Example/ScreenSelectorExample/example_bg@2x.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tueno/ScreenSelector/11eeacd66d52c4673e695cafacfe060f25cefc6e/Example/ScreenSelectorExample/example_bg@2x.jpg -------------------------------------------------------------------------------- /Example/ScreenSelectorExampleTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /Example/ScreenSelectorExampleTests/ScreenSelectorExampleTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ScreenSelectorExampleTests.swift 3 | // ScreenSelectorExampleTests 4 | // 5 | // Created by Tomonori Ueno on 2016/07/20. 6 | // Copyright © 2016年 Tomonori Ueno. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import ScreenSelectorExample 11 | 12 | class ScreenSelectorExampleTests: XCTestCase { 13 | 14 | override func setUp() { 15 | super.setUp() 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | } 18 | 19 | override func tearDown() { 20 | // Put teardown code here. This method is called after the invocation of each test method in the class. 21 | super.tearDown() 22 | } 23 | 24 | func testExample() { 25 | // This is an example of a functional test case. 26 | // Use XCTAssert and related functions to verify your tests produce the correct results. 27 | } 28 | 29 | func testPerformanceExample() { 30 | // This is an example of a performance test case. 31 | self.measureBlock { 32 | // Put the code you want to measure the time of here. 33 | } 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /Example/ScreenSelectorExampleUITests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /Example/ScreenSelectorExampleUITests/ScreenSelectorExampleUITests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ScreenSelectorExampleUITests.swift 3 | // ScreenSelectorExampleUITests 4 | // 5 | // Created by Tomonori Ueno on 2016/07/20. 6 | // Copyright © 2016年 Tomonori Ueno. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | class ScreenSelectorExampleUITests: XCTestCase { 12 | 13 | override func setUp() { 14 | super.setUp() 15 | 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | 18 | // In UI tests it is usually best to stop immediately when a failure occurs. 19 | continueAfterFailure = false 20 | // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method. 21 | XCUIApplication().launch() 22 | 23 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. 24 | } 25 | 26 | override func tearDown() { 27 | // Put teardown code here. This method is called after the invocation of each test method in the class. 28 | super.tearDown() 29 | } 30 | 31 | func testExample() { 32 | // Use recording to get started writing UI tests. 33 | // Use XCTAssert and related functions to verify your tests produce the correct results. 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Tueno 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ScreenSelector 2 | 3 | Provides screen selector UI for iOS. 4 | 5 | ![preview](https://github.com/Tueno/ScreenSelector/blob/master/preview.gif?raw=true) 6 | 7 | ## Feature 8 | ### ScreenSelectorView 9 | * IBDesignable supporting. 10 | * IBInspectable supporting. 11 | * Can use your custom preview view that is compatible with ScreenPreviewViewProtocol. 12 | * Can show preview view's specular reflection. 13 | 14 | ### ScreenSelectorViewController 15 | * Wrapper class of `ScreenSelectorView`. 16 | * Easy to use. 17 | 18 | ## Usage 19 | 20 | * Extend `ScreenSelectorViewController` and set `UIViewController` instance that conforms to `ScreenSelectorPreviewable` protocol to `previewableScreens` property. 21 | 22 | OR 23 | 24 | * Add `ScreenSelectorView` instance to any view and set `dataSource` and `delegate`. (You can use custom preview view class.) 25 | 26 | (See `Example` for details.) 27 | 28 | ## Requirements 29 | 30 | * iOS8.0+ 31 | * Xcode7.3+ 32 | 33 | ## Installation 34 | 35 | ### Carthage 36 | 37 | Add this line to your Cartfile. 38 | ```ogdl 39 | github "Tueno/ScreenSelector" 40 | ``` 41 | 42 | -------------------------------------------------------------------------------- /ScreenSelector.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 2D384F991D6177E200DA1F6A /* ScreenSelector.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2D384F8E1D6177E200DA1F6A /* ScreenSelector.framework */; }; 11 | 2D384F9E1D6177E200DA1F6A /* ScreenSelectorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D384F9D1D6177E200DA1F6A /* ScreenSelectorTests.swift */; }; 12 | 2D384FB21D617A8200DA1F6A /* DummyScreenGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D384FAA1D617A8200DA1F6A /* DummyScreenGenerator.swift */; }; 13 | 2D384FB31D617A8200DA1F6A /* ReversibleAnimatedTransitioning.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D384FAB1D617A8200DA1F6A /* ReversibleAnimatedTransitioning.swift */; }; 14 | 2D384FB41D617A8200DA1F6A /* ScreenPreviewView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D384FAC1D617A8200DA1F6A /* ScreenPreviewView.swift */; }; 15 | 2D384FB51D617A8200DA1F6A /* ScreenSelectorPreviewable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D384FAD1D617A8200DA1F6A /* ScreenSelectorPreviewable.swift */; }; 16 | 2D384FB61D617A8200DA1F6A /* ScreenSelectorTransitioningAnimator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D384FAE1D617A8200DA1F6A /* ScreenSelectorTransitioningAnimator.swift */; }; 17 | 2D384FB71D617A8200DA1F6A /* ScreenSelectorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D384FAF1D617A8200DA1F6A /* ScreenSelectorView.swift */; }; 18 | 2D384FB81D617A8200DA1F6A /* ScreenSelectorViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D384FB01D617A8200DA1F6A /* ScreenSelectorViewController.swift */; }; 19 | 2D384FB91D617A8200DA1F6A /* SpecularReflectionLayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D384FB11D617A8200DA1F6A /* SpecularReflectionLayer.swift */; }; 20 | 2D384FFD1D6184A500DA1F6A /* ScreenSelector.h in Headers */ = {isa = PBXBuildFile; fileRef = 2D384FFC1D61842100DA1F6A /* ScreenSelector.h */; settings = {ATTRIBUTES = (Public, ); }; }; 21 | /* End PBXBuildFile section */ 22 | 23 | /* Begin PBXContainerItemProxy section */ 24 | 2D384F9A1D6177E200DA1F6A /* PBXContainerItemProxy */ = { 25 | isa = PBXContainerItemProxy; 26 | containerPortal = 2D384F851D6177E200DA1F6A /* Project object */; 27 | proxyType = 1; 28 | remoteGlobalIDString = 2D384F8D1D6177E200DA1F6A; 29 | remoteInfo = ScreenSelector; 30 | }; 31 | /* End PBXContainerItemProxy section */ 32 | 33 | /* Begin PBXFileReference section */ 34 | 2D384F8E1D6177E200DA1F6A /* ScreenSelector.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ScreenSelector.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 35 | 2D384F931D6177E200DA1F6A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 36 | 2D384F981D6177E200DA1F6A /* ScreenSelectorTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ScreenSelectorTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 37 | 2D384F9D1D6177E200DA1F6A /* ScreenSelectorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScreenSelectorTests.swift; sourceTree = ""; }; 38 | 2D384F9F1D6177E200DA1F6A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 39 | 2D384FAA1D617A8200DA1F6A /* DummyScreenGenerator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DummyScreenGenerator.swift; sourceTree = ""; }; 40 | 2D384FAB1D617A8200DA1F6A /* ReversibleAnimatedTransitioning.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReversibleAnimatedTransitioning.swift; sourceTree = ""; }; 41 | 2D384FAC1D617A8200DA1F6A /* ScreenPreviewView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScreenPreviewView.swift; sourceTree = ""; }; 42 | 2D384FAD1D617A8200DA1F6A /* ScreenSelectorPreviewable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScreenSelectorPreviewable.swift; sourceTree = ""; }; 43 | 2D384FAE1D617A8200DA1F6A /* ScreenSelectorTransitioningAnimator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScreenSelectorTransitioningAnimator.swift; sourceTree = ""; }; 44 | 2D384FAF1D617A8200DA1F6A /* ScreenSelectorView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScreenSelectorView.swift; sourceTree = ""; }; 45 | 2D384FB01D617A8200DA1F6A /* ScreenSelectorViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScreenSelectorViewController.swift; sourceTree = ""; }; 46 | 2D384FB11D617A8200DA1F6A /* SpecularReflectionLayer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SpecularReflectionLayer.swift; sourceTree = ""; }; 47 | 2D384FFC1D61842100DA1F6A /* ScreenSelector.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ScreenSelector.h; sourceTree = ""; }; 48 | /* End PBXFileReference section */ 49 | 50 | /* Begin PBXFrameworksBuildPhase section */ 51 | 2D384F8A1D6177E200DA1F6A /* Frameworks */ = { 52 | isa = PBXFrameworksBuildPhase; 53 | buildActionMask = 2147483647; 54 | files = ( 55 | ); 56 | runOnlyForDeploymentPostprocessing = 0; 57 | }; 58 | 2D384F951D6177E200DA1F6A /* Frameworks */ = { 59 | isa = PBXFrameworksBuildPhase; 60 | buildActionMask = 2147483647; 61 | files = ( 62 | 2D384F991D6177E200DA1F6A /* ScreenSelector.framework in Frameworks */, 63 | ); 64 | runOnlyForDeploymentPostprocessing = 0; 65 | }; 66 | /* End PBXFrameworksBuildPhase section */ 67 | 68 | /* Begin PBXGroup section */ 69 | 2D384F841D6177E200DA1F6A = { 70 | isa = PBXGroup; 71 | children = ( 72 | 2D384F901D6177E200DA1F6A /* ScreenSelector */, 73 | 2D384F9C1D6177E200DA1F6A /* ScreenSelectorTests */, 74 | 2D384F8F1D6177E200DA1F6A /* Products */, 75 | ); 76 | sourceTree = ""; 77 | }; 78 | 2D384F8F1D6177E200DA1F6A /* Products */ = { 79 | isa = PBXGroup; 80 | children = ( 81 | 2D384F8E1D6177E200DA1F6A /* ScreenSelector.framework */, 82 | 2D384F981D6177E200DA1F6A /* ScreenSelectorTests.xctest */, 83 | ); 84 | name = Products; 85 | sourceTree = ""; 86 | }; 87 | 2D384F901D6177E200DA1F6A /* ScreenSelector */ = { 88 | isa = PBXGroup; 89 | children = ( 90 | 2D384FBE1D617AD600DA1F6A /* Dummy */, 91 | 2D384FAB1D617A8200DA1F6A /* ReversibleAnimatedTransitioning.swift */, 92 | 2D384FAC1D617A8200DA1F6A /* ScreenPreviewView.swift */, 93 | 2D384FAD1D617A8200DA1F6A /* ScreenSelectorPreviewable.swift */, 94 | 2D384FAE1D617A8200DA1F6A /* ScreenSelectorTransitioningAnimator.swift */, 95 | 2D384FAF1D617A8200DA1F6A /* ScreenSelectorView.swift */, 96 | 2D384FB01D617A8200DA1F6A /* ScreenSelectorViewController.swift */, 97 | 2D384FB11D617A8200DA1F6A /* SpecularReflectionLayer.swift */, 98 | 2D384F931D6177E200DA1F6A /* Info.plist */, 99 | 2D384FFC1D61842100DA1F6A /* ScreenSelector.h */, 100 | ); 101 | path = ScreenSelector; 102 | sourceTree = ""; 103 | }; 104 | 2D384F9C1D6177E200DA1F6A /* ScreenSelectorTests */ = { 105 | isa = PBXGroup; 106 | children = ( 107 | 2D384F9D1D6177E200DA1F6A /* ScreenSelectorTests.swift */, 108 | 2D384F9F1D6177E200DA1F6A /* Info.plist */, 109 | ); 110 | path = ScreenSelectorTests; 111 | sourceTree = ""; 112 | }; 113 | 2D384FBE1D617AD600DA1F6A /* Dummy */ = { 114 | isa = PBXGroup; 115 | children = ( 116 | 2D384FAA1D617A8200DA1F6A /* DummyScreenGenerator.swift */, 117 | ); 118 | name = Dummy; 119 | sourceTree = ""; 120 | }; 121 | /* End PBXGroup section */ 122 | 123 | /* Begin PBXHeadersBuildPhase section */ 124 | 2D384F8B1D6177E200DA1F6A /* Headers */ = { 125 | isa = PBXHeadersBuildPhase; 126 | buildActionMask = 2147483647; 127 | files = ( 128 | 2D384FFD1D6184A500DA1F6A /* ScreenSelector.h in Headers */, 129 | ); 130 | runOnlyForDeploymentPostprocessing = 0; 131 | }; 132 | /* End PBXHeadersBuildPhase section */ 133 | 134 | /* Begin PBXNativeTarget section */ 135 | 2D384F8D1D6177E200DA1F6A /* ScreenSelector */ = { 136 | isa = PBXNativeTarget; 137 | buildConfigurationList = 2D384FA21D6177E200DA1F6A /* Build configuration list for PBXNativeTarget "ScreenSelector" */; 138 | buildPhases = ( 139 | 2D384F891D6177E200DA1F6A /* Sources */, 140 | 2D384F8A1D6177E200DA1F6A /* Frameworks */, 141 | 2D384F8B1D6177E200DA1F6A /* Headers */, 142 | 2D384F8C1D6177E200DA1F6A /* Resources */, 143 | ); 144 | buildRules = ( 145 | ); 146 | dependencies = ( 147 | ); 148 | name = ScreenSelector; 149 | productName = ScreenSelector; 150 | productReference = 2D384F8E1D6177E200DA1F6A /* ScreenSelector.framework */; 151 | productType = "com.apple.product-type.framework"; 152 | }; 153 | 2D384F971D6177E200DA1F6A /* ScreenSelectorTests */ = { 154 | isa = PBXNativeTarget; 155 | buildConfigurationList = 2D384FA51D6177E200DA1F6A /* Build configuration list for PBXNativeTarget "ScreenSelectorTests" */; 156 | buildPhases = ( 157 | 2D384F941D6177E200DA1F6A /* Sources */, 158 | 2D384F951D6177E200DA1F6A /* Frameworks */, 159 | 2D384F961D6177E200DA1F6A /* Resources */, 160 | ); 161 | buildRules = ( 162 | ); 163 | dependencies = ( 164 | 2D384F9B1D6177E200DA1F6A /* PBXTargetDependency */, 165 | ); 166 | name = ScreenSelectorTests; 167 | productName = ScreenSelectorTests; 168 | productReference = 2D384F981D6177E200DA1F6A /* ScreenSelectorTests.xctest */; 169 | productType = "com.apple.product-type.bundle.unit-test"; 170 | }; 171 | /* End PBXNativeTarget section */ 172 | 173 | /* Begin PBXProject section */ 174 | 2D384F851D6177E200DA1F6A /* Project object */ = { 175 | isa = PBXProject; 176 | attributes = { 177 | LastSwiftUpdateCheck = 0730; 178 | LastUpgradeCheck = 0730; 179 | ORGANIZATIONNAME = "Tomonori Ueno"; 180 | TargetAttributes = { 181 | 2D384F8D1D6177E200DA1F6A = { 182 | CreatedOnToolsVersion = 7.3.1; 183 | }; 184 | 2D384F971D6177E200DA1F6A = { 185 | CreatedOnToolsVersion = 7.3.1; 186 | }; 187 | }; 188 | }; 189 | buildConfigurationList = 2D384F881D6177E200DA1F6A /* Build configuration list for PBXProject "ScreenSelector" */; 190 | compatibilityVersion = "Xcode 3.2"; 191 | developmentRegion = English; 192 | hasScannedForEncodings = 0; 193 | knownRegions = ( 194 | en, 195 | ); 196 | mainGroup = 2D384F841D6177E200DA1F6A; 197 | productRefGroup = 2D384F8F1D6177E200DA1F6A /* Products */; 198 | projectDirPath = ""; 199 | projectRoot = ""; 200 | targets = ( 201 | 2D384F8D1D6177E200DA1F6A /* ScreenSelector */, 202 | 2D384F971D6177E200DA1F6A /* ScreenSelectorTests */, 203 | ); 204 | }; 205 | /* End PBXProject section */ 206 | 207 | /* Begin PBXResourcesBuildPhase section */ 208 | 2D384F8C1D6177E200DA1F6A /* Resources */ = { 209 | isa = PBXResourcesBuildPhase; 210 | buildActionMask = 2147483647; 211 | files = ( 212 | ); 213 | runOnlyForDeploymentPostprocessing = 0; 214 | }; 215 | 2D384F961D6177E200DA1F6A /* Resources */ = { 216 | isa = PBXResourcesBuildPhase; 217 | buildActionMask = 2147483647; 218 | files = ( 219 | ); 220 | runOnlyForDeploymentPostprocessing = 0; 221 | }; 222 | /* End PBXResourcesBuildPhase section */ 223 | 224 | /* Begin PBXSourcesBuildPhase section */ 225 | 2D384F891D6177E200DA1F6A /* Sources */ = { 226 | isa = PBXSourcesBuildPhase; 227 | buildActionMask = 2147483647; 228 | files = ( 229 | 2D384FB41D617A8200DA1F6A /* ScreenPreviewView.swift in Sources */, 230 | 2D384FB61D617A8200DA1F6A /* ScreenSelectorTransitioningAnimator.swift in Sources */, 231 | 2D384FB81D617A8200DA1F6A /* ScreenSelectorViewController.swift in Sources */, 232 | 2D384FB51D617A8200DA1F6A /* ScreenSelectorPreviewable.swift in Sources */, 233 | 2D384FB21D617A8200DA1F6A /* DummyScreenGenerator.swift in Sources */, 234 | 2D384FB31D617A8200DA1F6A /* ReversibleAnimatedTransitioning.swift in Sources */, 235 | 2D384FB71D617A8200DA1F6A /* ScreenSelectorView.swift in Sources */, 236 | 2D384FB91D617A8200DA1F6A /* SpecularReflectionLayer.swift in Sources */, 237 | ); 238 | runOnlyForDeploymentPostprocessing = 0; 239 | }; 240 | 2D384F941D6177E200DA1F6A /* Sources */ = { 241 | isa = PBXSourcesBuildPhase; 242 | buildActionMask = 2147483647; 243 | files = ( 244 | 2D384F9E1D6177E200DA1F6A /* ScreenSelectorTests.swift in Sources */, 245 | ); 246 | runOnlyForDeploymentPostprocessing = 0; 247 | }; 248 | /* End PBXSourcesBuildPhase section */ 249 | 250 | /* Begin PBXTargetDependency section */ 251 | 2D384F9B1D6177E200DA1F6A /* PBXTargetDependency */ = { 252 | isa = PBXTargetDependency; 253 | target = 2D384F8D1D6177E200DA1F6A /* ScreenSelector */; 254 | targetProxy = 2D384F9A1D6177E200DA1F6A /* PBXContainerItemProxy */; 255 | }; 256 | /* End PBXTargetDependency section */ 257 | 258 | /* Begin XCBuildConfiguration section */ 259 | 2D384FA01D6177E200DA1F6A /* Debug */ = { 260 | isa = XCBuildConfiguration; 261 | buildSettings = { 262 | ALWAYS_SEARCH_USER_PATHS = NO; 263 | CLANG_ANALYZER_NONNULL = YES; 264 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 265 | CLANG_CXX_LIBRARY = "libc++"; 266 | CLANG_ENABLE_MODULES = YES; 267 | CLANG_ENABLE_OBJC_ARC = YES; 268 | CLANG_WARN_BOOL_CONVERSION = YES; 269 | CLANG_WARN_CONSTANT_CONVERSION = YES; 270 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 271 | CLANG_WARN_EMPTY_BODY = YES; 272 | CLANG_WARN_ENUM_CONVERSION = YES; 273 | CLANG_WARN_INT_CONVERSION = YES; 274 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 275 | CLANG_WARN_UNREACHABLE_CODE = YES; 276 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 277 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 278 | COPY_PHASE_STRIP = NO; 279 | CURRENT_PROJECT_VERSION = 1; 280 | DEBUG_INFORMATION_FORMAT = dwarf; 281 | ENABLE_BITCODE = NO; 282 | ENABLE_STRICT_OBJC_MSGSEND = YES; 283 | ENABLE_TESTABILITY = YES; 284 | GCC_C_LANGUAGE_STANDARD = gnu99; 285 | GCC_DYNAMIC_NO_PIC = NO; 286 | GCC_NO_COMMON_BLOCKS = YES; 287 | GCC_OPTIMIZATION_LEVEL = 0; 288 | GCC_PREPROCESSOR_DEFINITIONS = ( 289 | "DEBUG=1", 290 | "$(inherited)", 291 | ); 292 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 293 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 294 | GCC_WARN_UNDECLARED_SELECTOR = YES; 295 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 296 | GCC_WARN_UNUSED_FUNCTION = YES; 297 | GCC_WARN_UNUSED_VARIABLE = YES; 298 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 299 | MTL_ENABLE_DEBUG_INFO = YES; 300 | ONLY_ACTIVE_ARCH = YES; 301 | SDKROOT = iphoneos; 302 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 303 | TARGETED_DEVICE_FAMILY = "1,2"; 304 | VERSIONING_SYSTEM = "apple-generic"; 305 | VERSION_INFO_PREFIX = ""; 306 | }; 307 | name = Debug; 308 | }; 309 | 2D384FA11D6177E200DA1F6A /* Release */ = { 310 | isa = XCBuildConfiguration; 311 | buildSettings = { 312 | ALWAYS_SEARCH_USER_PATHS = NO; 313 | CLANG_ANALYZER_NONNULL = YES; 314 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 315 | CLANG_CXX_LIBRARY = "libc++"; 316 | CLANG_ENABLE_MODULES = YES; 317 | CLANG_ENABLE_OBJC_ARC = YES; 318 | CLANG_WARN_BOOL_CONVERSION = YES; 319 | CLANG_WARN_CONSTANT_CONVERSION = YES; 320 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 321 | CLANG_WARN_EMPTY_BODY = YES; 322 | CLANG_WARN_ENUM_CONVERSION = YES; 323 | CLANG_WARN_INT_CONVERSION = YES; 324 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 325 | CLANG_WARN_UNREACHABLE_CODE = YES; 326 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 327 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 328 | COPY_PHASE_STRIP = NO; 329 | CURRENT_PROJECT_VERSION = 1; 330 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 331 | ENABLE_BITCODE = NO; 332 | ENABLE_NS_ASSERTIONS = NO; 333 | ENABLE_STRICT_OBJC_MSGSEND = YES; 334 | GCC_C_LANGUAGE_STANDARD = gnu99; 335 | GCC_NO_COMMON_BLOCKS = YES; 336 | GCC_OPTIMIZATION_LEVEL = 0; 337 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 338 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 339 | GCC_WARN_UNDECLARED_SELECTOR = YES; 340 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 341 | GCC_WARN_UNUSED_FUNCTION = YES; 342 | GCC_WARN_UNUSED_VARIABLE = YES; 343 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 344 | MTL_ENABLE_DEBUG_INFO = NO; 345 | SDKROOT = iphoneos; 346 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 347 | TARGETED_DEVICE_FAMILY = "1,2"; 348 | VALIDATE_PRODUCT = YES; 349 | VERSIONING_SYSTEM = "apple-generic"; 350 | VERSION_INFO_PREFIX = ""; 351 | }; 352 | name = Release; 353 | }; 354 | 2D384FA31D6177E200DA1F6A /* Debug */ = { 355 | isa = XCBuildConfiguration; 356 | buildSettings = { 357 | APPLICATION_EXTENSION_API_ONLY = NO; 358 | CLANG_ENABLE_MODULES = YES; 359 | DEFINES_MODULE = YES; 360 | DYLIB_COMPATIBILITY_VERSION = 1; 361 | DYLIB_CURRENT_VERSION = 1; 362 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 363 | INFOPLIST_FILE = ScreenSelector/Info.plist; 364 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 365 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 366 | PRODUCT_BUNDLE_IDENTIFIER = com.tueno.ScreenSelector; 367 | PRODUCT_NAME = "$(TARGET_NAME)"; 368 | SKIP_INSTALL = YES; 369 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 370 | }; 371 | name = Debug; 372 | }; 373 | 2D384FA41D6177E200DA1F6A /* Release */ = { 374 | isa = XCBuildConfiguration; 375 | buildSettings = { 376 | APPLICATION_EXTENSION_API_ONLY = NO; 377 | CLANG_ENABLE_MODULES = YES; 378 | DEFINES_MODULE = YES; 379 | DYLIB_COMPATIBILITY_VERSION = 1; 380 | DYLIB_CURRENT_VERSION = 1; 381 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 382 | INFOPLIST_FILE = ScreenSelector/Info.plist; 383 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 384 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 385 | PRODUCT_BUNDLE_IDENTIFIER = com.tueno.ScreenSelector; 386 | PRODUCT_NAME = "$(TARGET_NAME)"; 387 | SKIP_INSTALL = YES; 388 | }; 389 | name = Release; 390 | }; 391 | 2D384FA61D6177E200DA1F6A /* Debug */ = { 392 | isa = XCBuildConfiguration; 393 | buildSettings = { 394 | INFOPLIST_FILE = ScreenSelectorTests/Info.plist; 395 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 396 | PRODUCT_BUNDLE_IDENTIFIER = com.tueno.ScreenSelectorTests; 397 | PRODUCT_NAME = "$(TARGET_NAME)"; 398 | }; 399 | name = Debug; 400 | }; 401 | 2D384FA71D6177E200DA1F6A /* Release */ = { 402 | isa = XCBuildConfiguration; 403 | buildSettings = { 404 | INFOPLIST_FILE = ScreenSelectorTests/Info.plist; 405 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 406 | PRODUCT_BUNDLE_IDENTIFIER = com.tueno.ScreenSelectorTests; 407 | PRODUCT_NAME = "$(TARGET_NAME)"; 408 | }; 409 | name = Release; 410 | }; 411 | /* End XCBuildConfiguration section */ 412 | 413 | /* Begin XCConfigurationList section */ 414 | 2D384F881D6177E200DA1F6A /* Build configuration list for PBXProject "ScreenSelector" */ = { 415 | isa = XCConfigurationList; 416 | buildConfigurations = ( 417 | 2D384FA01D6177E200DA1F6A /* Debug */, 418 | 2D384FA11D6177E200DA1F6A /* Release */, 419 | ); 420 | defaultConfigurationIsVisible = 0; 421 | defaultConfigurationName = Release; 422 | }; 423 | 2D384FA21D6177E200DA1F6A /* Build configuration list for PBXNativeTarget "ScreenSelector" */ = { 424 | isa = XCConfigurationList; 425 | buildConfigurations = ( 426 | 2D384FA31D6177E200DA1F6A /* Debug */, 427 | 2D384FA41D6177E200DA1F6A /* Release */, 428 | ); 429 | defaultConfigurationIsVisible = 0; 430 | defaultConfigurationName = Release; 431 | }; 432 | 2D384FA51D6177E200DA1F6A /* Build configuration list for PBXNativeTarget "ScreenSelectorTests" */ = { 433 | isa = XCConfigurationList; 434 | buildConfigurations = ( 435 | 2D384FA61D6177E200DA1F6A /* Debug */, 436 | 2D384FA71D6177E200DA1F6A /* Release */, 437 | ); 438 | defaultConfigurationIsVisible = 0; 439 | defaultConfigurationName = Release; 440 | }; 441 | /* End XCConfigurationList section */ 442 | }; 443 | rootObject = 2D384F851D6177E200DA1F6A /* Project object */; 444 | } 445 | -------------------------------------------------------------------------------- /ScreenSelector.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ScreenSelector.xcodeproj/xcshareddata/xcschemes/ScreenSelector.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 53 | 54 | 64 | 65 | 71 | 72 | 73 | 74 | 75 | 76 | 82 | 83 | 89 | 90 | 91 | 92 | 94 | 95 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /ScreenSelector/DummyScreenGenerator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DummyScreenGenerator.swift 3 | // ScreenSelectorExample 4 | // 5 | // Created by Tomonori Ueno on 2016/07/26. 6 | // Copyright © 2016年 Tomonori Ueno. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | final class DummyScreenGenerator { 12 | 13 | class func generateViews() -> [ScreenSelectorPreviewable] { 14 | var views = [ScreenSelectorPreviewable]() 15 | for _ in 0.stride(to: 5, by: 1) { 16 | let view = DummyView() 17 | views.append(view) 18 | } 19 | return views 20 | } 21 | 22 | } 23 | 24 | final class DummyScreenDataSource: ScreenSelectorViewDataSource { 25 | 26 | let dummyScreeens = DummyScreenGenerator.generateViews() 27 | func previewViewForScreenAtIndex(index: Int) -> ScreenPreviewViewProtocol { 28 | let size = CGSize(width: UIScreen.mainScreen().bounds.width * 0.6, height: UIScreen.mainScreen().bounds.height * 0.6) 29 | let screenView = ScreenPreviewView(frame: CGRect(origin: CGPoint.zero, size: size)) 30 | let previewable = dummyScreeens[index] 31 | let previewImage: UIImage? = previewable.thumbnailImage 32 | let titleText: String? = "Title" 33 | screenView.titleLabel.text = titleText 34 | screenView.imageView.image = previewImage 35 | return screenView 36 | } 37 | 38 | func numberOfScreens() -> Int { 39 | return dummyScreeens.count 40 | } 41 | 42 | } 43 | 44 | final class DummyView: UIView, ScreenSelectorPreviewable { 45 | 46 | @IBOutlet weak var backgroundImageView: UIImageView? 47 | var thumbnailImage: UIImage? { 48 | get { 49 | UIGraphicsBeginImageContextWithOptions(UIScreen.mainScreen().bounds.size, false, 0) 50 | if let context = UIGraphicsGetCurrentContext() { 51 | let path = UIBezierPath(rect: UIScreen.mainScreen().bounds) 52 | path.lineWidth = 4 53 | path.stroke() 54 | CGContextAddPath(context, path.CGPath) 55 | let image = UIGraphicsGetImageFromCurrentImageContext() 56 | UIGraphicsEndImageContext() 57 | return image 58 | } else { 59 | UIGraphicsEndImageContext() 60 | return nil 61 | } 62 | } 63 | } 64 | var title: String? = "Title" 65 | 66 | } 67 | -------------------------------------------------------------------------------- /ScreenSelector/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /ScreenSelector/ReversibleAnimatedTransitioning.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ReversibleAnimatedTransitioning.swift 3 | // Oder 4 | // 5 | // Created by Tomonori Ueno on 2016/05/10. 6 | // Copyright © 2016年 Showcase Gig Inc. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | public protocol ReversibleAnimatedTransitioning: UIViewControllerAnimatedTransitioning { 12 | 13 | var isReverse: Bool { get set } 14 | init() 15 | 16 | } 17 | -------------------------------------------------------------------------------- /ScreenSelector/ScreenPreviewView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ScreenPreviewView.swift 3 | // ScreenSelectorExample 4 | // 5 | // Created by Tomonori Ueno on 2016/07/22. 6 | // Copyright © 2016年 Tomonori Ueno. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | public protocol ScreenPreviewViewProtocol: class { 12 | var titleLabel: UILabel! { get set } 13 | var imageView: UIImageView! { get set } 14 | } 15 | 16 | public class ScreenPreviewView: UIView, ScreenPreviewViewProtocol { 17 | 18 | public var titleLabel: UILabel! = UILabel() 19 | public var imageView: UIImageView! = UIImageView() 20 | 21 | public override init(frame: CGRect) { 22 | super.init(frame: frame) 23 | setupSubviews() 24 | } 25 | 26 | required public init?(coder aDecoder: NSCoder) { 27 | fatalError("init(coder:) has not been implemented") 28 | } 29 | 30 | private func setupSubviews() { 31 | let imageAreaRatio: CGFloat = 0.9 32 | var titleLabelFrame = titleLabel.frame 33 | titleLabelFrame.origin.y = 0 34 | titleLabelFrame.size.height = bounds.height * (1-imageAreaRatio) 35 | titleLabelFrame.size.width = bounds.width * imageAreaRatio 36 | titleLabel.frame = titleLabelFrame 37 | titleLabel.center = CGPoint(x: bounds.midX, y: titleLabel.center.y) 38 | titleLabel.textAlignment = .Center 39 | var imageViewFrame = imageView.frame 40 | imageViewFrame.size.height = bounds.height * imageAreaRatio 41 | imageViewFrame.size.width = bounds.width * imageAreaRatio 42 | imageViewFrame.origin.y = titleLabel.frame.maxY 43 | imageView.frame = imageViewFrame 44 | imageView.center = CGPoint(x: bounds.width * 0.5, y: imageView.center.y) 45 | imageView.contentMode = .ScaleAspectFill 46 | imageView.layer.masksToBounds = true 47 | addSubview(titleLabel) 48 | addSubview(imageView) 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /ScreenSelector/ScreenSelector.h: -------------------------------------------------------------------------------- 1 | // 2 | // ScreenSelector.h 3 | // ScreenSelector 4 | // 5 | // Created by Tomonori on 2016/08/15. 6 | // Copyright © 2016年 Tomonori Ueno. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for ScreenSelector. 12 | FOUNDATION_EXPORT double ScreenSelectorVersionNumber; 13 | 14 | //! Project version string for ScreenSelector. 15 | FOUNDATION_EXPORT const unsigned char ScreenSelectorVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | -------------------------------------------------------------------------------- /ScreenSelector/ScreenSelectorPreviewable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ScreenSelectorPreviewable.swift 3 | // ScreenSelectorExample 4 | // 5 | // Created by Tomonori Ueno on 2016/07/22. 6 | // Copyright © 2016年 Tomonori Ueno. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | public protocol ScreenSelectorPreviewable: class { 12 | var thumbnailImage: UIImage? { get } 13 | var backgroundImageView: UIImageView? { get } 14 | } 15 | -------------------------------------------------------------------------------- /ScreenSelector/ScreenSelectorTransitioningAnimator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ScreenSelectorTransitioningAnimator.swift 3 | // 4 | // Created by Tomonori Ueno on 2016/05/10. 5 | // Copyright © 2016年 Tomonori Ueno All rights reserved. 6 | // 7 | 8 | import UIKit 9 | 10 | final public class ScreenSelectorTransitioningAnimator: NSObject, ReversibleAnimatedTransitioning { 11 | 12 | weak var moveFromImageView: UIImageView? 13 | public var isReverse: Bool 14 | 15 | override required public init() { 16 | isReverse = false 17 | super.init() 18 | } 19 | 20 | required public init(isReverse: Bool = false, moveFromImageView: UIImageView?) { 21 | self.isReverse = isReverse 22 | self.moveFromImageView = moveFromImageView 23 | super.init() 24 | } 25 | 26 | public func transitionDuration(transitionContext: UIViewControllerContextTransitioning?) -> NSTimeInterval { 27 | return 0.6 28 | } 29 | 30 | public func animateTransition(transitionContext: UIViewControllerContextTransitioning) { 31 | 32 | guard let fromVC = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey), 33 | toVC = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey), 34 | containerView = transitionContext.containerView() else { 35 | transitionContext.completeTransition(!transitionContext.transitionWasCancelled()) 36 | return 37 | } 38 | 39 | let fromView = fromVC.view 40 | let toView = toVC.view 41 | let generateCopyOfImageView: UIImageView? -> UIImageView? = { view in 42 | guard let view = view else { 43 | return nil 44 | } 45 | toView.layoutIfNeeded() 46 | let copy = UIImageView(image: view.image) 47 | copy.contentMode = view.contentMode 48 | copy.frame = containerView.convertRect(view.frame, fromView: view.superview) 49 | copy.layer.masksToBounds = true 50 | return copy 51 | } 52 | if toView.frame == CGRect.zero { 53 | toView.frame = fromView.frame 54 | } 55 | let copiedImageView: UIImageView? 56 | let moveFromView: UIView? 57 | let moveToView: UIView? 58 | if let f = fromVC as? ScreenSelectorPreviewable where isReverse { 59 | containerView.insertSubview(toView, belowSubview: fromView) 60 | moveFromView = f.backgroundImageView 61 | copiedImageView = generateCopyOfImageView(moveFromView as? UIImageView) 62 | moveToView = moveFromImageView 63 | } else if let t = toVC as? ScreenSelectorPreviewable where !isReverse { 64 | containerView.addSubview(toView) 65 | moveFromView = moveFromImageView 66 | copiedImageView = generateCopyOfImageView(moveFromView as? UIImageView) 67 | moveToView = t.backgroundImageView 68 | } else { 69 | fatalError() 70 | } 71 | 72 | if let moveToView = moveToView, copiedImageView = copiedImageView, moveFromImageView = moveFromImageView { 73 | containerView.addSubview(copiedImageView) 74 | let duration = transitionDuration(transitionContext) 75 | let toFrame: CGRect 76 | if isReverse { 77 | fromView.hidden = true 78 | toFrame = containerView.convertRect(moveFromImageView.frame, fromView: moveFromImageView.superview) 79 | } else { 80 | toView.alpha = 0 81 | toFrame = containerView.convertRect(moveToView.frame, fromView: moveToView.superview) 82 | } 83 | 84 | UIView.animateWithDuration(duration * 0.6, delay: 0, options: [.BeginFromCurrentState, .CurveEaseOut], animations: { 85 | copiedImageView.frame = toFrame 86 | }) { (finished) in 87 | if self.isReverse { 88 | fromView.hidden = false 89 | // 完了通知 90 | copiedImageView.removeFromSuperview() 91 | let isCancelled = transitionContext.transitionWasCancelled() 92 | transitionContext.completeTransition(!isCancelled) 93 | } else { 94 | containerView.insertSubview(toView, aboveSubview: copiedImageView) 95 | UIView.animateWithDuration(duration * 0.4, delay: 0, options: [.BeginFromCurrentState], animations: { 96 | toView.alpha = 1.0 97 | }) { (finished) in 98 | fromView.hidden = false 99 | // 完了通知 100 | copiedImageView.removeFromSuperview() 101 | let isCancelled = transitionContext.transitionWasCancelled() 102 | transitionContext.completeTransition(!isCancelled) 103 | } 104 | } 105 | } 106 | } else { 107 | let isCancelled = transitionContext.transitionWasCancelled() 108 | transitionContext.completeTransition(!isCancelled) 109 | } 110 | 111 | } 112 | 113 | } 114 | -------------------------------------------------------------------------------- /ScreenSelector/ScreenSelectorView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ScreenSelectorView.swift 3 | // ScreenSelector 4 | // 5 | // Created by Tomonori Ueno on 2016/07/20. 6 | // Copyright © 2016年 Tomonori Ueno. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | public protocol ScreenSelectorViewDelegate: class { 12 | func screenSelectorViewDidTapScreenAtIndex(index: Int, previewView: ScreenPreviewViewProtocol) 13 | } 14 | public protocol ScreenSelectorViewDataSource: class { 15 | func numberOfScreens() -> Int 16 | func previewViewForScreenAtIndex(index: Int) -> ScreenPreviewViewProtocol 17 | } 18 | 19 | @IBDesignable 20 | public class ScreenSelectorView: UIView { 21 | 22 | @IBInspectable public var horizontalMargin: CGFloat = 20 23 | @IBInspectable public var previewVerticalOffset: CGFloat = 0 24 | @IBInspectable public var previewReflectionEnabled: Bool = false 25 | 26 | public let scrollView: UIScrollView = UIScrollView() 27 | public weak var delegate: ScreenSelectorViewDelegate? 28 | public weak var dataSource: ScreenSelectorViewDataSource? { 29 | didSet { 30 | reloadData() 31 | } 32 | } 33 | public weak var scrollViewDelegate: UIScrollViewDelegate? { 34 | didSet { 35 | scrollView.delegate = scrollViewDelegate 36 | } 37 | } 38 | private var previewViews: [ScreenPreviewViewProtocol] = [] 39 | 40 | required public init?(coder aDecoder: NSCoder) { 41 | super.init(coder: aDecoder) 42 | } 43 | 44 | public override init(frame: CGRect) { 45 | super.init(frame: frame) 46 | commonInit() 47 | } 48 | 49 | public override func prepareForInterfaceBuilder() { 50 | super.prepareForInterfaceBuilder() 51 | commonInit() 52 | scrollView.frame = bounds 53 | reloadData(dataSource: DummyScreenDataSource()) 54 | } 55 | 56 | public override func awakeFromNib() { 57 | super.awakeFromNib() 58 | commonInit() 59 | } 60 | 61 | public override func didMoveToSuperview() { 62 | super.didMoveToSuperview() 63 | } 64 | 65 | public override func layoutSubviews() { 66 | super.layoutSubviews() 67 | scrollView.frame = bounds 68 | setScrollViewHorizontalInset() 69 | } 70 | 71 | private func commonInit() { 72 | setupScrollView() 73 | let tapRecog = UITapGestureRecognizer(target: self, action: #selector(tapped(_:))) 74 | addGestureRecognizer(tapRecog) 75 | scrollView.delegate = scrollViewDelegate 76 | scrollView.frame = bounds 77 | } 78 | 79 | private func setupScrollView() { 80 | scrollView.alwaysBounceHorizontal = true 81 | scrollView.showsVerticalScrollIndicator = false 82 | scrollView.showsHorizontalScrollIndicator = false 83 | addSubview(scrollView) 84 | } 85 | 86 | private func reloadData(dataSource dataSource: ScreenSelectorViewDataSource?) { 87 | guard let dataSource = dataSource where dataSource.numberOfScreens() > 0 else { 88 | return 89 | } 90 | removeAllPreviews() 91 | for i in 0.stride(to: dataSource.numberOfScreens(), by: 1) { 92 | let contentView = dataSource.previewViewForScreenAtIndex(i) 93 | previewViews.append(contentView) 94 | guard let c = contentView as? UIView else { 95 | fatalError() 96 | } 97 | var contentViewFrame = c.frame 98 | contentViewFrame.origin.x = CGFloat(i) * (horizontalMargin + contentViewFrame.size.width) 99 | c.frame = contentViewFrame 100 | c.center = CGPoint(x: c.center.x, y: bounds.height * 0.5 + previewVerticalOffset) 101 | scrollView.addSubview(c) 102 | scrollView.contentSize = CGSize(width: contentViewFrame.maxX, height: 0) 103 | if previewReflectionEnabled { 104 | addSpecularReflectionLayerToView(c) 105 | } 106 | } 107 | setScrollViewHorizontalInset() 108 | } 109 | 110 | private func setScrollViewHorizontalInset() { 111 | // Calculate horizontal inset to place first screen to center. 112 | let contentViewBounds = previewViews 113 | .map() { view in 114 | return view as? UIView 115 | } 116 | .flatMap() { a in a } 117 | .first?.bounds ?? CGRect.zero 118 | let horizontalInset = scrollView.bounds.width * 0.5 - contentViewBounds.width * 0.5 119 | scrollView.contentInset.left = horizontalInset 120 | scrollView.contentInset.right = horizontalInset 121 | scrollView.setContentOffset(CGPoint(x: -scrollView.contentInset.left, y: 0), animated: false) 122 | } 123 | 124 | private func addSpecularReflectionLayerToView(view: UIView) { 125 | let layer = SpecularReflectionLayer(sourceView: view) 126 | view.layer.addSublayer(layer) 127 | } 128 | 129 | private func removeAllPreviews() { 130 | previewViews.forEach { (previewView) in 131 | if let view = previewView as? UIView { 132 | view.removeFromSuperview() 133 | } 134 | } 135 | previewViews.removeAll() 136 | } 137 | 138 | public func reloadData() { 139 | reloadData(dataSource: dataSource) 140 | } 141 | 142 | public func scrollToIndex(index: Int, animated: Bool) { 143 | guard previewViews.count > index else { 144 | return 145 | } 146 | if let preview = previewViews[index] as? UIView { 147 | let xOffset = -(scrollView.bounds.width * 0.5 - (preview.frame.maxX - preview.bounds.midX)) 148 | scrollView.setContentOffset(CGPoint(x: xOffset, y: 0), animated: animated) 149 | } 150 | } 151 | 152 | func tapped(tapRecog: UITapGestureRecognizer) { 153 | let location = tapRecog.locationInView(self) 154 | let tappedView = previewViews 155 | .filter { (view) -> Bool in 156 | guard let view = view as? UIView else { 157 | return false 158 | } 159 | let rectInView = scrollView.convertRect(view.frame, toView: self) 160 | return rectInView.contains(location) 161 | } 162 | .first 163 | let index = previewViews 164 | .indexOf { (viewProtocol) -> Bool in 165 | return viewProtocol === tappedView 166 | } 167 | if let t = tappedView, index = index { 168 | self.delegate?.screenSelectorViewDidTapScreenAtIndex(index, previewView: t) 169 | } 170 | } 171 | 172 | } 173 | -------------------------------------------------------------------------------- /ScreenSelector/ScreenSelectorViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ScreenSelectorViewController.swift 3 | // ScreenSelectorExample 4 | // 5 | // Created by Tomonori Ueno on 2016/07/22. 6 | // Copyright © 2016年 Tomonori Ueno. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | public class ScreenSelectorViewController: UIViewController, ScreenSelectorViewDelegate, ScreenSelectorViewDataSource, UIViewControllerTransitioningDelegate { 12 | 13 | public let screenSelectorView = ScreenSelectorView() 14 | public var previewableScreens: [ScreenSelectorPreviewable] = [] 15 | 16 | weak var transitionFromPreviewImageView: UIImageView? 17 | 18 | override public func viewDidLoad() { 19 | super.viewDidLoad() 20 | screenSelectorView.frame = view.bounds 21 | screenSelectorView.delegate = self 22 | screenSelectorView.dataSource = self 23 | view.addSubview(screenSelectorView) 24 | } 25 | 26 | override public func viewDidLayoutSubviews() { 27 | super.viewDidLayoutSubviews() 28 | } 29 | 30 | // MARK: - ScreenSelectorViewDelegate 31 | 32 | public func screenSelectorViewDidTapScreenAtIndex(index: Int, previewView: ScreenPreviewViewProtocol) { 33 | transitionFromPreviewImageView = previewView.imageView 34 | if let viewController = previewableScreens[index] as? UIViewController { 35 | viewController.transitioningDelegate = self 36 | presentViewController(viewController, animated: true) { 37 | // Move selected preview to center of screen. 38 | self.screenSelectorView.scrollToIndex(index, animated: false) 39 | } 40 | } 41 | } 42 | 43 | // MARK: - ScreenSelectorViewDataSource 44 | 45 | public func numberOfScreens() -> Int { 46 | return previewableScreens.count 47 | } 48 | 49 | public func previewViewForScreenAtIndex(index: Int) -> ScreenPreviewViewProtocol { 50 | let size = CGSize(width: UIScreen.mainScreen().bounds.width * 0.6, height: UIScreen.mainScreen().bounds.height * 0.6) 51 | let screenView = ScreenPreviewView(frame: CGRect(origin: CGPoint.zero, size: size)) 52 | let previewable = previewableScreens[index] 53 | let previewImage: UIImage? = previewable.thumbnailImage 54 | guard let vc = previewable as? UIViewController else { 55 | fatalError() 56 | } 57 | let titleText: String? = vc.title 58 | screenView.titleLabel.text = titleText 59 | screenView.imageView.image = previewImage 60 | return screenView 61 | } 62 | 63 | // MARK: - 64 | 65 | public func animationControllerForPresentedController(presented: UIViewController, 66 | presentingController presenting: UIViewController, 67 | sourceController source: UIViewController) -> UIViewControllerAnimatedTransitioning? { 68 | let animator = ScreenSelectorTransitioningAnimator(isReverse: false, moveFromImageView: transitionFromPreviewImageView) 69 | return animator 70 | } 71 | 72 | public func animationControllerForDismissedController(dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? { 73 | let animator = ScreenSelectorTransitioningAnimator(isReverse: true, moveFromImageView: transitionFromPreviewImageView) 74 | return animator 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /ScreenSelector/SpecularReflectionLayer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SpecularReflectionLayer.swift 3 | // ScreenSelectorExample 4 | // 5 | // Created by Tomonori Ueno on 2016/07/26. 6 | // Copyright © 2016年 Tomonori Ueno. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | final public class SpecularReflectionLayer: CALayer { 12 | 13 | init(sourceView: UIView, xAngle: Double = M_PI) { 14 | super.init() 15 | bounds = sourceView.bounds 16 | commonInit(sourceView: sourceView, xAngle: xAngle) 17 | } 18 | 19 | required public init?(coder aDecoder: NSCoder) { 20 | fatalError("init(coder:) has not been implemented") 21 | } 22 | 23 | private func commonInit(sourceView view: UIView, xAngle: Double) { 24 | let image = generateImageOfView(sourceView: view) 25 | contents = image.CGImage 26 | opacity = 1.0 27 | anchorPoint = CGPoint(x: 0.5, y: 1.0) 28 | position = CGPoint(x: bounds.width * 0.5, y: bounds.height + 20) 29 | actions = ["transform" : NSNull()] 30 | var perspective = CATransform3DIdentity 31 | perspective.m34 = 1 / bounds.height * 0.25 32 | transform = CATransform3DConcat(CATransform3DMakeRotation(CGFloat(xAngle), 1, 0, 0), perspective) 33 | let gradientLayer = generateGradientLayer() 34 | mask = gradientLayer 35 | gradientLayer.anchorPoint = anchorPoint 36 | gradientLayer.position = position 37 | } 38 | 39 | private func generateGradientLayer() -> CAGradientLayer { 40 | let gradientLayer = CAGradientLayer() 41 | gradientLayer.colors = [UIColor(red: 0, green: 0, blue: 0, alpha: 0.3).CGColor, UIColor.clearColor().CGColor] 42 | gradientLayer.startPoint = CGPoint(x: 0.5, y: 1.0) 43 | gradientLayer.endPoint = CGPoint(x: 0.5, y: 0.75) 44 | gradientLayer.bounds = bounds 45 | return gradientLayer 46 | } 47 | 48 | private func generateImageOfView(sourceView view: UIView) -> UIImage { 49 | UIGraphicsBeginImageContextWithOptions(view.bounds.size, false, 0) 50 | #if TARGET_INTERFACE_BUILDER 51 | if let context = UIGraphicsGetCurrentContext() { 52 | view.layer.renderInContext(context) 53 | } 54 | #else 55 | view.drawViewHierarchyInRect(view.bounds, afterScreenUpdates: true) 56 | #endif 57 | let image = UIGraphicsGetImageFromCurrentImageContext() 58 | UIGraphicsEndImageContext() 59 | return image 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /ScreenSelectorTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /ScreenSelectorTests/ScreenSelectorTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ScreenSelectorTests.swift 3 | // ScreenSelectorTests 4 | // 5 | // Created by Tomonori on 2016/08/15. 6 | // Copyright © 2016年 Tomonori Ueno. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import ScreenSelector 11 | 12 | class ScreenSelectorTests: XCTestCase { 13 | 14 | override func setUp() { 15 | super.setUp() 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | } 18 | 19 | override func tearDown() { 20 | // Put teardown code here. This method is called after the invocation of each test method in the class. 21 | super.tearDown() 22 | } 23 | 24 | func testExample() { 25 | // This is an example of a functional test case. 26 | // Use XCTAssert and related functions to verify your tests produce the correct results. 27 | } 28 | 29 | func testPerformanceExample() { 30 | // This is an example of a performance test case. 31 | self.measureBlock { 32 | // Put the code you want to measure the time of here. 33 | } 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /preview.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tueno/ScreenSelector/11eeacd66d52c4673e695cafacfe060f25cefc6e/preview.gif --------------------------------------------------------------------------------