├── .gitignore ├── LICENSE ├── LaunchServicesApis ├── LMApp.h ├── LMApp.m ├── LMAppController.h └── LMAppController.m ├── README.TXT ├── WatchSpringboard.xcodeproj ├── project.pbxproj └── project.xcworkspace │ └── contents.xcworkspacedata ├── WatchSpringboard ├── AppDelegate.h ├── AppDelegate.m ├── Base.lproj │ ├── LaunchScreen.xib │ └── Main.storyboard ├── Icons │ ├── Icon-0-for-vibrancy@2x.png │ ├── Icon-0@2x.png │ ├── Icon-10@2x.png │ ├── Icon-11@2x.png │ ├── Icon-12@2x.png │ ├── Icon-13@2x.png │ ├── Icon-14@2x.png │ ├── Icon-15@2x.png │ ├── Icon-16@2x.png │ ├── Icon-17@2x.png │ ├── Icon-18@2x.png │ ├── Icon-19@2x.png │ ├── Icon-1@2x.png │ ├── Icon-20@2x.png │ ├── Icon-21@2x.png │ ├── Icon-22@2x.png │ ├── Icon-23@2x.png │ ├── Icon-24@2x.png │ ├── Icon-25@2x.png │ ├── Icon-26@2x.png │ ├── Icon-27@2x.png │ ├── Icon-28@2x.png │ ├── Icon-29@2x.png │ ├── Icon-2@2x.png │ ├── Icon-30@2x.png │ ├── Icon-31@2x.png │ ├── Icon-32@2x.png │ ├── Icon-33@2x.png │ ├── Icon-34@2x.png │ ├── Icon-3@2x.png │ ├── Icon-4@2x.png │ ├── Icon-5@2x.png │ ├── Icon-6@2x.png │ ├── Icon-7@2x.png │ ├── Icon-8@2x.png │ └── Icon-9@2x.png ├── Images.xcassets │ ├── App.imageset │ │ ├── App@2x.png │ │ └── Contents.json │ ├── AppIcon.appiconset │ │ └── Contents.json │ ├── Icon.imageset │ │ ├── Contents.json │ │ ├── Icon@2x.png │ │ └── Icon@3x.png │ └── Wallpaper.imageset │ │ ├── Contents.json │ │ └── Wallpaper@2x.png ├── Info.plist ├── LMSpringboardItemView.h ├── LMSpringboardItemView.m ├── LMSpringboardView.h ├── LMSpringboardView.m ├── LMViewControllerView.h ├── LMViewControllerView.m ├── ViewController.h ├── ViewController.m └── main.m └── WatchSpringboardTests ├── Info.plist └── WatchSpringboardTests.m /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.swp 3 | *~.nib 4 | 5 | build 6 | 7 | *.pbxuser 8 | *.perspective 9 | *.perspectivev3 10 | *.xcworkspace 11 | xcuserdata 12 | 13 | *.mode1v3 14 | *.mode2v3 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014, Lucas Mendes Menge 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | * Redistributions of source code must retain the above copyright 7 | notice, this list of conditions and the following disclaimer. 8 | * Redistributions in binary form must reproduce the above copyright 9 | notice, this list of conditions and the following disclaimer in the 10 | documentation and/or other materials provided with the distribution. 11 | * Neither the name of the WatchSpringboard-Prototype nor the 12 | names of its contributors may be used to endorse or promote products 13 | derived from this software without specific prior written permission. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL LUCAS MENDES MENGE BE LIABLE FOR ANY 19 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /LaunchServicesApis/LMApp.h: -------------------------------------------------------------------------------- 1 | // 2 | // LMApp.h 3 | // WatchSpringboard 4 | // 5 | // Created by Andreas Verhoeven on 28-10-14. 6 | // Copyright (c) 2014 Lucas Menge. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface LMApp : NSObject 12 | 13 | @property (nonatomic, readonly) NSString* bundleIdentifier; 14 | @property (nonatomic, readonly) NSString* name; 15 | @property (nonatomic, readonly) UIImage* icon; 16 | 17 | @property (nonatomic, readonly) BOOL isHiddenApp; 18 | 19 | + (instancetype)appWithPrivateProxy:(id)privateProxy; 20 | + (instancetype)appWithBundleIdentifier:(NSString*)bundleIdentifier; 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /LaunchServicesApis/LMApp.m: -------------------------------------------------------------------------------- 1 | // 2 | // LMApp.m 3 | // WatchSpringboard 4 | // 5 | // Created by Andreas Verhoeven on 28-10-14. 6 | // Copyright (c) 2014 Lucas Menge. All rights reserved. 7 | // 8 | 9 | #import "LMApp.h" 10 | 11 | @interface UIImage () 12 | + (id)_iconForResourceProxy:(id)arg1 variant:(int)arg2 variantsScale:(float)arg3; 13 | + (id)_applicationIconImageForBundleIdentifier:(id)arg1 format:(int)arg2 scale:(double)arg3; 14 | @end 15 | 16 | #pragma mark - 17 | 18 | @interface PrivateApi_LSApplicationProxy 19 | 20 | + (instancetype)applicationProxyForIdentifier:(NSString*)identifier; 21 | @property (nonatomic, readonly) NSString* localizedShortName; 22 | @property (nonatomic, readonly) NSString* localizedName; 23 | @property (nonatomic, readonly) NSString* bundleIdentifier; 24 | @property (nonatomic, readonly) NSArray* appTags; 25 | 26 | @end 27 | 28 | #pragma mark - 29 | 30 | @implementation LMApp 31 | { 32 | PrivateApi_LSApplicationProxy* _applicationProxy; 33 | UIImage* _icon; 34 | } 35 | 36 | - (NSString*)name 37 | { 38 | return _applicationProxy.localizedName ?: _applicationProxy.localizedShortName; 39 | } 40 | 41 | - (NSString*)bundleIdentifier 42 | { 43 | return [_applicationProxy bundleIdentifier]; 44 | } 45 | 46 | - (UIImage*)icon 47 | { 48 | if(nil == _icon) 49 | { 50 | _icon = [UIImage _applicationIconImageForBundleIdentifier:self.bundleIdentifier format:10 scale:2.0]; 51 | } 52 | 53 | return _icon; 54 | } 55 | 56 | - (BOOL)isHiddenApp 57 | { 58 | return [[_applicationProxy appTags] indexOfObject:@"hidden"] != NSNotFound; 59 | } 60 | 61 | - (id)initWithPrivateProxy:(id)privateProxy 62 | { 63 | self = [super init]; 64 | if(self != nil) 65 | { 66 | _applicationProxy = (PrivateApi_LSApplicationProxy*)privateProxy; 67 | } 68 | 69 | return self; 70 | } 71 | 72 | - (instancetype)initWithBundleIdentifier:(NSString*)bundleIdentifier 73 | { 74 | self = [super init]; 75 | if(self != nil) 76 | { 77 | _applicationProxy = [NSClassFromString(@"LSApplicationProxy") applicationProxyForIdentifier:bundleIdentifier]; 78 | } 79 | 80 | return self; 81 | } 82 | 83 | + (instancetype)appWithPrivateProxy:(id)privateProxy 84 | { 85 | return [[self alloc] initWithPrivateProxy:privateProxy]; 86 | } 87 | 88 | + (instancetype)appWithBundleIdentifier:(NSString*)bundleIdentifier 89 | { 90 | return [[self alloc] initWithBundleIdentifier:bundleIdentifier]; 91 | } 92 | 93 | @end 94 | -------------------------------------------------------------------------------- /LaunchServicesApis/LMAppController.h: -------------------------------------------------------------------------------- 1 | // 2 | // LMAppController.h 3 | // WatchSpringboard 4 | // 5 | // Created by Andreas Verhoeven on 28-10-14. 6 | // Copyright (c) 2014 Lucas Menge. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "LMApp.h" 11 | 12 | @interface LMAppController : NSObject 13 | 14 | @property (nonatomic, readonly) NSArray* installedApplications; 15 | 16 | - (BOOL)openAppWithBundleIdentifier:(NSString*)bundleIdentifier; 17 | 18 | + (instancetype)sharedInstance; 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /LaunchServicesApis/LMAppController.m: -------------------------------------------------------------------------------- 1 | // 2 | // LMAppController.m 3 | // WatchSpringboard 4 | // 5 | // Created by Andreas Verhoeven on 28-10-14. 6 | // Copyright (c) 2014 Lucas Menge. All rights reserved. 7 | // 8 | 9 | #import "LMAppController.h" 10 | 11 | @interface PrivateApi_LSApplicationWorkspace 12 | - (NSArray*)allInstalledApplications; 13 | - (bool)openApplicationWithBundleID:(id)arg1; 14 | @end 15 | 16 | #pragma mark - 17 | 18 | @implementation LMAppController 19 | { 20 | PrivateApi_LSApplicationWorkspace* _workspace; 21 | NSArray* _installedApplications; 22 | } 23 | 24 | - (instancetype)init 25 | { 26 | self = [super init]; 27 | if(self != nil) 28 | { 29 | _workspace = [NSClassFromString(@"LSApplicationWorkspace") new]; 30 | } 31 | 32 | return self; 33 | } 34 | 35 | - (NSArray*)readApplications 36 | { 37 | NSArray* allInstalledApplications = [_workspace allInstalledApplications]; 38 | NSMutableArray* applications = [NSMutableArray arrayWithCapacity:allInstalledApplications.count]; 39 | for(id proxy in allInstalledApplications) 40 | { 41 | LMApp* app = [LMApp appWithPrivateProxy:proxy]; 42 | if(!app.isHiddenApp) 43 | { 44 | [applications addObject:app]; 45 | } 46 | } 47 | 48 | return applications; 49 | } 50 | 51 | - (NSArray*)installedApplications 52 | { 53 | if(nil == _installedApplications) 54 | { 55 | _installedApplications = [self readApplications]; 56 | } 57 | 58 | return _installedApplications; 59 | } 60 | 61 | - (BOOL)openAppWithBundleIdentifier:(NSString *)bundleIdentifier 62 | { 63 | return (BOOL)[_workspace openApplicationWithBundleID:bundleIdentifier]; 64 | } 65 | 66 | + (instancetype)sharedInstance 67 | { 68 | static dispatch_once_t once; 69 | static id sharedInstance; 70 | dispatch_once(&once, ^{ 71 | sharedInstance = [[self alloc] init]; 72 | }); 73 | return sharedInstance; 74 | } 75 | 76 | @end 77 | -------------------------------------------------------------------------------- /README.TXT: -------------------------------------------------------------------------------- 1 | On October 22nd, @MichaelSteeber wrote an article for @9to5mac ( http://9to5mac.com/2014/10/22/apple-watch-home-screen-design-iphone/ ) in which he theorized an Apple Watch-like home screen for the iPhone. I thought the designs shown looked pretty, and the GIF he posted showing interaction with the real Apple Watch home screen made me notice a lot of tiny little details on it, so I decided to give a try at programming it over the past couple of days in my free time. This is the result. I hope you guys like it! 2 | 3 | For a video of this in action, go to: https://www.youtube.com/watch?v=UggYGThmFEo&list=UU7C761Fn9aih_W6kMvkm24w 4 | 5 | Please consider this prototype code. There are unpolished edge cases! -------------------------------------------------------------------------------- /WatchSpringboard.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | A069C6CB19FC5AAE00E7E6D5 /* Icon-34@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = A069C6CA19FC5AAD00E7E6D5 /* Icon-34@2x.png */; }; 11 | A069C6CD19FEBB7400E7E6D5 /* Icon-0@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = A069C6CC19FEBB7400E7E6D5 /* Icon-0@2x.png */; }; 12 | A069C6CF19FEDA9B00E7E6D5 /* LICENSE in Resources */ = {isa = PBXBuildFile; fileRef = A069C6CE19FEDA9B00E7E6D5 /* LICENSE */; }; 13 | A069C6D119FEDAA200E7E6D5 /* README.TXT in Resources */ = {isa = PBXBuildFile; fileRef = A069C6D019FEDAA200E7E6D5 /* README.TXT */; }; 14 | A073BBB819F9C7AB0068D6A6 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = A073BBB719F9C7AB0068D6A6 /* main.m */; }; 15 | A073BBBB19F9C7AB0068D6A6 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = A073BBBA19F9C7AB0068D6A6 /* AppDelegate.m */; }; 16 | A073BBBE19F9C7AB0068D6A6 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = A073BBBD19F9C7AB0068D6A6 /* ViewController.m */; }; 17 | A073BBC119F9C7AB0068D6A6 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = A073BBBF19F9C7AB0068D6A6 /* Main.storyboard */; }; 18 | A073BBC319F9C7AB0068D6A6 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A073BBC219F9C7AB0068D6A6 /* Images.xcassets */; }; 19 | A073BBC619F9C7AB0068D6A6 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = A073BBC419F9C7AB0068D6A6 /* LaunchScreen.xib */; }; 20 | A073BBD219F9C7AC0068D6A6 /* WatchSpringboardTests.m in Sources */ = {isa = PBXBuildFile; fileRef = A073BBD119F9C7AC0068D6A6 /* WatchSpringboardTests.m */; }; 21 | A073BBDE19F9C8220068D6A6 /* LMSpringboardView.m in Sources */ = {isa = PBXBuildFile; fileRef = A073BBDD19F9C8220068D6A6 /* LMSpringboardView.m */; }; 22 | A089811319FAF4EC00034E0B /* LMViewControllerView.m in Sources */ = {isa = PBXBuildFile; fileRef = A089811219FAF4EC00034E0B /* LMViewControllerView.m */; }; 23 | A089813719FAFC7900034E0B /* Icon-0-for-vibrancy@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = A089811519FAFC7900034E0B /* Icon-0-for-vibrancy@2x.png */; }; 24 | A089813819FAFC7900034E0B /* Icon-10@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = A089811619FAFC7900034E0B /* Icon-10@2x.png */; }; 25 | A089813919FAFC7900034E0B /* Icon-11@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = A089811719FAFC7900034E0B /* Icon-11@2x.png */; }; 26 | A089813A19FAFC7900034E0B /* Icon-12@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = A089811819FAFC7900034E0B /* Icon-12@2x.png */; }; 27 | A089813B19FAFC7900034E0B /* Icon-13@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = A089811919FAFC7900034E0B /* Icon-13@2x.png */; }; 28 | A089813C19FAFC7900034E0B /* Icon-14@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = A089811A19FAFC7900034E0B /* Icon-14@2x.png */; }; 29 | A089813D19FAFC7900034E0B /* Icon-15@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = A089811B19FAFC7900034E0B /* Icon-15@2x.png */; }; 30 | A089813E19FAFC7900034E0B /* Icon-16@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = A089811C19FAFC7900034E0B /* Icon-16@2x.png */; }; 31 | A089813F19FAFC7900034E0B /* Icon-17@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = A089811D19FAFC7900034E0B /* Icon-17@2x.png */; }; 32 | A089814019FAFC7900034E0B /* Icon-18@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = A089811E19FAFC7900034E0B /* Icon-18@2x.png */; }; 33 | A089814119FAFC7900034E0B /* Icon-19@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = A089811F19FAFC7900034E0B /* Icon-19@2x.png */; }; 34 | A089814219FAFC7900034E0B /* Icon-1@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = A089812019FAFC7900034E0B /* Icon-1@2x.png */; }; 35 | A089814319FAFC7900034E0B /* Icon-20@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = A089812119FAFC7900034E0B /* Icon-20@2x.png */; }; 36 | A089814419FAFC7900034E0B /* Icon-21@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = A089812219FAFC7900034E0B /* Icon-21@2x.png */; }; 37 | A089814519FAFC7900034E0B /* Icon-22@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = A089812319FAFC7900034E0B /* Icon-22@2x.png */; }; 38 | A089814619FAFC7900034E0B /* Icon-23@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = A089812419FAFC7900034E0B /* Icon-23@2x.png */; }; 39 | A089814719FAFC7900034E0B /* Icon-24@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = A089812519FAFC7900034E0B /* Icon-24@2x.png */; }; 40 | A089814819FAFC7900034E0B /* Icon-25@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = A089812619FAFC7900034E0B /* Icon-25@2x.png */; }; 41 | A089814919FAFC7900034E0B /* Icon-26@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = A089812719FAFC7900034E0B /* Icon-26@2x.png */; }; 42 | A089814A19FAFC7900034E0B /* Icon-27@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = A089812819FAFC7900034E0B /* Icon-27@2x.png */; }; 43 | A089814B19FAFC7900034E0B /* Icon-28@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = A089812919FAFC7900034E0B /* Icon-28@2x.png */; }; 44 | A089814C19FAFC7900034E0B /* Icon-29@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = A089812A19FAFC7900034E0B /* Icon-29@2x.png */; }; 45 | A089814D19FAFC7900034E0B /* Icon-2@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = A089812B19FAFC7900034E0B /* Icon-2@2x.png */; }; 46 | A089814E19FAFC7900034E0B /* Icon-30@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = A089812C19FAFC7900034E0B /* Icon-30@2x.png */; }; 47 | A089814F19FAFC7900034E0B /* Icon-31@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = A089812D19FAFC7900034E0B /* Icon-31@2x.png */; }; 48 | A089815019FAFC7900034E0B /* Icon-32@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = A089812E19FAFC7900034E0B /* Icon-32@2x.png */; }; 49 | A089815119FAFC7900034E0B /* Icon-33@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = A089812F19FAFC7900034E0B /* Icon-33@2x.png */; }; 50 | A089815219FAFC7900034E0B /* Icon-3@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = A089813019FAFC7900034E0B /* Icon-3@2x.png */; }; 51 | A089815319FAFC7900034E0B /* Icon-4@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = A089813119FAFC7900034E0B /* Icon-4@2x.png */; }; 52 | A089815419FAFC7900034E0B /* Icon-5@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = A089813219FAFC7900034E0B /* Icon-5@2x.png */; }; 53 | A089815519FAFC7900034E0B /* Icon-6@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = A089813319FAFC7900034E0B /* Icon-6@2x.png */; }; 54 | A089815619FAFC7900034E0B /* Icon-7@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = A089813419FAFC7900034E0B /* Icon-7@2x.png */; }; 55 | A089815719FAFC7900034E0B /* Icon-8@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = A089813519FAFC7900034E0B /* Icon-8@2x.png */; }; 56 | A089815819FAFC7900034E0B /* Icon-9@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = A089813619FAFC7900034E0B /* Icon-9@2x.png */; }; 57 | A089815B19FB039E00034E0B /* LMSpringboardItemView.m in Sources */ = {isa = PBXBuildFile; fileRef = A089815A19FB039E00034E0B /* LMSpringboardItemView.m */; }; 58 | B2E377B819FF127A00A10D5E /* LMApp.m in Sources */ = {isa = PBXBuildFile; fileRef = B2E377B519FF127A00A10D5E /* LMApp.m */; }; 59 | B2E377B919FF127A00A10D5E /* LMAppController.m in Sources */ = {isa = PBXBuildFile; fileRef = B2E377B719FF127A00A10D5E /* LMAppController.m */; }; 60 | /* End PBXBuildFile section */ 61 | 62 | /* Begin PBXContainerItemProxy section */ 63 | A073BBCC19F9C7AC0068D6A6 /* PBXContainerItemProxy */ = { 64 | isa = PBXContainerItemProxy; 65 | containerPortal = A073BBAA19F9C7AB0068D6A6 /* Project object */; 66 | proxyType = 1; 67 | remoteGlobalIDString = A073BBB119F9C7AB0068D6A6; 68 | remoteInfo = WatchSpringboard; 69 | }; 70 | /* End PBXContainerItemProxy section */ 71 | 72 | /* Begin PBXFileReference section */ 73 | A069C6CA19FC5AAD00E7E6D5 /* Icon-34@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-34@2x.png"; sourceTree = ""; }; 74 | A069C6CC19FEBB7400E7E6D5 /* Icon-0@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-0@2x.png"; sourceTree = ""; }; 75 | A069C6CE19FEDA9B00E7E6D5 /* LICENSE */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = LICENSE; sourceTree = ""; }; 76 | A069C6D019FEDAA200E7E6D5 /* README.TXT */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = README.TXT; sourceTree = ""; }; 77 | A073BBB219F9C7AB0068D6A6 /* WatchSpringboard.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = WatchSpringboard.app; sourceTree = BUILT_PRODUCTS_DIR; }; 78 | A073BBB619F9C7AB0068D6A6 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 79 | A073BBB719F9C7AB0068D6A6 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 80 | A073BBB919F9C7AB0068D6A6 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 81 | A073BBBA19F9C7AB0068D6A6 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 82 | A073BBBC19F9C7AB0068D6A6 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 83 | A073BBBD19F9C7AB0068D6A6 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 84 | A073BBC019F9C7AB0068D6A6 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 85 | A073BBC219F9C7AB0068D6A6 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 86 | A073BBC519F9C7AB0068D6A6 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; 87 | A073BBCB19F9C7AC0068D6A6 /* WatchSpringboardTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = WatchSpringboardTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 88 | A073BBD019F9C7AC0068D6A6 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 89 | A073BBD119F9C7AC0068D6A6 /* WatchSpringboardTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = WatchSpringboardTests.m; sourceTree = ""; }; 90 | A073BBDC19F9C8220068D6A6 /* LMSpringboardView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LMSpringboardView.h; sourceTree = ""; }; 91 | A073BBDD19F9C8220068D6A6 /* LMSpringboardView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LMSpringboardView.m; sourceTree = ""; }; 92 | A089811119FAF4EC00034E0B /* LMViewControllerView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LMViewControllerView.h; sourceTree = ""; }; 93 | A089811219FAF4EC00034E0B /* LMViewControllerView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LMViewControllerView.m; sourceTree = ""; }; 94 | A089811519FAFC7900034E0B /* Icon-0-for-vibrancy@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-0-for-vibrancy@2x.png"; sourceTree = ""; }; 95 | A089811619FAFC7900034E0B /* Icon-10@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-10@2x.png"; sourceTree = ""; }; 96 | A089811719FAFC7900034E0B /* Icon-11@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-11@2x.png"; sourceTree = ""; }; 97 | A089811819FAFC7900034E0B /* Icon-12@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-12@2x.png"; sourceTree = ""; }; 98 | A089811919FAFC7900034E0B /* Icon-13@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-13@2x.png"; sourceTree = ""; }; 99 | A089811A19FAFC7900034E0B /* Icon-14@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-14@2x.png"; sourceTree = ""; }; 100 | A089811B19FAFC7900034E0B /* Icon-15@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-15@2x.png"; sourceTree = ""; }; 101 | A089811C19FAFC7900034E0B /* Icon-16@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-16@2x.png"; sourceTree = ""; }; 102 | A089811D19FAFC7900034E0B /* Icon-17@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-17@2x.png"; sourceTree = ""; }; 103 | A089811E19FAFC7900034E0B /* Icon-18@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-18@2x.png"; sourceTree = ""; }; 104 | A089811F19FAFC7900034E0B /* Icon-19@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-19@2x.png"; sourceTree = ""; }; 105 | A089812019FAFC7900034E0B /* Icon-1@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-1@2x.png"; sourceTree = ""; }; 106 | A089812119FAFC7900034E0B /* Icon-20@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-20@2x.png"; sourceTree = ""; }; 107 | A089812219FAFC7900034E0B /* Icon-21@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-21@2x.png"; sourceTree = ""; }; 108 | A089812319FAFC7900034E0B /* Icon-22@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-22@2x.png"; sourceTree = ""; }; 109 | A089812419FAFC7900034E0B /* Icon-23@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-23@2x.png"; sourceTree = ""; }; 110 | A089812519FAFC7900034E0B /* Icon-24@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-24@2x.png"; sourceTree = ""; }; 111 | A089812619FAFC7900034E0B /* Icon-25@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-25@2x.png"; sourceTree = ""; }; 112 | A089812719FAFC7900034E0B /* Icon-26@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-26@2x.png"; sourceTree = ""; }; 113 | A089812819FAFC7900034E0B /* Icon-27@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-27@2x.png"; sourceTree = ""; }; 114 | A089812919FAFC7900034E0B /* Icon-28@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-28@2x.png"; sourceTree = ""; }; 115 | A089812A19FAFC7900034E0B /* Icon-29@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-29@2x.png"; sourceTree = ""; }; 116 | A089812B19FAFC7900034E0B /* Icon-2@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-2@2x.png"; sourceTree = ""; }; 117 | A089812C19FAFC7900034E0B /* Icon-30@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-30@2x.png"; sourceTree = ""; }; 118 | A089812D19FAFC7900034E0B /* Icon-31@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-31@2x.png"; sourceTree = ""; }; 119 | A089812E19FAFC7900034E0B /* Icon-32@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-32@2x.png"; sourceTree = ""; }; 120 | A089812F19FAFC7900034E0B /* Icon-33@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-33@2x.png"; sourceTree = ""; }; 121 | A089813019FAFC7900034E0B /* Icon-3@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-3@2x.png"; sourceTree = ""; }; 122 | A089813119FAFC7900034E0B /* Icon-4@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-4@2x.png"; sourceTree = ""; }; 123 | A089813219FAFC7900034E0B /* Icon-5@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-5@2x.png"; sourceTree = ""; }; 124 | A089813319FAFC7900034E0B /* Icon-6@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-6@2x.png"; sourceTree = ""; }; 125 | A089813419FAFC7900034E0B /* Icon-7@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-7@2x.png"; sourceTree = ""; }; 126 | A089813519FAFC7900034E0B /* Icon-8@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-8@2x.png"; sourceTree = ""; }; 127 | A089813619FAFC7900034E0B /* Icon-9@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-9@2x.png"; sourceTree = ""; }; 128 | A089815919FB039E00034E0B /* LMSpringboardItemView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LMSpringboardItemView.h; sourceTree = ""; }; 129 | A089815A19FB039E00034E0B /* LMSpringboardItemView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LMSpringboardItemView.m; sourceTree = ""; }; 130 | B2E377B419FF127A00A10D5E /* LMApp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LMApp.h; path = LaunchServicesApis/LMApp.h; sourceTree = SOURCE_ROOT; }; 131 | B2E377B519FF127A00A10D5E /* LMApp.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = LMApp.m; path = LaunchServicesApis/LMApp.m; sourceTree = SOURCE_ROOT; }; 132 | B2E377B619FF127A00A10D5E /* LMAppController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LMAppController.h; path = LaunchServicesApis/LMAppController.h; sourceTree = SOURCE_ROOT; }; 133 | B2E377B719FF127A00A10D5E /* LMAppController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = LMAppController.m; path = LaunchServicesApis/LMAppController.m; sourceTree = SOURCE_ROOT; }; 134 | /* End PBXFileReference section */ 135 | 136 | /* Begin PBXFrameworksBuildPhase section */ 137 | A073BBAF19F9C7AB0068D6A6 /* Frameworks */ = { 138 | isa = PBXFrameworksBuildPhase; 139 | buildActionMask = 2147483647; 140 | files = ( 141 | ); 142 | runOnlyForDeploymentPostprocessing = 0; 143 | }; 144 | A073BBC819F9C7AC0068D6A6 /* Frameworks */ = { 145 | isa = PBXFrameworksBuildPhase; 146 | buildActionMask = 2147483647; 147 | files = ( 148 | ); 149 | runOnlyForDeploymentPostprocessing = 0; 150 | }; 151 | /* End PBXFrameworksBuildPhase section */ 152 | 153 | /* Begin PBXGroup section */ 154 | A073BBA919F9C7AB0068D6A6 = { 155 | isa = PBXGroup; 156 | children = ( 157 | A069C6D019FEDAA200E7E6D5 /* README.TXT */, 158 | A069C6CE19FEDA9B00E7E6D5 /* LICENSE */, 159 | A073BBB419F9C7AB0068D6A6 /* WatchSpringboard */, 160 | A073BBCE19F9C7AC0068D6A6 /* WatchSpringboardTests */, 161 | A073BBB319F9C7AB0068D6A6 /* Products */, 162 | ); 163 | sourceTree = ""; 164 | }; 165 | A073BBB319F9C7AB0068D6A6 /* Products */ = { 166 | isa = PBXGroup; 167 | children = ( 168 | A073BBB219F9C7AB0068D6A6 /* WatchSpringboard.app */, 169 | A073BBCB19F9C7AC0068D6A6 /* WatchSpringboardTests.xctest */, 170 | ); 171 | name = Products; 172 | sourceTree = ""; 173 | }; 174 | A073BBB419F9C7AB0068D6A6 /* WatchSpringboard */ = { 175 | isa = PBXGroup; 176 | children = ( 177 | B2E377B319FF126500A10D5E /* PrivateLaunchServicesApis */, 178 | A073BBDB19F9C7F80068D6A6 /* LMSpringboardView */, 179 | A073BBB919F9C7AB0068D6A6 /* AppDelegate.h */, 180 | A073BBBA19F9C7AB0068D6A6 /* AppDelegate.m */, 181 | A073BBBC19F9C7AB0068D6A6 /* ViewController.h */, 182 | A073BBBD19F9C7AB0068D6A6 /* ViewController.m */, 183 | A089811119FAF4EC00034E0B /* LMViewControllerView.h */, 184 | A089811219FAF4EC00034E0B /* LMViewControllerView.m */, 185 | A073BBBF19F9C7AB0068D6A6 /* Main.storyboard */, 186 | A073BBC219F9C7AB0068D6A6 /* Images.xcassets */, 187 | A073BBC419F9C7AB0068D6A6 /* LaunchScreen.xib */, 188 | A089811419FAFC7900034E0B /* Icons */, 189 | A073BBB519F9C7AB0068D6A6 /* Supporting Files */, 190 | ); 191 | path = WatchSpringboard; 192 | sourceTree = ""; 193 | }; 194 | A073BBB519F9C7AB0068D6A6 /* Supporting Files */ = { 195 | isa = PBXGroup; 196 | children = ( 197 | A073BBB619F9C7AB0068D6A6 /* Info.plist */, 198 | A073BBB719F9C7AB0068D6A6 /* main.m */, 199 | ); 200 | name = "Supporting Files"; 201 | sourceTree = ""; 202 | }; 203 | A073BBCE19F9C7AC0068D6A6 /* WatchSpringboardTests */ = { 204 | isa = PBXGroup; 205 | children = ( 206 | A073BBD119F9C7AC0068D6A6 /* WatchSpringboardTests.m */, 207 | A073BBCF19F9C7AC0068D6A6 /* Supporting Files */, 208 | ); 209 | path = WatchSpringboardTests; 210 | sourceTree = ""; 211 | }; 212 | A073BBCF19F9C7AC0068D6A6 /* Supporting Files */ = { 213 | isa = PBXGroup; 214 | children = ( 215 | A073BBD019F9C7AC0068D6A6 /* Info.plist */, 216 | ); 217 | name = "Supporting Files"; 218 | sourceTree = ""; 219 | }; 220 | A073BBDB19F9C7F80068D6A6 /* LMSpringboardView */ = { 221 | isa = PBXGroup; 222 | children = ( 223 | A073BBDC19F9C8220068D6A6 /* LMSpringboardView.h */, 224 | A073BBDD19F9C8220068D6A6 /* LMSpringboardView.m */, 225 | A089815919FB039E00034E0B /* LMSpringboardItemView.h */, 226 | A089815A19FB039E00034E0B /* LMSpringboardItemView.m */, 227 | ); 228 | name = LMSpringboardView; 229 | sourceTree = ""; 230 | }; 231 | A089811419FAFC7900034E0B /* Icons */ = { 232 | isa = PBXGroup; 233 | children = ( 234 | A089811519FAFC7900034E0B /* Icon-0-for-vibrancy@2x.png */, 235 | A069C6CC19FEBB7400E7E6D5 /* Icon-0@2x.png */, 236 | A089812019FAFC7900034E0B /* Icon-1@2x.png */, 237 | A089812B19FAFC7900034E0B /* Icon-2@2x.png */, 238 | A089813019FAFC7900034E0B /* Icon-3@2x.png */, 239 | A089813119FAFC7900034E0B /* Icon-4@2x.png */, 240 | A089813219FAFC7900034E0B /* Icon-5@2x.png */, 241 | A089813319FAFC7900034E0B /* Icon-6@2x.png */, 242 | A089813419FAFC7900034E0B /* Icon-7@2x.png */, 243 | A089813519FAFC7900034E0B /* Icon-8@2x.png */, 244 | A089813619FAFC7900034E0B /* Icon-9@2x.png */, 245 | A089811619FAFC7900034E0B /* Icon-10@2x.png */, 246 | A089811719FAFC7900034E0B /* Icon-11@2x.png */, 247 | A089811819FAFC7900034E0B /* Icon-12@2x.png */, 248 | A089811919FAFC7900034E0B /* Icon-13@2x.png */, 249 | A089811A19FAFC7900034E0B /* Icon-14@2x.png */, 250 | A089811B19FAFC7900034E0B /* Icon-15@2x.png */, 251 | A089811C19FAFC7900034E0B /* Icon-16@2x.png */, 252 | A089811D19FAFC7900034E0B /* Icon-17@2x.png */, 253 | A089811E19FAFC7900034E0B /* Icon-18@2x.png */, 254 | A089811F19FAFC7900034E0B /* Icon-19@2x.png */, 255 | A089812119FAFC7900034E0B /* Icon-20@2x.png */, 256 | A089812219FAFC7900034E0B /* Icon-21@2x.png */, 257 | A089812319FAFC7900034E0B /* Icon-22@2x.png */, 258 | A089812419FAFC7900034E0B /* Icon-23@2x.png */, 259 | A089812519FAFC7900034E0B /* Icon-24@2x.png */, 260 | A089812619FAFC7900034E0B /* Icon-25@2x.png */, 261 | A089812719FAFC7900034E0B /* Icon-26@2x.png */, 262 | A089812819FAFC7900034E0B /* Icon-27@2x.png */, 263 | A089812919FAFC7900034E0B /* Icon-28@2x.png */, 264 | A089812A19FAFC7900034E0B /* Icon-29@2x.png */, 265 | A089812C19FAFC7900034E0B /* Icon-30@2x.png */, 266 | A089812D19FAFC7900034E0B /* Icon-31@2x.png */, 267 | A089812E19FAFC7900034E0B /* Icon-32@2x.png */, 268 | A089812F19FAFC7900034E0B /* Icon-33@2x.png */, 269 | A069C6CA19FC5AAD00E7E6D5 /* Icon-34@2x.png */, 270 | ); 271 | path = Icons; 272 | sourceTree = ""; 273 | }; 274 | B2E377B319FF126500A10D5E /* PrivateLaunchServicesApis */ = { 275 | isa = PBXGroup; 276 | children = ( 277 | B2E377B419FF127A00A10D5E /* LMApp.h */, 278 | B2E377B519FF127A00A10D5E /* LMApp.m */, 279 | B2E377B619FF127A00A10D5E /* LMAppController.h */, 280 | B2E377B719FF127A00A10D5E /* LMAppController.m */, 281 | ); 282 | name = PrivateLaunchServicesApis; 283 | sourceTree = ""; 284 | }; 285 | /* End PBXGroup section */ 286 | 287 | /* Begin PBXNativeTarget section */ 288 | A073BBB119F9C7AB0068D6A6 /* WatchSpringboard */ = { 289 | isa = PBXNativeTarget; 290 | buildConfigurationList = A073BBD519F9C7AC0068D6A6 /* Build configuration list for PBXNativeTarget "WatchSpringboard" */; 291 | buildPhases = ( 292 | A073BBAE19F9C7AB0068D6A6 /* Sources */, 293 | A073BBAF19F9C7AB0068D6A6 /* Frameworks */, 294 | A073BBB019F9C7AB0068D6A6 /* Resources */, 295 | ); 296 | buildRules = ( 297 | ); 298 | dependencies = ( 299 | ); 300 | name = WatchSpringboard; 301 | productName = WatchSpringboard; 302 | productReference = A073BBB219F9C7AB0068D6A6 /* WatchSpringboard.app */; 303 | productType = "com.apple.product-type.application"; 304 | }; 305 | A073BBCA19F9C7AC0068D6A6 /* WatchSpringboardTests */ = { 306 | isa = PBXNativeTarget; 307 | buildConfigurationList = A073BBD819F9C7AC0068D6A6 /* Build configuration list for PBXNativeTarget "WatchSpringboardTests" */; 308 | buildPhases = ( 309 | A073BBC719F9C7AC0068D6A6 /* Sources */, 310 | A073BBC819F9C7AC0068D6A6 /* Frameworks */, 311 | A073BBC919F9C7AC0068D6A6 /* Resources */, 312 | ); 313 | buildRules = ( 314 | ); 315 | dependencies = ( 316 | A073BBCD19F9C7AC0068D6A6 /* PBXTargetDependency */, 317 | ); 318 | name = WatchSpringboardTests; 319 | productName = WatchSpringboardTests; 320 | productReference = A073BBCB19F9C7AC0068D6A6 /* WatchSpringboardTests.xctest */; 321 | productType = "com.apple.product-type.bundle.unit-test"; 322 | }; 323 | /* End PBXNativeTarget section */ 324 | 325 | /* Begin PBXProject section */ 326 | A073BBAA19F9C7AB0068D6A6 /* Project object */ = { 327 | isa = PBXProject; 328 | attributes = { 329 | LastUpgradeCheck = 0610; 330 | ORGANIZATIONNAME = "Lucas Menge"; 331 | TargetAttributes = { 332 | A073BBB119F9C7AB0068D6A6 = { 333 | CreatedOnToolsVersion = 6.1; 334 | }; 335 | A073BBCA19F9C7AC0068D6A6 = { 336 | CreatedOnToolsVersion = 6.1; 337 | TestTargetID = A073BBB119F9C7AB0068D6A6; 338 | }; 339 | }; 340 | }; 341 | buildConfigurationList = A073BBAD19F9C7AB0068D6A6 /* Build configuration list for PBXProject "WatchSpringboard" */; 342 | compatibilityVersion = "Xcode 3.2"; 343 | developmentRegion = English; 344 | hasScannedForEncodings = 0; 345 | knownRegions = ( 346 | en, 347 | Base, 348 | ); 349 | mainGroup = A073BBA919F9C7AB0068D6A6; 350 | productRefGroup = A073BBB319F9C7AB0068D6A6 /* Products */; 351 | projectDirPath = ""; 352 | projectRoot = ""; 353 | targets = ( 354 | A073BBB119F9C7AB0068D6A6 /* WatchSpringboard */, 355 | A073BBCA19F9C7AC0068D6A6 /* WatchSpringboardTests */, 356 | ); 357 | }; 358 | /* End PBXProject section */ 359 | 360 | /* Begin PBXResourcesBuildPhase section */ 361 | A073BBB019F9C7AB0068D6A6 /* Resources */ = { 362 | isa = PBXResourcesBuildPhase; 363 | buildActionMask = 2147483647; 364 | files = ( 365 | A089814019FAFC7900034E0B /* Icon-18@2x.png in Resources */, 366 | A089814F19FAFC7900034E0B /* Icon-31@2x.png in Resources */, 367 | A089813C19FAFC7900034E0B /* Icon-14@2x.png in Resources */, 368 | A089814619FAFC7900034E0B /* Icon-23@2x.png in Resources */, 369 | A089815719FAFC7900034E0B /* Icon-8@2x.png in Resources */, 370 | A089814A19FAFC7900034E0B /* Icon-27@2x.png in Resources */, 371 | A089814719FAFC7900034E0B /* Icon-24@2x.png in Resources */, 372 | A089813719FAFC7900034E0B /* Icon-0-for-vibrancy@2x.png in Resources */, 373 | A089814319FAFC7900034E0B /* Icon-20@2x.png in Resources */, 374 | A089814419FAFC7900034E0B /* Icon-21@2x.png in Resources */, 375 | A089815019FAFC7900034E0B /* Icon-32@2x.png in Resources */, 376 | A089814519FAFC7900034E0B /* Icon-22@2x.png in Resources */, 377 | A069C6CF19FEDA9B00E7E6D5 /* LICENSE in Resources */, 378 | A089814119FAFC7900034E0B /* Icon-19@2x.png in Resources */, 379 | A073BBC119F9C7AB0068D6A6 /* Main.storyboard in Resources */, 380 | A089814B19FAFC7900034E0B /* Icon-28@2x.png in Resources */, 381 | A069C6D119FEDAA200E7E6D5 /* README.TXT in Resources */, 382 | A089814E19FAFC7900034E0B /* Icon-30@2x.png in Resources */, 383 | A089814219FAFC7900034E0B /* Icon-1@2x.png in Resources */, 384 | A089813D19FAFC7900034E0B /* Icon-15@2x.png in Resources */, 385 | A073BBC619F9C7AB0068D6A6 /* LaunchScreen.xib in Resources */, 386 | A089814919FAFC7900034E0B /* Icon-26@2x.png in Resources */, 387 | A089813E19FAFC7900034E0B /* Icon-16@2x.png in Resources */, 388 | A089815319FAFC7900034E0B /* Icon-4@2x.png in Resources */, 389 | A089813A19FAFC7900034E0B /* Icon-12@2x.png in Resources */, 390 | A089815519FAFC7900034E0B /* Icon-6@2x.png in Resources */, 391 | A089813919FAFC7900034E0B /* Icon-11@2x.png in Resources */, 392 | A089813B19FAFC7900034E0B /* Icon-13@2x.png in Resources */, 393 | A069C6CD19FEBB7400E7E6D5 /* Icon-0@2x.png in Resources */, 394 | A089815819FAFC7900034E0B /* Icon-9@2x.png in Resources */, 395 | A089814D19FAFC7900034E0B /* Icon-2@2x.png in Resources */, 396 | A089814819FAFC7900034E0B /* Icon-25@2x.png in Resources */, 397 | A089815619FAFC7900034E0B /* Icon-7@2x.png in Resources */, 398 | A089813F19FAFC7900034E0B /* Icon-17@2x.png in Resources */, 399 | A089813819FAFC7900034E0B /* Icon-10@2x.png in Resources */, 400 | A089815219FAFC7900034E0B /* Icon-3@2x.png in Resources */, 401 | A089815119FAFC7900034E0B /* Icon-33@2x.png in Resources */, 402 | A069C6CB19FC5AAE00E7E6D5 /* Icon-34@2x.png in Resources */, 403 | A089815419FAFC7900034E0B /* Icon-5@2x.png in Resources */, 404 | A089814C19FAFC7900034E0B /* Icon-29@2x.png in Resources */, 405 | A073BBC319F9C7AB0068D6A6 /* Images.xcassets in Resources */, 406 | ); 407 | runOnlyForDeploymentPostprocessing = 0; 408 | }; 409 | A073BBC919F9C7AC0068D6A6 /* Resources */ = { 410 | isa = PBXResourcesBuildPhase; 411 | buildActionMask = 2147483647; 412 | files = ( 413 | ); 414 | runOnlyForDeploymentPostprocessing = 0; 415 | }; 416 | /* End PBXResourcesBuildPhase section */ 417 | 418 | /* Begin PBXSourcesBuildPhase section */ 419 | A073BBAE19F9C7AB0068D6A6 /* Sources */ = { 420 | isa = PBXSourcesBuildPhase; 421 | buildActionMask = 2147483647; 422 | files = ( 423 | A089815B19FB039E00034E0B /* LMSpringboardItemView.m in Sources */, 424 | A073BBBE19F9C7AB0068D6A6 /* ViewController.m in Sources */, 425 | B2E377B919FF127A00A10D5E /* LMAppController.m in Sources */, 426 | A089811319FAF4EC00034E0B /* LMViewControllerView.m in Sources */, 427 | A073BBDE19F9C8220068D6A6 /* LMSpringboardView.m in Sources */, 428 | B2E377B819FF127A00A10D5E /* LMApp.m in Sources */, 429 | A073BBBB19F9C7AB0068D6A6 /* AppDelegate.m in Sources */, 430 | A073BBB819F9C7AB0068D6A6 /* main.m in Sources */, 431 | ); 432 | runOnlyForDeploymentPostprocessing = 0; 433 | }; 434 | A073BBC719F9C7AC0068D6A6 /* Sources */ = { 435 | isa = PBXSourcesBuildPhase; 436 | buildActionMask = 2147483647; 437 | files = ( 438 | A073BBD219F9C7AC0068D6A6 /* WatchSpringboardTests.m in Sources */, 439 | ); 440 | runOnlyForDeploymentPostprocessing = 0; 441 | }; 442 | /* End PBXSourcesBuildPhase section */ 443 | 444 | /* Begin PBXTargetDependency section */ 445 | A073BBCD19F9C7AC0068D6A6 /* PBXTargetDependency */ = { 446 | isa = PBXTargetDependency; 447 | target = A073BBB119F9C7AB0068D6A6 /* WatchSpringboard */; 448 | targetProxy = A073BBCC19F9C7AC0068D6A6 /* PBXContainerItemProxy */; 449 | }; 450 | /* End PBXTargetDependency section */ 451 | 452 | /* Begin PBXVariantGroup section */ 453 | A073BBBF19F9C7AB0068D6A6 /* Main.storyboard */ = { 454 | isa = PBXVariantGroup; 455 | children = ( 456 | A073BBC019F9C7AB0068D6A6 /* Base */, 457 | ); 458 | name = Main.storyboard; 459 | sourceTree = ""; 460 | }; 461 | A073BBC419F9C7AB0068D6A6 /* LaunchScreen.xib */ = { 462 | isa = PBXVariantGroup; 463 | children = ( 464 | A073BBC519F9C7AB0068D6A6 /* Base */, 465 | ); 466 | name = LaunchScreen.xib; 467 | sourceTree = ""; 468 | }; 469 | /* End PBXVariantGroup section */ 470 | 471 | /* Begin XCBuildConfiguration section */ 472 | A073BBD319F9C7AC0068D6A6 /* Debug */ = { 473 | isa = XCBuildConfiguration; 474 | buildSettings = { 475 | ALWAYS_SEARCH_USER_PATHS = NO; 476 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 477 | CLANG_CXX_LIBRARY = "libc++"; 478 | CLANG_ENABLE_MODULES = YES; 479 | CLANG_ENABLE_OBJC_ARC = YES; 480 | CLANG_WARN_BOOL_CONVERSION = YES; 481 | CLANG_WARN_CONSTANT_CONVERSION = YES; 482 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 483 | CLANG_WARN_EMPTY_BODY = YES; 484 | CLANG_WARN_ENUM_CONVERSION = YES; 485 | CLANG_WARN_INT_CONVERSION = YES; 486 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 487 | CLANG_WARN_UNREACHABLE_CODE = YES; 488 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 489 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 490 | COPY_PHASE_STRIP = NO; 491 | ENABLE_STRICT_OBJC_MSGSEND = YES; 492 | GCC_C_LANGUAGE_STANDARD = gnu99; 493 | GCC_DYNAMIC_NO_PIC = NO; 494 | GCC_OPTIMIZATION_LEVEL = 0; 495 | GCC_PREPROCESSOR_DEFINITIONS = ( 496 | "DEBUG=1", 497 | "$(inherited)", 498 | ); 499 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 500 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 501 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 502 | GCC_WARN_UNDECLARED_SELECTOR = YES; 503 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 504 | GCC_WARN_UNUSED_FUNCTION = YES; 505 | GCC_WARN_UNUSED_VARIABLE = YES; 506 | IPHONEOS_DEPLOYMENT_TARGET = 8.1; 507 | MTL_ENABLE_DEBUG_INFO = YES; 508 | ONLY_ACTIVE_ARCH = YES; 509 | SDKROOT = iphoneos; 510 | TARGETED_DEVICE_FAMILY = "1,2"; 511 | }; 512 | name = Debug; 513 | }; 514 | A073BBD419F9C7AC0068D6A6 /* Release */ = { 515 | isa = XCBuildConfiguration; 516 | buildSettings = { 517 | ALWAYS_SEARCH_USER_PATHS = NO; 518 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 519 | CLANG_CXX_LIBRARY = "libc++"; 520 | CLANG_ENABLE_MODULES = YES; 521 | CLANG_ENABLE_OBJC_ARC = YES; 522 | CLANG_WARN_BOOL_CONVERSION = YES; 523 | CLANG_WARN_CONSTANT_CONVERSION = YES; 524 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 525 | CLANG_WARN_EMPTY_BODY = YES; 526 | CLANG_WARN_ENUM_CONVERSION = YES; 527 | CLANG_WARN_INT_CONVERSION = YES; 528 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 529 | CLANG_WARN_UNREACHABLE_CODE = YES; 530 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 531 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 532 | COPY_PHASE_STRIP = YES; 533 | ENABLE_NS_ASSERTIONS = NO; 534 | ENABLE_STRICT_OBJC_MSGSEND = YES; 535 | GCC_C_LANGUAGE_STANDARD = gnu99; 536 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 537 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 538 | GCC_WARN_UNDECLARED_SELECTOR = YES; 539 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 540 | GCC_WARN_UNUSED_FUNCTION = YES; 541 | GCC_WARN_UNUSED_VARIABLE = YES; 542 | IPHONEOS_DEPLOYMENT_TARGET = 8.1; 543 | MTL_ENABLE_DEBUG_INFO = NO; 544 | SDKROOT = iphoneos; 545 | TARGETED_DEVICE_FAMILY = "1,2"; 546 | VALIDATE_PRODUCT = YES; 547 | }; 548 | name = Release; 549 | }; 550 | A073BBD619F9C7AC0068D6A6 /* Debug */ = { 551 | isa = XCBuildConfiguration; 552 | buildSettings = { 553 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 554 | INFOPLIST_FILE = WatchSpringboard/Info.plist; 555 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 556 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 557 | PRODUCT_NAME = "$(TARGET_NAME)"; 558 | }; 559 | name = Debug; 560 | }; 561 | A073BBD719F9C7AC0068D6A6 /* Release */ = { 562 | isa = XCBuildConfiguration; 563 | buildSettings = { 564 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 565 | INFOPLIST_FILE = WatchSpringboard/Info.plist; 566 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 567 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 568 | PRODUCT_NAME = "$(TARGET_NAME)"; 569 | }; 570 | name = Release; 571 | }; 572 | A073BBD919F9C7AC0068D6A6 /* Debug */ = { 573 | isa = XCBuildConfiguration; 574 | buildSettings = { 575 | BUNDLE_LOADER = "$(TEST_HOST)"; 576 | FRAMEWORK_SEARCH_PATHS = ( 577 | "$(SDKROOT)/Developer/Library/Frameworks", 578 | "$(inherited)", 579 | ); 580 | GCC_PREPROCESSOR_DEFINITIONS = ( 581 | "DEBUG=1", 582 | "$(inherited)", 583 | ); 584 | INFOPLIST_FILE = WatchSpringboardTests/Info.plist; 585 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 586 | PRODUCT_NAME = "$(TARGET_NAME)"; 587 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/WatchSpringboard.app/WatchSpringboard"; 588 | }; 589 | name = Debug; 590 | }; 591 | A073BBDA19F9C7AC0068D6A6 /* Release */ = { 592 | isa = XCBuildConfiguration; 593 | buildSettings = { 594 | BUNDLE_LOADER = "$(TEST_HOST)"; 595 | FRAMEWORK_SEARCH_PATHS = ( 596 | "$(SDKROOT)/Developer/Library/Frameworks", 597 | "$(inherited)", 598 | ); 599 | INFOPLIST_FILE = WatchSpringboardTests/Info.plist; 600 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 601 | PRODUCT_NAME = "$(TARGET_NAME)"; 602 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/WatchSpringboard.app/WatchSpringboard"; 603 | }; 604 | name = Release; 605 | }; 606 | /* End XCBuildConfiguration section */ 607 | 608 | /* Begin XCConfigurationList section */ 609 | A073BBAD19F9C7AB0068D6A6 /* Build configuration list for PBXProject "WatchSpringboard" */ = { 610 | isa = XCConfigurationList; 611 | buildConfigurations = ( 612 | A073BBD319F9C7AC0068D6A6 /* Debug */, 613 | A073BBD419F9C7AC0068D6A6 /* Release */, 614 | ); 615 | defaultConfigurationIsVisible = 0; 616 | defaultConfigurationName = Release; 617 | }; 618 | A073BBD519F9C7AC0068D6A6 /* Build configuration list for PBXNativeTarget "WatchSpringboard" */ = { 619 | isa = XCConfigurationList; 620 | buildConfigurations = ( 621 | A073BBD619F9C7AC0068D6A6 /* Debug */, 622 | A073BBD719F9C7AC0068D6A6 /* Release */, 623 | ); 624 | defaultConfigurationIsVisible = 0; 625 | defaultConfigurationName = Release; 626 | }; 627 | A073BBD819F9C7AC0068D6A6 /* Build configuration list for PBXNativeTarget "WatchSpringboardTests" */ = { 628 | isa = XCConfigurationList; 629 | buildConfigurations = ( 630 | A073BBD919F9C7AC0068D6A6 /* Debug */, 631 | A073BBDA19F9C7AC0068D6A6 /* Release */, 632 | ); 633 | defaultConfigurationIsVisible = 0; 634 | defaultConfigurationName = Release; 635 | }; 636 | /* End XCConfigurationList section */ 637 | }; 638 | rootObject = A073BBAA19F9C7AB0068D6A6 /* Project object */; 639 | } 640 | -------------------------------------------------------------------------------- /WatchSpringboard.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /WatchSpringboard/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // WatchSpringboard 4 | // 5 | // Created by Lucas Menge on 10/23/14. 6 | // Copyright (c) 2014 Lucas Menge. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /WatchSpringboard/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // WatchSpringboard 4 | // 5 | // Created by Lucas Menge on 10/23/14. 6 | // Copyright (c) 2014 Lucas Menge. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | // Override point for customization after application launch. 20 | return YES; 21 | } 22 | 23 | - (void)applicationWillResignActive:(UIApplication *)application { 24 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 25 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 26 | } 27 | 28 | - (void)applicationDidEnterBackground:(UIApplication *)application { 29 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 30 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 31 | } 32 | 33 | - (void)applicationWillEnterForeground:(UIApplication *)application { 34 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 35 | } 36 | 37 | - (void)applicationDidBecomeActive:(UIApplication *)application { 38 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 39 | } 40 | 41 | - (void)applicationWillTerminate:(UIApplication *)application { 42 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 43 | } 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /WatchSpringboard/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /WatchSpringboard/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /WatchSpringboard/Icons/Icon-0-for-vibrancy@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmmenge/WatchSpringboard-Prototype/a79e2b43e630b2c8341031ddca57ad3e7e555062/WatchSpringboard/Icons/Icon-0-for-vibrancy@2x.png -------------------------------------------------------------------------------- /WatchSpringboard/Icons/Icon-0@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmmenge/WatchSpringboard-Prototype/a79e2b43e630b2c8341031ddca57ad3e7e555062/WatchSpringboard/Icons/Icon-0@2x.png -------------------------------------------------------------------------------- /WatchSpringboard/Icons/Icon-10@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmmenge/WatchSpringboard-Prototype/a79e2b43e630b2c8341031ddca57ad3e7e555062/WatchSpringboard/Icons/Icon-10@2x.png -------------------------------------------------------------------------------- /WatchSpringboard/Icons/Icon-11@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmmenge/WatchSpringboard-Prototype/a79e2b43e630b2c8341031ddca57ad3e7e555062/WatchSpringboard/Icons/Icon-11@2x.png -------------------------------------------------------------------------------- /WatchSpringboard/Icons/Icon-12@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmmenge/WatchSpringboard-Prototype/a79e2b43e630b2c8341031ddca57ad3e7e555062/WatchSpringboard/Icons/Icon-12@2x.png -------------------------------------------------------------------------------- /WatchSpringboard/Icons/Icon-13@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmmenge/WatchSpringboard-Prototype/a79e2b43e630b2c8341031ddca57ad3e7e555062/WatchSpringboard/Icons/Icon-13@2x.png -------------------------------------------------------------------------------- /WatchSpringboard/Icons/Icon-14@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmmenge/WatchSpringboard-Prototype/a79e2b43e630b2c8341031ddca57ad3e7e555062/WatchSpringboard/Icons/Icon-14@2x.png -------------------------------------------------------------------------------- /WatchSpringboard/Icons/Icon-15@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmmenge/WatchSpringboard-Prototype/a79e2b43e630b2c8341031ddca57ad3e7e555062/WatchSpringboard/Icons/Icon-15@2x.png -------------------------------------------------------------------------------- /WatchSpringboard/Icons/Icon-16@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmmenge/WatchSpringboard-Prototype/a79e2b43e630b2c8341031ddca57ad3e7e555062/WatchSpringboard/Icons/Icon-16@2x.png -------------------------------------------------------------------------------- /WatchSpringboard/Icons/Icon-17@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmmenge/WatchSpringboard-Prototype/a79e2b43e630b2c8341031ddca57ad3e7e555062/WatchSpringboard/Icons/Icon-17@2x.png -------------------------------------------------------------------------------- /WatchSpringboard/Icons/Icon-18@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmmenge/WatchSpringboard-Prototype/a79e2b43e630b2c8341031ddca57ad3e7e555062/WatchSpringboard/Icons/Icon-18@2x.png -------------------------------------------------------------------------------- /WatchSpringboard/Icons/Icon-19@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmmenge/WatchSpringboard-Prototype/a79e2b43e630b2c8341031ddca57ad3e7e555062/WatchSpringboard/Icons/Icon-19@2x.png -------------------------------------------------------------------------------- /WatchSpringboard/Icons/Icon-1@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmmenge/WatchSpringboard-Prototype/a79e2b43e630b2c8341031ddca57ad3e7e555062/WatchSpringboard/Icons/Icon-1@2x.png -------------------------------------------------------------------------------- /WatchSpringboard/Icons/Icon-20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmmenge/WatchSpringboard-Prototype/a79e2b43e630b2c8341031ddca57ad3e7e555062/WatchSpringboard/Icons/Icon-20@2x.png -------------------------------------------------------------------------------- /WatchSpringboard/Icons/Icon-21@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmmenge/WatchSpringboard-Prototype/a79e2b43e630b2c8341031ddca57ad3e7e555062/WatchSpringboard/Icons/Icon-21@2x.png -------------------------------------------------------------------------------- /WatchSpringboard/Icons/Icon-22@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmmenge/WatchSpringboard-Prototype/a79e2b43e630b2c8341031ddca57ad3e7e555062/WatchSpringboard/Icons/Icon-22@2x.png -------------------------------------------------------------------------------- /WatchSpringboard/Icons/Icon-23@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmmenge/WatchSpringboard-Prototype/a79e2b43e630b2c8341031ddca57ad3e7e555062/WatchSpringboard/Icons/Icon-23@2x.png -------------------------------------------------------------------------------- /WatchSpringboard/Icons/Icon-24@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmmenge/WatchSpringboard-Prototype/a79e2b43e630b2c8341031ddca57ad3e7e555062/WatchSpringboard/Icons/Icon-24@2x.png -------------------------------------------------------------------------------- /WatchSpringboard/Icons/Icon-25@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmmenge/WatchSpringboard-Prototype/a79e2b43e630b2c8341031ddca57ad3e7e555062/WatchSpringboard/Icons/Icon-25@2x.png -------------------------------------------------------------------------------- /WatchSpringboard/Icons/Icon-26@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmmenge/WatchSpringboard-Prototype/a79e2b43e630b2c8341031ddca57ad3e7e555062/WatchSpringboard/Icons/Icon-26@2x.png -------------------------------------------------------------------------------- /WatchSpringboard/Icons/Icon-27@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmmenge/WatchSpringboard-Prototype/a79e2b43e630b2c8341031ddca57ad3e7e555062/WatchSpringboard/Icons/Icon-27@2x.png -------------------------------------------------------------------------------- /WatchSpringboard/Icons/Icon-28@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmmenge/WatchSpringboard-Prototype/a79e2b43e630b2c8341031ddca57ad3e7e555062/WatchSpringboard/Icons/Icon-28@2x.png -------------------------------------------------------------------------------- /WatchSpringboard/Icons/Icon-29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmmenge/WatchSpringboard-Prototype/a79e2b43e630b2c8341031ddca57ad3e7e555062/WatchSpringboard/Icons/Icon-29@2x.png -------------------------------------------------------------------------------- /WatchSpringboard/Icons/Icon-2@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmmenge/WatchSpringboard-Prototype/a79e2b43e630b2c8341031ddca57ad3e7e555062/WatchSpringboard/Icons/Icon-2@2x.png -------------------------------------------------------------------------------- /WatchSpringboard/Icons/Icon-30@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmmenge/WatchSpringboard-Prototype/a79e2b43e630b2c8341031ddca57ad3e7e555062/WatchSpringboard/Icons/Icon-30@2x.png -------------------------------------------------------------------------------- /WatchSpringboard/Icons/Icon-31@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmmenge/WatchSpringboard-Prototype/a79e2b43e630b2c8341031ddca57ad3e7e555062/WatchSpringboard/Icons/Icon-31@2x.png -------------------------------------------------------------------------------- /WatchSpringboard/Icons/Icon-32@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmmenge/WatchSpringboard-Prototype/a79e2b43e630b2c8341031ddca57ad3e7e555062/WatchSpringboard/Icons/Icon-32@2x.png -------------------------------------------------------------------------------- /WatchSpringboard/Icons/Icon-33@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmmenge/WatchSpringboard-Prototype/a79e2b43e630b2c8341031ddca57ad3e7e555062/WatchSpringboard/Icons/Icon-33@2x.png -------------------------------------------------------------------------------- /WatchSpringboard/Icons/Icon-34@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmmenge/WatchSpringboard-Prototype/a79e2b43e630b2c8341031ddca57ad3e7e555062/WatchSpringboard/Icons/Icon-34@2x.png -------------------------------------------------------------------------------- /WatchSpringboard/Icons/Icon-3@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmmenge/WatchSpringboard-Prototype/a79e2b43e630b2c8341031ddca57ad3e7e555062/WatchSpringboard/Icons/Icon-3@2x.png -------------------------------------------------------------------------------- /WatchSpringboard/Icons/Icon-4@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmmenge/WatchSpringboard-Prototype/a79e2b43e630b2c8341031ddca57ad3e7e555062/WatchSpringboard/Icons/Icon-4@2x.png -------------------------------------------------------------------------------- /WatchSpringboard/Icons/Icon-5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmmenge/WatchSpringboard-Prototype/a79e2b43e630b2c8341031ddca57ad3e7e555062/WatchSpringboard/Icons/Icon-5@2x.png -------------------------------------------------------------------------------- /WatchSpringboard/Icons/Icon-6@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmmenge/WatchSpringboard-Prototype/a79e2b43e630b2c8341031ddca57ad3e7e555062/WatchSpringboard/Icons/Icon-6@2x.png -------------------------------------------------------------------------------- /WatchSpringboard/Icons/Icon-7@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmmenge/WatchSpringboard-Prototype/a79e2b43e630b2c8341031ddca57ad3e7e555062/WatchSpringboard/Icons/Icon-7@2x.png -------------------------------------------------------------------------------- /WatchSpringboard/Icons/Icon-8@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmmenge/WatchSpringboard-Prototype/a79e2b43e630b2c8341031ddca57ad3e7e555062/WatchSpringboard/Icons/Icon-8@2x.png -------------------------------------------------------------------------------- /WatchSpringboard/Icons/Icon-9@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmmenge/WatchSpringboard-Prototype/a79e2b43e630b2c8341031ddca57ad3e7e555062/WatchSpringboard/Icons/Icon-9@2x.png -------------------------------------------------------------------------------- /WatchSpringboard/Images.xcassets/App.imageset/App@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmmenge/WatchSpringboard-Prototype/a79e2b43e630b2c8341031ddca57ad3e7e555062/WatchSpringboard/Images.xcassets/App.imageset/App@2x.png -------------------------------------------------------------------------------- /WatchSpringboard/Images.xcassets/App.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x", 10 | "filename" : "App@2x.png" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /WatchSpringboard/Images.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 | "idiom" : "ipad", 35 | "size" : "29x29", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "ipad", 40 | "size" : "29x29", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "40x40", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "40x40", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "76x76", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "76x76", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } -------------------------------------------------------------------------------- /WatchSpringboard/Images.xcassets/Icon.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x", 10 | "filename" : "Icon@2x.png" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x", 15 | "filename" : "Icon@3x.png" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /WatchSpringboard/Images.xcassets/Icon.imageset/Icon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmmenge/WatchSpringboard-Prototype/a79e2b43e630b2c8341031ddca57ad3e7e555062/WatchSpringboard/Images.xcassets/Icon.imageset/Icon@2x.png -------------------------------------------------------------------------------- /WatchSpringboard/Images.xcassets/Icon.imageset/Icon@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmmenge/WatchSpringboard-Prototype/a79e2b43e630b2c8341031ddca57ad3e7e555062/WatchSpringboard/Images.xcassets/Icon.imageset/Icon@3x.png -------------------------------------------------------------------------------- /WatchSpringboard/Images.xcassets/Wallpaper.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x", 10 | "filename" : "Wallpaper@2x.png" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /WatchSpringboard/Images.xcassets/Wallpaper.imageset/Wallpaper@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmmenge/WatchSpringboard-Prototype/a79e2b43e630b2c8341031ddca57ad3e7e555062/WatchSpringboard/Images.xcassets/Wallpaper.imageset/Wallpaper@2x.png -------------------------------------------------------------------------------- /WatchSpringboard/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | com.lmmenge.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UIStatusBarStyle 34 | UIStatusBarStyleLightContent 35 | UISupportedInterfaceOrientations 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationLandscapeLeft 39 | UIInterfaceOrientationLandscapeRight 40 | 41 | UISupportedInterfaceOrientations~ipad 42 | 43 | UIInterfaceOrientationPortrait 44 | UIInterfaceOrientationPortraitUpsideDown 45 | UIInterfaceOrientationLandscapeLeft 46 | UIInterfaceOrientationLandscapeRight 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /WatchSpringboard/LMSpringboardItemView.h: -------------------------------------------------------------------------------- 1 | // 2 | // LMSpringboardItemView.h 3 | // WatchSpringboard 4 | // 5 | // Created by Lucas Menge on 10/24/14. 6 | // Copyright (c) 2014 Lucas Menge. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface LMSpringboardItemView : UIView 12 | 13 | @property (readonly) UIImageView* icon; 14 | @property (readonly) UILabel* label; 15 | @property (nonatomic) CGFloat scale; 16 | - (void)setScale:(CGFloat)scale animated:(BOOL)animated; 17 | @property (nonatomic) BOOL isFolderLike; 18 | 19 | @property (nonatomic, copy) NSString* bundleIdentifier; 20 | 21 | - (void)setTitle:(NSString*)title; 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /WatchSpringboard/LMSpringboardItemView.m: -------------------------------------------------------------------------------- 1 | // 2 | // LMSpringboardItemView.m 3 | // WatchSpringboard 4 | // 5 | // Created by Lucas Menge on 10/24/14. 6 | // Copyright (c) 2014 Lucas Menge. All rights reserved. 7 | // 8 | 9 | #import "LMSpringboardItemView.h" 10 | 11 | static double const kLMSpringboardItemViewSmallThreshold = 0.75; 12 | 13 | @interface LMSpringboardItemView () 14 | { 15 | UIView* _visualEffectView; 16 | UIImageView* _visualEffectMaskView; 17 | } 18 | 19 | @end 20 | 21 | #pragma mark - 22 | 23 | @implementation LMSpringboardItemView 24 | 25 | - (void)setScale:(CGFloat)scale 26 | { 27 | [self setScale:scale animated:NO]; 28 | } 29 | 30 | - (void)setTitle:(NSString*)title 31 | { 32 | _label.text = title; 33 | [self setNeedsLayout]; 34 | } 35 | 36 | - (void)setIsFolderLike:(BOOL)isFolderLike 37 | { 38 | if(_isFolderLike != isFolderLike) 39 | { 40 | _isFolderLike = isFolderLike; 41 | if(_isFolderLike == YES) 42 | { 43 | _visualEffectView = [[UIView alloc] init]; 44 | UIView* vev = [[UIVisualEffectView alloc] initWithEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleLight]]; 45 | vev.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth; 46 | [_visualEffectView addSubview:vev]; 47 | [self insertSubview:_visualEffectView atIndex:0]; 48 | 49 | _visualEffectMaskView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"Icon.png"]]; 50 | _visualEffectMaskView.contentMode = UIViewContentModeScaleAspectFit; 51 | _visualEffectMaskView.autoresizingMask = vev.autoresizingMask; 52 | _visualEffectView.maskView = _visualEffectMaskView; 53 | } 54 | else 55 | { 56 | [_visualEffectView removeFromSuperview]; 57 | _visualEffectView = nil; 58 | _visualEffectMaskView = nil; 59 | } 60 | } 61 | } 62 | 63 | - (void)setScale:(CGFloat)scale animated:(BOOL)animated 64 | { 65 | if(_scale != scale) 66 | { 67 | BOOL wasSmallBefore = (_scale < kLMSpringboardItemViewSmallThreshold); 68 | _scale = scale; 69 | [self setNeedsLayout]; 70 | if((_scale < kLMSpringboardItemViewSmallThreshold) != wasSmallBefore) 71 | { 72 | if(animated == YES) 73 | { 74 | [UIView animateWithDuration:0.3 animations:^{ 75 | [self layoutIfNeeded]; 76 | if(_scale < kLMSpringboardItemViewSmallThreshold) 77 | _label.alpha = 0; 78 | else 79 | _label.alpha = 1; 80 | }]; 81 | } 82 | else 83 | { 84 | if(_scale < kLMSpringboardItemViewSmallThreshold) 85 | _label.alpha = 0; 86 | else 87 | _label.alpha = 1; 88 | } 89 | } 90 | } 91 | } 92 | 93 | #pragma mark - UIView 94 | 95 | - (void)layoutSubviews 96 | { 97 | [super layoutSubviews]; 98 | 99 | CGSize size = self.bounds.size; 100 | 101 | _icon.center = CGPointMake(size.width*0.5, size.height*0.5); 102 | _icon.bounds = CGRectMake(0, 0, size.width, size.height); 103 | 104 | _visualEffectView.center = _icon.center; 105 | _visualEffectView.bounds = _icon.bounds; 106 | _visualEffectMaskView.center = _icon.center; 107 | _visualEffectMaskView.bounds = _icon.bounds; 108 | 109 | [_label sizeToFit]; 110 | _label.center = CGPointMake(size.width*0.5, size.height+4); 111 | 112 | float scale = 60/size.width; 113 | _icon.transform = CGAffineTransformMakeScale(scale, scale); 114 | _visualEffectView.transform = _icon.transform; 115 | } 116 | 117 | - (instancetype)init 118 | { 119 | self = [super init]; 120 | if(self) 121 | { 122 | _scale = 1; 123 | 124 | _label = [[UILabel alloc] init]; 125 | _label.opaque = NO; 126 | _label.backgroundColor = nil; 127 | _label.textColor = [UIColor whiteColor]; 128 | _label.font = [UIFont systemFontOfSize:[UIFont smallSystemFontSize]]; 129 | [self addSubview:_label]; 130 | 131 | _icon = [[UIImageView alloc] init]; 132 | [self addSubview:_icon]; 133 | } 134 | return self; 135 | } 136 | 137 | @end 138 | -------------------------------------------------------------------------------- /WatchSpringboard/LMSpringboardView.h: -------------------------------------------------------------------------------- 1 | // 2 | // LMSpringboardView.h 3 | // WatchSpringboard 4 | // 5 | // Created by Lucas Menge on 10/23/14. 6 | // Copyright (c) 2014 Lucas Menge. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface LMSpringboardView : UIScrollView 12 | 13 | @property (copy,nonatomic) NSArray* itemViews; 14 | @property (nonatomic) NSUInteger itemDiameter; 15 | @property (nonatomic) NSUInteger itemPadding; 16 | @property (nonatomic) double minimumItemScaling; 17 | @property (nonatomic) double minimumZoomLevelToLaunchApp; 18 | @property (readonly) UITapGestureRecognizer* doubleTapGesture; 19 | 20 | - (void)showAllContentAnimated:(BOOL)animated; 21 | - (NSUInteger)indexOfItemClosestToPoint:(CGPoint)pointInSelf; 22 | - (void)centerOnIndex:(NSUInteger)index zoomScale:(CGFloat)zoomScale animated:(BOOL)animated; 23 | 24 | - (void)doIntroAnimation; 25 | 26 | @end 27 | -------------------------------------------------------------------------------- /WatchSpringboard/LMSpringboardView.m: -------------------------------------------------------------------------------- 1 | // 2 | // LMSpringboardView.m 3 | // WatchSpringboard 4 | // 5 | // Created by Lucas Menge on 10/23/14. 6 | // Copyright (c) 2014 Lucas Menge. All rights reserved. 7 | // 8 | 9 | #import "LMSpringboardView.h" 10 | 11 | #import "LMSpringboardItemView.h" 12 | 13 | #define LMPointDistance(x1, y1, x2, y2) (sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2))) 14 | #define LKPointDistanceSquared(x1, y1, x2, y2) ((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2)) 15 | 16 | @interface LMSpringboardView () 17 | { 18 | UIView* _touchView; 19 | UIView* _contentView; 20 | //UIView* _debugRectInContent; 21 | //UIView* _debugRectInScroll; 22 | 23 | // controls how much transform we apply to the views (not used) 24 | float _transformFactor; 25 | // a few state variables 26 | NSUInteger _lastFocusedViewIndex; 27 | CGFloat _zoomScaleCache; 28 | CGAffineTransform _minTransform; 29 | // dirty when the view changes width/height 30 | BOOL _minimumZoomLevelIsDirty; 31 | BOOL _contentSizeIsDirty; 32 | CGSize _contentSizeUnscaled; 33 | CGSize _contentSizeExtra; 34 | 35 | BOOL _centerOnEndDrag; 36 | BOOL _centerOnEndDeccel; 37 | } 38 | 39 | @end 40 | 41 | #pragma mark - 42 | 43 | @implementation LMSpringboardView 44 | 45 | - (void)setItemViews:(NSArray *)itemViews 46 | { 47 | if(_itemViews != itemViews) 48 | { 49 | for(UIView* view in _itemViews) 50 | if([view isDescendantOfView:self] == YES) 51 | [view removeFromSuperview]; 52 | 53 | _itemViews = [itemViews copy]; 54 | 55 | for(UIView* view in _itemViews) 56 | { 57 | [_contentView addSubview:view]; 58 | } 59 | 60 | [self LM_setContentSizeIsDirty]; 61 | } 62 | } 63 | 64 | - (void)setItemDiameter:(NSUInteger)itemDiameter 65 | { 66 | if(_itemDiameter != itemDiameter) 67 | { 68 | _itemDiameter = itemDiameter; 69 | [self LM_setContentSizeIsDirty]; 70 | } 71 | } 72 | 73 | - (void)setItemPadding:(NSUInteger)itemPadding 74 | { 75 | if(_itemPadding != itemPadding) 76 | { 77 | _itemPadding = itemPadding; 78 | [self LM_setContentSizeIsDirty]; 79 | } 80 | } 81 | 82 | - (void)setMinimumItemScaling:(double)minimumItemScaling 83 | { 84 | if(_minimumItemScaling != minimumItemScaling) 85 | { 86 | _minimumItemScaling = minimumItemScaling; 87 | [self setNeedsLayout]; 88 | } 89 | } 90 | 91 | - (void)showAllContentAnimated:(BOOL)animated 92 | { 93 | CGRect contentRectInContentSpace = [self LM_fullContentRectInContentSpace]; 94 | _lastFocusedViewIndex = [self LM_closestIndexToPointInContent:[self LM_rectCenter:contentRectInContentSpace]]; 95 | 96 | if(animated == YES) 97 | { 98 | [UIView animateWithDuration:0.5 delay:0 options:UIViewAnimationOptionLayoutSubviews|UIViewAnimationOptionAllowAnimatedContent|UIViewAnimationOptionBeginFromCurrentState|UIViewAnimationOptionCurveEaseInOut animations:^{ 99 | [self zoomToRect:contentRectInContentSpace animated:NO]; 100 | [self layoutIfNeeded]; 101 | } completion:nil]; 102 | } 103 | else 104 | [self zoomToRect:contentRectInContentSpace animated:NO]; 105 | } 106 | 107 | - (NSUInteger)indexOfItemClosestToPoint:(CGPoint)pointInSelf 108 | { 109 | return [self LM_closestIndexToPointInSelf:pointInSelf]; 110 | } 111 | 112 | - (void)centerOnIndex:(NSUInteger)index zoomScale:(CGFloat)zoomScale animated:(BOOL)animated 113 | { 114 | _lastFocusedViewIndex = index; 115 | UIView* view = [_itemViews objectAtIndex:index]; 116 | CGPoint centerContentSpace = view.center; 117 | 118 | if(zoomScale != self.zoomScale) 119 | { 120 | CGRect rectInContentSpace = [self LM_rectWithCenter:centerContentSpace size:view.bounds.size]; 121 | // this takes the rect in content space 122 | [self zoomToRect:rectInContentSpace animated:animated]; 123 | } 124 | else 125 | { 126 | CGSize sizeInSelfSpace = self.bounds.size; 127 | CGPoint centerInSelfSpace = [self LM_pointInContentToSelf:centerContentSpace]; 128 | CGRect rectInSelfSpace = [self LM_rectWithCenter:centerInSelfSpace size:sizeInSelfSpace]; 129 | // this takes the rect in self space 130 | [self scrollRectToVisible:rectInSelfSpace animated:animated]; 131 | } 132 | } 133 | 134 | - (void)doIntroAnimation 135 | { 136 | [self layoutIfNeeded]; 137 | 138 | CGSize size = self.bounds.size; 139 | NSUInteger i = 0; 140 | float minScale = 0.5; 141 | UIView* centerView = [_itemViews objectAtIndex:_lastFocusedViewIndex]; 142 | CGPoint centerViewCenter = centerView.center; 143 | for(UIView* view in _itemViews) 144 | { 145 | CGPoint viewCenter = view.center; 146 | view.alpha = 0; 147 | int dx = (viewCenter.x-centerViewCenter.x); 148 | int dy = (viewCenter.y-centerViewCenter.y); 149 | int distance = (dx*dx-dy*dy); 150 | float factor = MAX(MIN(MAX(size.width,size.height)/distance, 1), 0); 151 | float scaleFactor = ((factor)*0.8+0.2); 152 | float translateFactor = -0.9; 153 | // alt version 154 | //float scaleFactor = ((1-factor)); 155 | //float translateFactor = 5*(1-factor);//1-(factor*factor*factor); 156 | view.transform = CGAffineTransformScale( 157 | CGAffineTransformMakeTranslation(dx*translateFactor, dy*translateFactor), 158 | minScale*scaleFactor, minScale*scaleFactor); 159 | i++; 160 | } 161 | 162 | [self setNeedsLayout]; 163 | [UIView animateWithDuration:0.5 delay:0 options:UIViewAnimationOptionCurveEaseOut animations:^{ 164 | for(UIView* view in _itemViews) 165 | { 166 | view.alpha = 1; 167 | } 168 | [self layoutSubviews]; 169 | } completion:nil]; 170 | } 171 | 172 | #pragma mark - Input 173 | 174 | - (void)LM_didZoomGesture:(UITapGestureRecognizer*)sender 175 | { 176 | CGFloat maximumZoom = 1; 177 | 178 | CGPoint positionInSelf = [sender locationInView:self]; 179 | NSUInteger targetIndex = [self LM_closestIndexToPointInSelf:positionInSelf]; 180 | 181 | if(self.zoomScale >= _minimumZoomLevelToLaunchApp && self.zoomScale != self.minimumZoomScale) 182 | { 183 | // we should zoom out 184 | [self showAllContentAnimated:YES]; 185 | } 186 | else 187 | { 188 | // we missed a tap or we're too small, so zoom in 189 | [UIView animateWithDuration:0.5 animations:^{ 190 | [self centerOnIndex:targetIndex zoomScale:maximumZoom animated:NO]; 191 | [self layoutIfNeeded]; 192 | } completion:nil]; 193 | } 194 | } 195 | 196 | #pragma mark - Privates 197 | 198 | - (void)LM_initBase 199 | { 200 | NSLog(@"initbase"); 201 | self.delaysContentTouches = NO; 202 | self.showsHorizontalScrollIndicator = NO; 203 | self.showsVerticalScrollIndicator = NO; 204 | self.alwaysBounceHorizontal = YES; 205 | self.alwaysBounceVertical = YES; 206 | self.bouncesZoom = YES; 207 | self.decelerationRate = UIScrollViewDecelerationRateFast; 208 | self.delegate = self; 209 | 210 | self.itemDiameter = 68; 211 | self.itemPadding = 48; 212 | self.minimumItemScaling = 0.5; 213 | 214 | _transformFactor = 1; 215 | _zoomScaleCache = self.zoomScale; 216 | _minimumZoomLevelToLaunchApp = 0.4; 217 | 218 | _touchView = [[UIView alloc] init]; 219 | //_touchView.backgroundColor = [UIColor purpleColor]; 220 | [self addSubview:_touchView]; 221 | 222 | _contentView = [[UIView alloc] init]; 223 | //_contentView.backgroundColor = [UIColor greenColor]; 224 | [self addSubview:_contentView]; 225 | 226 | /*_debugRectInContent = [[UIView alloc] init]; 227 | _debugRectInContent.backgroundColor = [UIColor redColor]; 228 | _debugRectInContent.alpha = 0.4; 229 | [_contentView addSubview:_debugRectInContent]; 230 | _debugRectInScroll = [[UIView alloc] init]; 231 | _debugRectInScroll.backgroundColor = [UIColor blueColor]; 232 | _debugRectInScroll.alpha= 0.4; 233 | [self addSubview:_debugRectInScroll];*/ 234 | 235 | _doubleTapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(LM_didZoomGesture:)]; 236 | _doubleTapGesture.numberOfTapsRequired = 1; 237 | [_contentView addGestureRecognizer:_doubleTapGesture]; 238 | } 239 | 240 | - (CGPoint)LM_pointInSelfToContent:(CGPoint)point 241 | { 242 | CGFloat zoomScale = self.zoomScale; 243 | return CGPointMake(point.x/zoomScale, 244 | point.y/zoomScale); 245 | } 246 | 247 | - (CGPoint)LM_pointInContentToSelf:(CGPoint)point 248 | { 249 | CGFloat zoomScale = self.zoomScale; 250 | return CGPointMake(point.x*zoomScale, 251 | point.y*zoomScale); 252 | } 253 | 254 | - (CGSize)LM_sizeInSelfToContent:(CGSize)size 255 | { 256 | CGFloat zoomScale = self.zoomScale; 257 | return CGSizeMake(size.width/zoomScale, 258 | size.height/zoomScale); 259 | } 260 | 261 | - (CGSize)LM_sizeInContentToSelf:(CGSize)size 262 | { 263 | CGFloat zoomScale = self.zoomScale; 264 | return CGSizeMake(size.width*zoomScale, 265 | size.height*zoomScale); 266 | } 267 | 268 | - (CGPoint)LM_rectCenter:(CGRect)rect 269 | { 270 | return CGPointMake(rect.origin.x+rect.size.width*0.5, rect.origin.y+rect.size.height*0.5); 271 | } 272 | 273 | - (CGRect)LM_rectWithCenter:(CGPoint)center size:(CGSize)size 274 | { 275 | return CGRectMake(center.x-size.width*0.5, center.y-size.height*0.5, size.width, size.height); 276 | } 277 | 278 | - (void)LM_transformView:(LMSpringboardItemView*)view 279 | { 280 | // TODO: refactor to make functions use converter and helper functions 281 | CGSize size = self.bounds.size; 282 | CGFloat zoomScale = _zoomScaleCache; 283 | UIEdgeInsets insets = self.contentInset; 284 | 285 | CGPoint center = view.center; 286 | CGRect frame = [self convertRect:CGRectMake(view.center.x-_itemDiameter/2, view.center.y-_itemDiameter/2, _itemDiameter, _itemDiameter) fromView:view.superview]; 287 | CGPoint contentOffset = self.contentOffset; 288 | frame.origin.x -= contentOffset.x; 289 | frame.origin.y -= contentOffset.y; 290 | center = CGPointMake(frame.origin.x+frame.size.width/2, frame.origin.y+frame.size.height/2); 291 | NSUInteger padding = _itemPadding*zoomScale*0.4; 292 | double distanceToBorder = size.width; 293 | float xOffset = 0; 294 | float yOffset = 0; 295 | 296 | double distanceToBeOffset = _itemDiameter*zoomScale*(MIN(size.width, size.height)/320); 297 | //double distanceToBeOffset = MIN(size.width, size.height)*0.5*zoomScale; 298 | float leftDistance = center.x-padding-insets.left; 299 | if(leftDistance < distanceToBeOffset) 300 | { 301 | if(leftDistance < distanceToBorder) 302 | distanceToBorder = leftDistance; 303 | xOffset = 1-leftDistance/distanceToBeOffset; 304 | } 305 | float topDistance = center.y-padding-insets.top; 306 | if(topDistance < distanceToBeOffset) 307 | { 308 | if(topDistance < distanceToBorder) 309 | distanceToBorder = topDistance; 310 | yOffset = 1-topDistance/distanceToBeOffset; 311 | } 312 | float rightDistance = size.width-padding-center.x-insets.right; 313 | if(rightDistance < distanceToBeOffset) 314 | { 315 | if(rightDistance < distanceToBorder) 316 | distanceToBorder = rightDistance; 317 | xOffset = -(1-rightDistance/distanceToBeOffset); 318 | } 319 | float bottomDistance = size.height-padding-center.y-insets.bottom; 320 | if(bottomDistance < distanceToBeOffset) 321 | { 322 | if(bottomDistance < distanceToBorder) 323 | distanceToBorder = bottomDistance; 324 | yOffset = -(1-bottomDistance/distanceToBeOffset); 325 | } 326 | 327 | distanceToBorder *= 2; 328 | double usedScale; 329 | if(distanceToBorder < distanceToBeOffset*2) 330 | { 331 | if(distanceToBorder < -(NSInteger)_itemDiameter*2.5) 332 | { 333 | view.transform = _minTransform; 334 | usedScale = _minimumItemScaling*zoomScale; 335 | } 336 | else 337 | { 338 | double rawScale = MAX(distanceToBorder/(distanceToBeOffset*2), 0); 339 | rawScale = MIN(rawScale, 1); 340 | rawScale = 1-pow(1-rawScale, 2); 341 | double scale = rawScale*(1-_minimumItemScaling)+_minimumItemScaling; 342 | 343 | xOffset = frame.size.width*0.8*(1-rawScale)*xOffset; 344 | yOffset = frame.size.width*0.5*(1-rawScale)*yOffset; 345 | 346 | float translationModifier = MIN(distanceToBorder/_itemDiameter+2.5, 1); 347 | 348 | scale = MAX(MIN(scale*_transformFactor+(1-_transformFactor), 1), 0); 349 | translationModifier = MIN(translationModifier*_transformFactor, 1); 350 | view.transform = CGAffineTransformTranslate(CGAffineTransformMakeScale(scale, scale), xOffset*translationModifier, yOffset*translationModifier); 351 | 352 | usedScale = scale*zoomScale; 353 | } 354 | } 355 | else 356 | { 357 | view.transform = CGAffineTransformIdentity; 358 | usedScale = zoomScale; 359 | } 360 | if(self.isDragging == YES || self.isZooming == YES) 361 | [view setScale:usedScale animated:YES]; 362 | else 363 | view.scale = usedScale; 364 | } 365 | 366 | - (void)LM_setContentSizeIsDirty 367 | { 368 | _contentSizeIsDirty = YES; 369 | [self LM_setMinimumZoomLevelIsDirty]; 370 | } 371 | 372 | - (void)LM_setMinimumZoomLevelIsDirty 373 | { 374 | _minimumZoomLevelIsDirty = YES; 375 | _contentSizeIsDirty = YES; 376 | [self setNeedsLayout]; 377 | } 378 | 379 | - (NSUInteger)LM_closestIndexToPointInSelf:(CGPoint)pointInSelf 380 | { 381 | CGPoint pointInContent = [self LM_pointInSelfToContent:pointInSelf]; 382 | return [self LM_closestIndexToPointInContent:pointInContent]; 383 | } 384 | 385 | - (NSUInteger)LM_closestIndexToPointInContent:(CGPoint)pointInContent 386 | { 387 | BOOL hasItem = NO; 388 | double distance = 0; 389 | NSUInteger index = _lastFocusedViewIndex; 390 | NSUInteger i = 0; 391 | for(UIView* potentialView in _itemViews) 392 | { 393 | CGPoint center = potentialView.center; 394 | double potentialDistance = LMPointDistance(center.x, center.y, pointInContent.x, pointInContent.y); 395 | 396 | if(potentialDistance < distance || hasItem == NO) 397 | { 398 | hasItem = YES; 399 | distance = potentialDistance; 400 | index = i; 401 | } 402 | i++; 403 | } 404 | return index; 405 | } 406 | 407 | - (void)LM_centerOnClosestToScreenCenterAnimated:(BOOL)animated 408 | { 409 | CGSize sizeInSelf = self.bounds.size; 410 | CGPoint centerInSelf = CGPointMake(sizeInSelf.width*0.5, sizeInSelf.height*0.5); 411 | NSUInteger closestIndex = [self LM_closestIndexToPointInSelf:centerInSelf]; 412 | [self centerOnIndex:closestIndex zoomScale:self.zoomScale animated:animated]; 413 | } 414 | 415 | - (CGRect)LM_fullContentRectInContentSpace 416 | { 417 | CGRect rect = CGRectMake(_contentSizeExtra.width*0.5, 418 | _contentSizeExtra.height*0.5, 419 | _contentSizeUnscaled.width-_contentSizeExtra.width, 420 | _contentSizeUnscaled.height-_contentSizeExtra.height); 421 | //_debugRectInContent.frame = rect; 422 | return rect; 423 | } 424 | 425 | - (CGRect)LM_insetRectInSelf 426 | { 427 | UIEdgeInsets insets = self.contentInset; 428 | CGSize size = self.bounds.size; 429 | return CGRectMake(insets.left, insets.top, size.width-insets.left-insets.right, size.height-insets.top-insets.bottom); 430 | } 431 | 432 | - (void)LM_centerViewIfSmaller 433 | { 434 | /*CGRect frameToCenter = _contentView.frame; 435 | 436 | CGRect rect = [self LM_insetRect]; 437 | // center horizontally 438 | if (frameToCenter.size.width < rect.size.width) 439 | frameToCenter.origin.x = (rect.size.width - frameToCenter.size.width) / 2; 440 | else 441 | frameToCenter.origin.x = 0; 442 | 443 | // center vertically 444 | if (frameToCenter.size.height < rect.size.height) 445 | frameToCenter.origin.y = (rect.size.height - frameToCenter.size.height) / 2; 446 | else 447 | frameToCenter.origin.y = 0; 448 | 449 | _contentView.frame = frameToCenter;*/ 450 | } 451 | 452 | #pragma mark UIScrollViewDelegate 453 | 454 | - (void)scrollViewWillEndDragging:(UIScrollView *)scrollView 455 | withVelocity:(CGPoint)velocity 456 | targetContentOffset:(inout CGPoint *)targetContentOffset 457 | { 458 | // TODO: refactor to make functions use converter and helper functions 459 | CGSize size = self.bounds.size; 460 | CGFloat zoomScale = self.zoomScale; 461 | 462 | // targetContentOffset is in coordinates relative to self; 463 | 464 | // putting proposedTargetCenter in coordinates relative to _contentView 465 | CGPoint proposedTargetCenter = CGPointMake(targetContentOffset->x+size.width/2, targetContentOffset->y+size.height/2); 466 | proposedTargetCenter.x /= zoomScale; 467 | proposedTargetCenter.y /= zoomScale; 468 | //_debugRectInContent.frame = CGRectMake(proposedTargetCenter.x-40, proposedTargetCenter.y-40, 80, 80); 469 | 470 | // finding out the idealTargetCenter in coordinates relative to _contentView 471 | _lastFocusedViewIndex = [self LM_closestIndexToPointInContent:proposedTargetCenter]; 472 | UIView* view = [_itemViews objectAtIndex:_lastFocusedViewIndex]; 473 | CGPoint idealTargetCenter = view.center; 474 | //_debugRectInContent.frame = CGRectMake(idealTargetCenter.x-40, idealTargetCenter.y-40, 80, 80); 475 | 476 | // finding out the idealTargetOffset in coordinates relative to _contentView 477 | CGPoint idealTargetOffset = CGPointMake(idealTargetCenter.x-size.width/2/zoomScale, idealTargetCenter.y-size.height/2/zoomScale); 478 | //_debugRectInContent.frame = CGRectMake(idealTargetOffset.x-40, idealTargetOffset.y-40, 80, 80); 479 | 480 | // finding out the correctedTargetOffset in coordinates relative to self 481 | CGPoint correctedTargetOffset = CGPointMake(idealTargetOffset.x*zoomScale, 482 | idealTargetOffset.y*zoomScale); 483 | //_debugRectInScroll.frame = CGRectMake(correctedTargetOffset.x-40, correctedTargetOffset.y-40, 80, 80); 484 | 485 | // finding out currentCenter in coordinates relative to _contentView; 486 | CGPoint currentCenter = CGPointMake(self.contentOffset.x+size.width/2, self.contentOffset.y+size.height/2); 487 | currentCenter.x /= zoomScale; 488 | currentCenter.y /= zoomScale; 489 | //_debugRectInContent.frame = CGRectMake(currentCenter.x-40, currentCenter.y-40, 80, 80); 490 | 491 | // finding out the frame of actual icons in relation to _contentView 492 | CGPoint contentCenter = _contentView.center; 493 | contentCenter.x /= zoomScale; 494 | contentCenter.y /= zoomScale; 495 | CGSize contentSizeNoExtras = CGSizeMake(_contentSizeUnscaled.width-_contentSizeExtra.width, _contentSizeUnscaled.height-_contentSizeExtra.height); 496 | CGRect contentFrame = CGRectMake(contentCenter.x-contentSizeNoExtras.width*0.5, contentCenter.y-contentSizeNoExtras.height*0.5, contentSizeNoExtras.width, contentSizeNoExtras.height); 497 | //_debugRectInContent.frame = contentFrame; 498 | 499 | if(CGRectContainsPoint(contentFrame, proposedTargetCenter) == NO) 500 | { 501 | // we're going to end outside 502 | if(CGRectContainsPoint(contentFrame, currentCenter) == NO) 503 | { 504 | // we're already outside. stop roll and snap back on end drag 505 | *targetContentOffset = self.contentOffset; 506 | _centerOnEndDrag = YES; 507 | return; 508 | } 509 | else 510 | { 511 | // we're still in, ending out. Wait for the animation to end, THEN snap back. 512 | float ourPriority = 0.8; 513 | *targetContentOffset = CGPointMake( 514 | targetContentOffset->x*(1-ourPriority)+correctedTargetOffset.x*ourPriority, 515 | targetContentOffset->y*(1-ourPriority)+correctedTargetOffset.y*ourPriority); 516 | _centerOnEndDeccel = YES; 517 | return; 518 | } 519 | } 520 | // we're going to end in. snap to closest icon 521 | *targetContentOffset = correctedTargetOffset; 522 | } 523 | 524 | - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate 525 | { 526 | if(_centerOnEndDrag == YES) 527 | { 528 | _centerOnEndDrag = NO; 529 | [self centerOnIndex:_lastFocusedViewIndex zoomScale:self.zoomScale animated:YES]; 530 | } 531 | } 532 | 533 | - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView 534 | { 535 | if(_centerOnEndDeccel == YES) 536 | { 537 | _centerOnEndDeccel = NO; 538 | [self centerOnIndex:_lastFocusedViewIndex zoomScale:self.zoomScale animated:YES]; 539 | } 540 | } 541 | 542 | - (UIView*)viewForZoomingInScrollView:(UIScrollView *)scrollView 543 | { 544 | return _contentView; 545 | } 546 | 547 | - (void)scrollViewDidZoom:(UIScrollView *)scrollView 548 | { 549 | _zoomScaleCache = self.zoomScale; 550 | [self LM_centerViewIfSmaller]; 551 | } 552 | 553 | #pragma mark - UIView 554 | 555 | - (void)layoutSubviews 556 | { 557 | [super layoutSubviews]; 558 | 559 | CGSize size = self.bounds.size; 560 | 561 | UIEdgeInsets insets = self.contentInset; 562 | 563 | NSUInteger itemsPerLine = ceil(MIN(size.width,size.height)/MAX(size.width,size.height)*sqrt([_itemViews count])); 564 | if(itemsPerLine % 2 == 0) 565 | itemsPerLine++; 566 | NSUInteger lines = ceil([_itemViews count]/(double)itemsPerLine); 567 | CGFloat newMinimumZoomScale = 0; 568 | if(_contentSizeIsDirty == YES) 569 | { 570 | NSUInteger padding = _itemPadding; 571 | _contentSizeUnscaled = CGSizeMake(itemsPerLine*_itemDiameter+(itemsPerLine+1)*padding+(_itemDiameter+padding)/2, 572 | lines*_itemDiameter+(2)*padding); 573 | newMinimumZoomScale = MIN((size.width-insets.left-insets.right)/_contentSizeUnscaled.width, (size.height-insets.top-insets.bottom)/_contentSizeUnscaled.height); 574 | 575 | _contentSizeExtra = CGSizeMake((size.width-_itemDiameter*0.5)/newMinimumZoomScale, (size.height-_itemDiameter*0.5)/newMinimumZoomScale); 576 | 577 | _contentSizeUnscaled.width += _contentSizeExtra.width; 578 | _contentSizeUnscaled.height += _contentSizeExtra.height; 579 | _contentView.bounds = CGRectMake(0, 0, _contentSizeUnscaled.width, _contentSizeUnscaled.height); 580 | } 581 | if(_minimumZoomLevelIsDirty == YES) 582 | { 583 | self.minimumZoomScale = newMinimumZoomScale; 584 | CGFloat newZoom = MAX(self.zoomScale, newMinimumZoomScale); 585 | if(newZoom != _zoomScaleCache || YES) 586 | { 587 | self.zoomScale = newZoom; 588 | _zoomScaleCache = newZoom; 589 | 590 | _contentView.center = CGPointMake(_contentSizeUnscaled.width*0.5*newZoom, _contentSizeUnscaled.height*0.5*newZoom); 591 | self.contentSize = CGSizeMake(_contentSizeUnscaled.width*newZoom, _contentSizeUnscaled.height*newZoom); 592 | } 593 | } 594 | if(_contentSizeIsDirty == YES) 595 | { 596 | NSInteger i = 0; 597 | for(UIView* view in _itemViews) 598 | { 599 | view.bounds = CGRectMake(0, 0, _itemDiameter, _itemDiameter); 600 | 601 | NSInteger posX,posY; 602 | 603 | NSUInteger line = i/itemsPerLine; 604 | NSUInteger indexInLine = i%itemsPerLine; 605 | if(i == 0) 606 | { 607 | // place item 0 at the center of the grid 608 | line = [_itemViews count]/itemsPerLine/2; 609 | indexInLine = itemsPerLine/2; 610 | } 611 | else 612 | { 613 | // switch item at center of grid to position 0 614 | if(line == [_itemViews count]/itemsPerLine/2 615 | && indexInLine == itemsPerLine/2) 616 | { 617 | line = 0; 618 | indexInLine = 0; 619 | } 620 | } 621 | 622 | NSUInteger lineOffset = 0; 623 | if(line%2 == 1) 624 | lineOffset = (_itemDiameter+_itemPadding)/2; 625 | posX = _contentSizeExtra.width*0.5+_itemPadding+lineOffset+indexInLine*(_itemDiameter+_itemPadding)+_itemDiameter/2, 626 | posY = _contentSizeExtra.height*0.5+_itemPadding+line*(_itemDiameter)+_itemDiameter/2; 627 | view.center = CGPointMake(posX, posY); 628 | 629 | i++; 630 | } 631 | 632 | _contentSizeIsDirty = NO; 633 | } 634 | if(_minimumZoomLevelIsDirty == YES) 635 | { 636 | if(_lastFocusedViewIndex <= [_itemViews count]) 637 | { 638 | [self centerOnIndex:_lastFocusedViewIndex zoomScale:_zoomScaleCache animated:NO]; 639 | } 640 | 641 | _minimumZoomLevelIsDirty = NO; 642 | } 643 | 644 | _zoomScaleCache = self.zoomScale; 645 | 646 | _touchView.bounds = CGRectMake(0, 0, (_contentSizeUnscaled.width-_contentSizeExtra.width)*_zoomScaleCache, (_contentSizeUnscaled.height-_contentSizeExtra.height)*_zoomScaleCache); 647 | _touchView.center = CGPointMake(_contentSizeUnscaled.width*0.5*_zoomScaleCache, _contentSizeUnscaled.height*0.5*_zoomScaleCache); 648 | 649 | [self LM_centerViewIfSmaller]; 650 | 651 | double scale = MIN(_minimumItemScaling*_transformFactor+(1-_transformFactor), 1); 652 | _minTransform = CGAffineTransformMakeScale(scale, scale); 653 | for(LMSpringboardItemView* view in _itemViews) 654 | { 655 | [self LM_transformView:view]; 656 | } 657 | } 658 | 659 | - (void)setBounds:(CGRect)bounds 660 | { 661 | if(CGSizeEqualToSize(bounds.size, self.bounds.size) == NO) 662 | [self LM_setMinimumZoomLevelIsDirty]; 663 | [super setBounds:bounds]; 664 | } 665 | 666 | - (void)setFrame:(CGRect)frame 667 | { 668 | if(CGSizeEqualToSize(frame.size, self.bounds.size) == NO) 669 | [self LM_setMinimumZoomLevelIsDirty]; 670 | [super setFrame:frame]; 671 | } 672 | 673 | - (instancetype)initWithCoder:(NSCoder *)aDecoder 674 | { 675 | self = [super initWithCoder:aDecoder]; 676 | if(self) 677 | { 678 | [self LM_initBase]; 679 | } 680 | return self; 681 | } 682 | 683 | - (instancetype)initWithFrame:(CGRect)frame 684 | { 685 | self = [super initWithFrame:frame]; 686 | if(self) 687 | { 688 | [self LM_initBase]; 689 | } 690 | return self; 691 | } 692 | 693 | - (instancetype)init 694 | { 695 | self = [super init]; 696 | if(self) 697 | { 698 | [self LM_initBase]; 699 | } 700 | return self; 701 | } 702 | 703 | @end 704 | -------------------------------------------------------------------------------- /WatchSpringboard/LMViewControllerView.h: -------------------------------------------------------------------------------- 1 | // 2 | // LMViewControllerView.h 3 | // WatchSpringboard 4 | // 5 | // Created by Lucas Menge on 10/24/14. 6 | // Copyright (c) 2014 Lucas Menge. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class LMSpringboardItemView; 12 | @class LMSpringboardView; 13 | 14 | @interface LMViewControllerView : UIView 15 | 16 | @property (readonly) LMSpringboardView* springboard; 17 | @property (readonly) UIView* appView; 18 | @property (readonly) UIButton* respringButton; 19 | @property (readonly) BOOL isAppLaunched; 20 | 21 | - (void)launchAppItem:(LMSpringboardItemView*)item; 22 | - (void)quitApp; 23 | 24 | @end 25 | -------------------------------------------------------------------------------- /WatchSpringboard/LMViewControllerView.m: -------------------------------------------------------------------------------- 1 | // 2 | // LMViewControllerView.m 3 | // WatchSpringboard 4 | // 5 | // Created by Lucas Menge on 10/24/14. 6 | // Copyright (c) 2014 Lucas Menge. All rights reserved. 7 | // 8 | 9 | #import "LMViewControllerView.h" 10 | 11 | #import "LMSpringboardItemView.h" 12 | #import "LMSpringboardView.h" 13 | 14 | #import "LMAppController.h" 15 | 16 | @interface LMViewControllerView () 17 | { 18 | __strong UIImageView* _appLaunchMaskView; 19 | LMSpringboardItemView* _lastLaunchedItem; 20 | } 21 | 22 | @end 23 | 24 | @implementation LMViewControllerView 25 | 26 | - (void)launchAppItem:(LMSpringboardItemView*)item 27 | { 28 | if(_isAppLaunched == NO) 29 | { 30 | _isAppLaunched = YES; 31 | 32 | _lastLaunchedItem = item; 33 | 34 | CGPoint pointInSelf = [self convertPoint:item.icon.center fromView:item]; 35 | CGFloat dx = pointInSelf.x-_appView.center.x; 36 | CGFloat dy = pointInSelf.y-_appView.center.y; 37 | 38 | double appScale = 60*item.scale/MIN(_appView.bounds.size.width,_appView.bounds.size.height); 39 | _appView.transform = CGAffineTransformScale(CGAffineTransformMakeTranslation(dx, dy), appScale,appScale); 40 | _appView.alpha = 1; 41 | _appView.maskView = _appLaunchMaskView; 42 | 43 | _appLaunchMaskView.transform = CGAffineTransformMakeScale(0.01, 0.01); 44 | 45 | double springboardScale = MIN(self.bounds.size.width,self.bounds.size.height)/(60*item.scale); 46 | 47 | double maskScale = MAX(self.bounds.size.width,self.bounds.size.height)/(60*item.scale)*1.2*item.scale; 48 | 49 | [UIView animateWithDuration:0.5 animations:^{ 50 | _appView.transform = CGAffineTransformIdentity; 51 | _appView.alpha = 1; 52 | 53 | _appLaunchMaskView.transform = CGAffineTransformMakeScale(maskScale,maskScale); 54 | 55 | _springboard.transform = CGAffineTransformTranslate(CGAffineTransformMakeScale(springboardScale,springboardScale), -dx, -dy); 56 | _springboard.alpha = 0; 57 | } completion:^(BOOL finished) { 58 | _appView.maskView = nil; 59 | _appLaunchMaskView.transform = CGAffineTransformIdentity; 60 | 61 | _springboard.transform = CGAffineTransformIdentity; 62 | _springboard.alpha = 1; 63 | NSUInteger index = [_springboard indexOfItemClosestToPoint:[_springboard convertPoint:pointInSelf fromView:self]]; 64 | [_springboard centerOnIndex:index zoomScale:_springboard.zoomScale animated:NO]; 65 | 66 | 67 | [[LMAppController sharedInstance] openAppWithBundleIdentifier:item.bundleIdentifier]; 68 | }]; 69 | } 70 | } 71 | 72 | - (void)quitApp 73 | { 74 | if(_isAppLaunched == YES) 75 | { 76 | _isAppLaunched = NO; 77 | 78 | CGPoint pointInSelf = [self convertPoint:_lastLaunchedItem.icon.center fromView:_lastLaunchedItem]; 79 | CGFloat dx = pointInSelf.x-_appView.center.x; 80 | CGFloat dy = pointInSelf.y-_appView.center.y; 81 | 82 | double appScale = 60*_lastLaunchedItem.scale/MIN(_appView.bounds.size.width,_appView.bounds.size.height); 83 | CGAffineTransform appTransform = CGAffineTransformScale(CGAffineTransformMakeTranslation(dx, dy), appScale, appScale); 84 | _appView.maskView = _appLaunchMaskView; 85 | 86 | double springboardScale = MIN(self.bounds.size.width,self.bounds.size.height)/(60*_lastLaunchedItem.scale); 87 | _springboard.transform = CGAffineTransformTranslate(CGAffineTransformMakeScale(springboardScale,springboardScale), -dx, -dy); 88 | _springboard.alpha = 0; 89 | 90 | double maskScale = MAX(self.bounds.size.width,self.bounds.size.height)/(60*_lastLaunchedItem.scale)*1.2*_lastLaunchedItem.scale; 91 | 92 | _appLaunchMaskView.transform = CGAffineTransformMakeScale(maskScale,maskScale); 93 | 94 | [UIView animateWithDuration:0.5 delay:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{ 95 | _appView.alpha = 1; 96 | _appView.transform = appTransform; 97 | 98 | _appLaunchMaskView.transform = CGAffineTransformMakeScale(0.01, 0.01); 99 | 100 | _springboard.alpha = 1; 101 | _springboard.transform = CGAffineTransformIdentity; 102 | } completion:^(BOOL finished) { 103 | _appView.alpha = 0; 104 | _appView.maskView = nil; 105 | }]; 106 | 107 | _lastLaunchedItem = nil; 108 | } 109 | } 110 | 111 | #pragma mark - UIView 112 | 113 | - (instancetype)initWithCoder:(NSCoder *)aDecoder 114 | { 115 | self = [super initWithCoder:aDecoder]; 116 | if(self) 117 | { 118 | CGRect fullFrame = CGRectMake(0, 0, self.frame.size.width, self.frame.size.height); 119 | UIViewAutoresizing mask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth; 120 | 121 | UIImageView* bg = [[UIImageView alloc] initWithFrame:fullFrame]; 122 | bg.image = [UIImage imageNamed:@"Wallpaper.png"]; 123 | bg.contentMode = UIViewContentModeScaleAspectFill; 124 | bg.autoresizingMask = mask; 125 | [self addSubview:bg]; 126 | 127 | _springboard = [[LMSpringboardView alloc] initWithFrame:fullFrame]; 128 | _springboard.autoresizingMask = mask; 129 | 130 | NSMutableArray* itemViews = [NSMutableArray array]; 131 | 132 | NSArray* apps = [LMAppController sharedInstance].installedApplications; 133 | 134 | // pre-render the known icons 135 | NSMutableArray* images = [NSMutableArray array]; 136 | UIBezierPath* clipPath = [UIBezierPath bezierPathWithOvalInRect:CGRectInset(CGRectMake(0, 0, 60, 60), 0.5,0.5)]; 137 | for(LMApp* app in apps) 138 | { 139 | UIImage* image = app.icon; 140 | 141 | UIGraphicsBeginImageContextWithOptions(CGSizeMake(60, 60), NO, [UIScreen mainScreen].scale); 142 | [clipPath addClip]; 143 | [image drawInRect:CGRectMake(0, 0, 60, 60)]; 144 | UIImage* renderedImage = UIGraphicsGetImageFromCurrentImageContext(); 145 | UIGraphicsEndImageContext(); 146 | 147 | [images addObject:renderedImage]; 148 | } 149 | 150 | // build out item set 151 | NSInteger index = 0; 152 | for(LMApp* app in apps) 153 | { 154 | LMSpringboardItemView* item = [[LMSpringboardItemView alloc] init]; 155 | item.bundleIdentifier = app.bundleIdentifier; 156 | [item setTitle:app.name]; 157 | item.icon.image = images[index++]; 158 | [itemViews addObject:item]; 159 | } 160 | _springboard.itemViews = itemViews; 161 | 162 | [self addSubview:_springboard]; 163 | 164 | _appView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"App.png"]]; 165 | _appView.transform = CGAffineTransformMakeScale(0, 0); 166 | _appView.alpha = 0; 167 | _appView.backgroundColor = [UIColor whiteColor]; 168 | [self addSubview:_appView]; 169 | 170 | _appLaunchMaskView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"Icon.png"]]; 171 | 172 | _respringButton = [UIButton buttonWithType:UIButtonTypeCustom]; 173 | [self addSubview:_respringButton]; 174 | } 175 | return self; 176 | } 177 | 178 | - (void)layoutSubviews 179 | { 180 | [super layoutSubviews]; 181 | 182 | CGRect statusFrame = {0}; 183 | if(self.window != nil) 184 | { 185 | CGRect statusFrame = [UIApplication sharedApplication].statusBarFrame; 186 | statusFrame = [self.window convertRect:statusFrame toView:self]; 187 | 188 | UIEdgeInsets insets = _springboard.contentInset; 189 | insets.top = statusFrame.size.height; 190 | _springboard.contentInset = insets; 191 | } 192 | 193 | CGSize size = self.bounds.size; 194 | 195 | _appView.bounds = CGRectMake(0, 0, size.width, size.height); 196 | _appView.center = CGPointMake(size.width*0.5, size.height*0.5); 197 | 198 | _appLaunchMaskView.center =CGPointMake(size.width*0.5, size.height*0.5+statusFrame.size.height); 199 | 200 | _respringButton.bounds = CGRectMake(0, 0, 60, 60); 201 | _respringButton.center = CGPointMake(size.width*0.5, size.height-60*0.5); 202 | } 203 | 204 | @end 205 | -------------------------------------------------------------------------------- /WatchSpringboard/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // WatchSpringboard 4 | // 5 | // Created by Lucas Menge on 10/23/14. 6 | // Copyright (c) 2014 Lucas Menge. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | @end 14 | 15 | -------------------------------------------------------------------------------- /WatchSpringboard/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // WatchSpringboard 4 | // 5 | // Created by Lucas Menge on 10/23/14. 6 | // Copyright (c) 2014 Lucas Menge. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | 11 | #import "LMViewControllerView.h" 12 | #import "LMSpringboardItemView.h" 13 | #import "LMSpringboardView.h" 14 | 15 | @interface ViewController () 16 | 17 | @end 18 | 19 | @implementation ViewController 20 | 21 | #pragma mark - Privates 22 | 23 | - (LMViewControllerView*)customView 24 | { 25 | return (LMViewControllerView*)self.view; 26 | } 27 | 28 | - (LMSpringboardView*)springboard 29 | { 30 | return [(LMViewControllerView*)self.view springboard]; 31 | } 32 | 33 | #pragma mark Notifications 34 | 35 | - (void)LM_didBecomeActive 36 | { 37 | if([self customView].isAppLaunched == NO) 38 | { 39 | [[self springboard] centerOnIndex:0 zoomScale:1 animated:NO]; 40 | [[self springboard] doIntroAnimation]; 41 | [self springboard].alpha = 1; 42 | } 43 | } 44 | 45 | - (void)LM_didEnterBackground 46 | { 47 | if([self customView].isAppLaunched == NO) 48 | [self springboard].alpha = 0; 49 | } 50 | 51 | #pragma mark UIGestureRecognizerDelegate 52 | 53 | - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch 54 | { 55 | if([self springboard].zoomScale < [self springboard].minimumZoomLevelToLaunchApp) 56 | return NO; 57 | return YES; 58 | } 59 | 60 | #pragma mark - Input 61 | 62 | - (void)LM_respringTapped:(id)sender 63 | { 64 | if([self customView].isAppLaunched == YES) 65 | { 66 | [[self customView] quitApp]; 67 | [UIView animateWithDuration:0.3 animations:^{ 68 | [self setNeedsStatusBarAppearanceUpdate]; 69 | }]; 70 | } 71 | else 72 | { 73 | LMSpringboardView* springboard = [self springboard]; 74 | [UIView animateWithDuration:0.3 animations:^{ 75 | springboard.alpha = 0; 76 | } completion:^(BOOL finished) { 77 | [springboard doIntroAnimation]; 78 | springboard.alpha = 1; 79 | }]; 80 | } 81 | } 82 | 83 | - (void)LM_iconTapped:(UITapGestureRecognizer*)sender 84 | { 85 | UIView* item = sender.view; 86 | while(item != nil && [item isKindOfClass:[LMSpringboardItemView class]] == NO) 87 | item = item.superview; 88 | [[self customView] launchAppItem:(LMSpringboardItemView*)item]; 89 | [UIView animateWithDuration:0.5 animations:^{ 90 | [self setNeedsStatusBarAppearanceUpdate]; 91 | }]; 92 | } 93 | 94 | #pragma mark - UIViewController 95 | 96 | - (void)viewWillAppear:(BOOL)animated 97 | { 98 | [super viewWillAppear:animated]; 99 | 100 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(LM_didBecomeActive) name:UIApplicationDidBecomeActiveNotification object:nil]; 101 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(LM_didEnterBackground) name:UIApplicationDidEnterBackgroundNotification object:nil]; 102 | 103 | LMSpringboardView* springboard = [self springboard]; 104 | [springboard centerOnIndex:0 zoomScale:springboard.zoomScale animated:NO]; 105 | } 106 | 107 | - (void)viewWillDisappear:(BOOL)animated 108 | { 109 | [super viewWillDisappear:animated]; 110 | 111 | [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidBecomeActiveNotification object:nil]; 112 | [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidEnterBackgroundNotification object:nil]; 113 | } 114 | 115 | - (void)viewDidLoad 116 | { 117 | [super viewDidLoad]; 118 | 119 | [[self customView].respringButton addTarget:self action:@selector(LM_respringTapped:) forControlEvents:UIControlEventTouchUpInside]; 120 | [self springboard].alpha = 0; 121 | 122 | for(LMSpringboardItemView* item in [self springboard].itemViews) 123 | { 124 | UITapGestureRecognizer* tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(LM_iconTapped:)]; 125 | tap.numberOfTapsRequired = 1; 126 | tap.delegate = self; 127 | [item addGestureRecognizer:tap]; 128 | } 129 | } 130 | 131 | - (UIStatusBarStyle)preferredStatusBarStyle 132 | { 133 | if([self isViewLoaded] == YES && [self customView].isAppLaunched == YES) 134 | return UIStatusBarStyleDefault; 135 | else 136 | return UIStatusBarStyleLightContent; 137 | } 138 | 139 | @end 140 | -------------------------------------------------------------------------------- /WatchSpringboard/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // WatchSpringboard 4 | // 5 | // Created by Lucas Menge on 10/23/14. 6 | // Copyright (c) 2014 Lucas Menge. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /WatchSpringboardTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | com.lmmenge.$(PRODUCT_NAME:rfc1034identifier) 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 | -------------------------------------------------------------------------------- /WatchSpringboardTests/WatchSpringboardTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // WatchSpringboardTests.m 3 | // WatchSpringboardTests 4 | // 5 | // Created by Lucas Menge on 10/23/14. 6 | // Copyright (c) 2014 Lucas Menge. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface WatchSpringboardTests : XCTestCase 13 | 14 | @end 15 | 16 | @implementation WatchSpringboardTests 17 | 18 | - (void)setUp { 19 | [super setUp]; 20 | // Put setup code here. This method is called before the invocation of each test method in the class. 21 | } 22 | 23 | - (void)tearDown { 24 | // Put teardown code here. This method is called after the invocation of each test method in the class. 25 | [super tearDown]; 26 | } 27 | 28 | - (void)testExample { 29 | // This is an example of a functional test case. 30 | XCTAssert(YES, @"Pass"); 31 | } 32 | 33 | - (void)testPerformanceExample { 34 | // This is an example of a performance test case. 35 | [self measureBlock:^{ 36 | // Put the code you want to measure the time of here. 37 | }]; 38 | } 39 | 40 | @end 41 | --------------------------------------------------------------------------------