├── .gitignore ├── Example └── JBInterfaceController │ ├── JBInterfaceController WatchKit App │ ├── Base.lproj │ │ └── Interface.storyboard │ ├── Images.xcassets │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ └── Info.plist │ ├── JBInterfaceController WatchKit Extension │ ├── Images.xcassets │ │ └── README__ignoredByTemplate__ │ ├── Info.plist │ ├── InterfaceController.h │ ├── InterfaceController.m │ ├── JBModalInterfaceController.h │ └── JBModalInterfaceController.m │ ├── JBInterfaceController.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ └── contents.xcworkspacedata │ ├── JBInterfaceController │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Base.lproj │ │ ├── LaunchScreen.xib │ │ └── Main.storyboard │ ├── Images.xcassets │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── Info.plist │ ├── ViewController.h │ ├── ViewController.m │ └── main.m │ └── JBInterfaceControllerTests │ ├── Info.plist │ └── JBInterfaceControllerTests.m ├── JBInterfaceController.podspec ├── LICENSE ├── README.md └── Source ├── JBInterface.h ├── JBInterface.m ├── JBInterfaceController.h ├── JBInterfaceController.m ├── JBInterfaceController_Protected.h └── JBInterface_Protected.h /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | build/ 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | xcuserdata 13 | *.xccheckout 14 | *.moved-aside 15 | DerivedData 16 | *.hmap 17 | *.ipa 18 | *.xcuserstate 19 | 20 | # CocoaPods 21 | # 22 | # We recommend against adding the Pods directory to your .gitignore. However 23 | # you should judge for yourself, the pros and cons are mentioned at: 24 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 25 | # 26 | #Pods/ -------------------------------------------------------------------------------- /Example/JBInterfaceController/JBInterfaceController WatchKit App/Base.lproj/Interface.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 49 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /Example/JBInterfaceController/JBInterfaceController WatchKit App/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "24x24", 5 | "idiom" : "watch", 6 | "scale" : "2x", 7 | "role" : "notificationCenter", 8 | "subtype" : "38mm" 9 | }, 10 | { 11 | "size" : "27.5x27.5", 12 | "idiom" : "watch", 13 | "scale" : "2x", 14 | "role" : "notificationCenter", 15 | "subtype" : "42mm" 16 | }, 17 | { 18 | "size" : "29x29", 19 | "idiom" : "watch", 20 | "role" : "companionSettings", 21 | "scale" : "2x" 22 | }, 23 | { 24 | "size" : "29x29", 25 | "idiom" : "watch", 26 | "role" : "companionSettings", 27 | "scale" : "3x" 28 | }, 29 | { 30 | "size" : "40x40", 31 | "idiom" : "watch", 32 | "scale" : "2x", 33 | "role" : "appLauncher", 34 | }, 35 | { 36 | "size" : "86x86", 37 | "idiom" : "watch", 38 | "scale" : "2x", 39 | "role" : "quickLook", 40 | "subtype" : "38mm" 41 | }, 42 | { 43 | "size" : "98x98", 44 | "idiom" : "watch", 45 | "scale" : "2x", 46 | "role" : "quickLook", 47 | "subtype" : "42mm" 48 | } 49 | ], 50 | "info" : { 51 | "version" : 1, 52 | "author" : "xcode" 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Example/JBInterfaceController/JBInterfaceController WatchKit App/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | JBInterfaceController 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | com.juicybitssoftware.JBInterfaceController.watchkitapp 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | UISupportedInterfaceOrientations 26 | 27 | UIInterfaceOrientationPortrait 28 | UIInterfaceOrientationPortraitUpsideDown 29 | 30 | WKCompanionAppBundleIdentifier 31 | com.juicybitssoftware.JBInterfaceController 32 | WKWatchKitApp 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /Example/JBInterfaceController/JBInterfaceController WatchKit Extension/Images.xcassets/README__ignoredByTemplate__: -------------------------------------------------------------------------------- 1 | Did you know that git does not support storing empty directories? 2 | -------------------------------------------------------------------------------- /Example/JBInterfaceController/JBInterfaceController WatchKit Extension/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | JBInterfaceController WatchKit Extension 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | com.juicybitssoftware.JBInterfaceController.watchkitextension 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | XPC! 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1.0 25 | NSExtension 26 | 27 | NSExtensionAttributes 28 | 29 | WKAppBundleIdentifier 30 | com.juicybitssoftware.JBInterfaceController.watchkitapp 31 | 32 | NSExtensionPointIdentifier 33 | com.apple.watchkit 34 | 35 | RemoteInterfacePrincipalClass 36 | InterfaceController 37 | 38 | 39 | -------------------------------------------------------------------------------- /Example/JBInterfaceController/JBInterfaceController WatchKit Extension/InterfaceController.h: -------------------------------------------------------------------------------- 1 | // 2 | // InterfaceController.h 3 | // JBInterfaceController WatchKit Extension 4 | // 5 | // Created by Michael Swanson on 4/2/15. 6 | // Copyright (c) 2015 Michael Swanson. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import "JBInterfaceController.h" 12 | 13 | @interface InterfaceController : JBInterfaceController 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /Example/JBInterfaceController/JBInterfaceController WatchKit Extension/InterfaceController.m: -------------------------------------------------------------------------------- 1 | // 2 | // InterfaceController.m 3 | // JBInterfaceController WatchKit Extension 4 | // 5 | // Created by Michael Swanson on 4/2/15. 6 | // Copyright (c) 2015 Michael Swanson. All rights reserved. 7 | // 8 | 9 | #import "InterfaceController.h" 10 | #import "JBModalInterfaceController.h" 11 | 12 | @interface InterfaceController() 13 | 14 | @property (nonatomic, readwrite, weak) IBOutlet WKInterfaceButton *modalButton; 15 | @property (nonatomic, readwrite, copy) NSString *selectedOption; 16 | 17 | @end 18 | 19 | @implementation InterfaceController 20 | 21 | - (void)awakeWithContext:(id)context { 22 | [super awakeWithContext:context]; 23 | 24 | // Configure interface objects here. 25 | } 26 | 27 | - (void)willActivate { 28 | // This method is called when watch view controller is about to be visible to user 29 | [super willActivate]; 30 | } 31 | 32 | - (void)didDeactivate { 33 | // This method is called when watch view controller is no longer visible 34 | [super didDeactivate]; 35 | } 36 | 37 | - (IBAction)doPresentModal { 38 | 39 | JBInterfaceControllerConfigureBlock configureBlock = ^(JBInterfaceController *controller) { 40 | 41 | // Configure the presented controller 42 | ((JBModalInterfaceController *)controller).delegate = self; 43 | }; 44 | 45 | NSArray *configurators = @[ 46 | [JBInterfaceControllerConfigurator 47 | configuratorWithName:@"modal" 48 | context:@"Force Touch, then cancel to see the replacement status bar appear" 49 | configureBlock:configureBlock 50 | dismissBlock:^(JBInterfaceController *controller) { 51 | 52 | NSLog(@"First controller was dismissed"); 53 | }], 54 | [JBInterfaceControllerConfigurator 55 | configuratorWithName:@"modal" 56 | context:@"Notice how the replacement status bar appears here too" 57 | configureBlock:configureBlock 58 | dismissBlock:^(JBInterfaceController *controller) { 59 | 60 | NSLog(@"Second controller was dismissed"); 61 | }] 62 | ]; 63 | 64 | [self presentControllersWithConfigurators:configurators]; 65 | } 66 | 67 | - (void)didUpdateInterface { 68 | 69 | if (self.selectedOption) { 70 | 71 | [self.modalButton setTitle:self.selectedOption]; 72 | } 73 | } 74 | 75 | #pragma mark - JBModalInterfaceControllerDelegate 76 | 77 | - (void)modalInterfaceController:(JBModalInterfaceController *)controller 78 | didSelectOption:(NSString *)option { 79 | 80 | NSLog(@"Selection option: %@", option); 81 | 82 | self.selectedOption = option; 83 | [self.interface setNeedsUpdate]; 84 | } 85 | 86 | @end 87 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /Example/JBInterfaceController/JBInterfaceController WatchKit Extension/JBModalInterfaceController.h: -------------------------------------------------------------------------------- 1 | // 2 | // JBModalInterfaceController.h 3 | // JBInterfaceController 4 | // 5 | // Created by Michael Swanson on 4/2/15. 6 | // Copyright (c) 2015 Michael Swanson. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import "JBInterfaceController.h" 12 | 13 | @protocol JBModalInterfaceControllerDelegate; 14 | 15 | @interface JBModalInterfaceController : JBInterfaceController 16 | 17 | @property (nonatomic, readwrite, weak) id delegate; 18 | 19 | @end 20 | 21 | @protocol JBModalInterfaceControllerDelegate 22 | 23 | @optional 24 | - (void)modalInterfaceController:(JBModalInterfaceController *)controller didSelectOption:(NSString *)option; 25 | 26 | @end -------------------------------------------------------------------------------- /Example/JBInterfaceController/JBInterfaceController WatchKit Extension/JBModalInterfaceController.m: -------------------------------------------------------------------------------- 1 | // 2 | // JBModalInterfaceController.m 3 | // JBInterfaceController 4 | // 5 | // Created by Michael Swanson on 4/2/15. 6 | // Copyright (c) 2015 Michael Swanson. All rights reserved. 7 | // 8 | 9 | #import "JBModalInterfaceController.h" 10 | #import "JBInterfaceController_Protected.h" 11 | 12 | @interface JBModalInterfaceController() 13 | 14 | @property (nonatomic, readwrite, weak) IBOutlet WKInterfaceLabel *label; 15 | @property (nonatomic, readwrite, copy) NSString *message; 16 | @property (nonatomic, readwrite, weak) IBOutlet WKInterfaceGroup *replacementStatusBarGroup; 17 | @property (nonatomic, readwrite, assign) BOOL fixMenu; 18 | 19 | @end 20 | 21 | @implementation JBModalInterfaceController 22 | 23 | - (void)awakeWithContext:(id)context { 24 | [super awakeWithContext:context]; 25 | 26 | // Configure interface objects here. 27 | 28 | // Make sure we unwrap our context 29 | self.message = [self unwrappedContext:context]; 30 | 31 | [self addMenuItemWithItemIcon:WKMenuItemIconAdd 32 | title:@"Add" 33 | action:@selector(didSelectAdd)]; 34 | 35 | [self addMenuItemWithItemIcon:WKMenuItemIconTrash 36 | title:@"Delete" 37 | action:@selector(didSelectDelete)]; 38 | } 39 | 40 | - (void)willActivate { 41 | // This method is called when watch view controller is about to be visible to user 42 | [super willActivate]; 43 | 44 | [self.label setText:self.message]; 45 | } 46 | 47 | - (void)didDeactivate { 48 | // This method is called when watch view controller is no longer visible 49 | [super didDeactivate]; 50 | } 51 | 52 | - (IBAction)doClose { 53 | 54 | [self dismissController]; 55 | } 56 | 57 | - (void)didSelectAdd { 58 | 59 | [self didSelectOption:@"Add"]; 60 | } 61 | 62 | - (void)didSelectDelete { 63 | 64 | [self didSelectOption:@"Delete"]; 65 | } 66 | 67 | - (void)didSelectOption:(NSString *)option { 68 | 69 | // Inform delegate 70 | if (self.delegate && 71 | [self.delegate respondsToSelector:@selector(modalInterfaceController:didSelectOption:)]) { 72 | 73 | [self.delegate modalInterfaceController:self didSelectOption:option]; 74 | } 75 | } 76 | 77 | - (void)fixMenuBug { 78 | 79 | [super fixMenuBug]; 80 | 81 | self.fixMenu = YES; 82 | [self.interface setNeedsUpdate]; 83 | } 84 | 85 | - (void)didUpdateInterface { 86 | 87 | [super didUpdateInterface]; 88 | 89 | if (self.fixMenu) { 90 | 91 | [self.replacementStatusBarGroup setHidden:NO]; 92 | self.fixMenu = NO; 93 | } 94 | } 95 | 96 | 97 | @end 98 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /Example/JBInterfaceController/JBInterfaceController.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 0907FEDC1ACDF7AC00C37C2E /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 0907FEDB1ACDF7AC00C37C2E /* main.m */; }; 11 | 0907FEDF1ACDF7AC00C37C2E /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 0907FEDE1ACDF7AC00C37C2E /* AppDelegate.m */; }; 12 | 0907FEE21ACDF7AC00C37C2E /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 0907FEE11ACDF7AC00C37C2E /* ViewController.m */; }; 13 | 0907FEE51ACDF7AC00C37C2E /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0907FEE31ACDF7AC00C37C2E /* Main.storyboard */; }; 14 | 0907FEE71ACDF7AC00C37C2E /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0907FEE61ACDF7AC00C37C2E /* Images.xcassets */; }; 15 | 0907FEEA1ACDF7AC00C37C2E /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 0907FEE81ACDF7AC00C37C2E /* LaunchScreen.xib */; }; 16 | 0907FEF61ACDF7AC00C37C2E /* JBInterfaceControllerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 0907FEF51ACDF7AC00C37C2E /* JBInterfaceControllerTests.m */; }; 17 | 0907FF091ACDF7EE00C37C2E /* InterfaceController.m in Sources */ = {isa = PBXBuildFile; fileRef = 0907FF081ACDF7EE00C37C2E /* InterfaceController.m */; }; 18 | 0907FF0B1ACDF7EE00C37C2E /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0907FF0A1ACDF7EE00C37C2E /* Images.xcassets */; }; 19 | 0907FF0F1ACDF7EE00C37C2E /* JBInterfaceController WatchKit App.app in Resources */ = {isa = PBXBuildFile; fileRef = 0907FF0E1ACDF7EE00C37C2E /* JBInterfaceController WatchKit App.app */; }; 20 | 0907FF171ACDF7EE00C37C2E /* Interface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0907FF151ACDF7EE00C37C2E /* Interface.storyboard */; }; 21 | 0907FF191ACDF7EE00C37C2E /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0907FF181ACDF7EE00C37C2E /* Images.xcassets */; }; 22 | 0907FF1C1ACDF7EE00C37C2E /* JBInterfaceController WatchKit Extension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 0907FF031ACDF7EE00C37C2E /* JBInterfaceController WatchKit Extension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 23 | 0988B4021ACDF9FB00A4EF18 /* JBInterface.m in Sources */ = {isa = PBXBuildFile; fileRef = 0988B3FE1ACDF9FB00A4EF18 /* JBInterface.m */; }; 24 | 0988B4031ACDF9FB00A4EF18 /* JBInterfaceController.m in Sources */ = {isa = PBXBuildFile; fileRef = 0988B4011ACDF9FB00A4EF18 /* JBInterfaceController.m */; }; 25 | 09EC1CB81ACE44BF00D3A29D /* JBModalInterfaceController.m in Sources */ = {isa = PBXBuildFile; fileRef = 09EC1CB71ACE44BF00D3A29D /* JBModalInterfaceController.m */; }; 26 | /* End PBXBuildFile section */ 27 | 28 | /* Begin PBXContainerItemProxy section */ 29 | 0907FEF01ACDF7AC00C37C2E /* PBXContainerItemProxy */ = { 30 | isa = PBXContainerItemProxy; 31 | containerPortal = 0907FECE1ACDF7AC00C37C2E /* Project object */; 32 | proxyType = 1; 33 | remoteGlobalIDString = 0907FED51ACDF7AC00C37C2E; 34 | remoteInfo = JBInterfaceController; 35 | }; 36 | 0907FF101ACDF7EE00C37C2E /* PBXContainerItemProxy */ = { 37 | isa = PBXContainerItemProxy; 38 | containerPortal = 0907FECE1ACDF7AC00C37C2E /* Project object */; 39 | proxyType = 1; 40 | remoteGlobalIDString = 0907FF0D1ACDF7EE00C37C2E; 41 | remoteInfo = "JBInterfaceController WatchKit App"; 42 | }; 43 | 0907FF1A1ACDF7EE00C37C2E /* PBXContainerItemProxy */ = { 44 | isa = PBXContainerItemProxy; 45 | containerPortal = 0907FECE1ACDF7AC00C37C2E /* Project object */; 46 | proxyType = 1; 47 | remoteGlobalIDString = 0907FF021ACDF7EE00C37C2E; 48 | remoteInfo = "JBInterfaceController WatchKit Extension"; 49 | }; 50 | /* End PBXContainerItemProxy section */ 51 | 52 | /* Begin PBXCopyFilesBuildPhase section */ 53 | 0907FF231ACDF7EE00C37C2E /* Embed App Extensions */ = { 54 | isa = PBXCopyFilesBuildPhase; 55 | buildActionMask = 2147483647; 56 | dstPath = ""; 57 | dstSubfolderSpec = 13; 58 | files = ( 59 | 0907FF1C1ACDF7EE00C37C2E /* JBInterfaceController WatchKit Extension.appex in Embed App Extensions */, 60 | ); 61 | name = "Embed App Extensions"; 62 | runOnlyForDeploymentPostprocessing = 0; 63 | }; 64 | /* End PBXCopyFilesBuildPhase section */ 65 | 66 | /* Begin PBXFileReference section */ 67 | 0907FED61ACDF7AC00C37C2E /* JBInterfaceController.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = JBInterfaceController.app; sourceTree = BUILT_PRODUCTS_DIR; }; 68 | 0907FEDA1ACDF7AC00C37C2E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 69 | 0907FEDB1ACDF7AC00C37C2E /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 70 | 0907FEDD1ACDF7AC00C37C2E /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 71 | 0907FEDE1ACDF7AC00C37C2E /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 72 | 0907FEE01ACDF7AC00C37C2E /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 73 | 0907FEE11ACDF7AC00C37C2E /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 74 | 0907FEE41ACDF7AC00C37C2E /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 75 | 0907FEE61ACDF7AC00C37C2E /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 76 | 0907FEE91ACDF7AC00C37C2E /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; 77 | 0907FEEF1ACDF7AC00C37C2E /* JBInterfaceControllerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = JBInterfaceControllerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 78 | 0907FEF41ACDF7AC00C37C2E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 79 | 0907FEF51ACDF7AC00C37C2E /* JBInterfaceControllerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = JBInterfaceControllerTests.m; sourceTree = ""; }; 80 | 0907FF031ACDF7EE00C37C2E /* JBInterfaceController WatchKit Extension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "JBInterfaceController WatchKit Extension.appex"; sourceTree = BUILT_PRODUCTS_DIR; }; 81 | 0907FF061ACDF7EE00C37C2E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 82 | 0907FF071ACDF7EE00C37C2E /* InterfaceController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = InterfaceController.h; sourceTree = ""; }; 83 | 0907FF081ACDF7EE00C37C2E /* InterfaceController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = InterfaceController.m; sourceTree = ""; }; 84 | 0907FF0A1ACDF7EE00C37C2E /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 85 | 0907FF0E1ACDF7EE00C37C2E /* JBInterfaceController WatchKit App.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "JBInterfaceController WatchKit App.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 86 | 0907FF141ACDF7EE00C37C2E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 87 | 0907FF161ACDF7EE00C37C2E /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Interface.storyboard; sourceTree = ""; }; 88 | 0907FF181ACDF7EE00C37C2E /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 89 | 0988B3FD1ACDF9FB00A4EF18 /* JBInterface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = JBInterface.h; path = ../../../Source/JBInterface.h; sourceTree = ""; }; 90 | 0988B3FE1ACDF9FB00A4EF18 /* JBInterface.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = JBInterface.m; path = ../../../Source/JBInterface.m; sourceTree = ""; }; 91 | 0988B3FF1ACDF9FB00A4EF18 /* JBInterfaceController_Protected.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = JBInterfaceController_Protected.h; path = ../../../Source/JBInterfaceController_Protected.h; sourceTree = ""; }; 92 | 0988B4001ACDF9FB00A4EF18 /* JBInterfaceController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = JBInterfaceController.h; path = ../../../Source/JBInterfaceController.h; sourceTree = ""; }; 93 | 0988B4011ACDF9FB00A4EF18 /* JBInterfaceController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = JBInterfaceController.m; path = ../../../Source/JBInterfaceController.m; sourceTree = ""; }; 94 | 0988B4041ACE03FC00A4EF18 /* JBInterface_Protected.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = JBInterface_Protected.h; path = ../../../Source/JBInterface_Protected.h; sourceTree = ""; }; 95 | 09EC1CB61ACE44BF00D3A29D /* JBModalInterfaceController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JBModalInterfaceController.h; sourceTree = ""; }; 96 | 09EC1CB71ACE44BF00D3A29D /* JBModalInterfaceController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JBModalInterfaceController.m; sourceTree = ""; }; 97 | /* End PBXFileReference section */ 98 | 99 | /* Begin PBXFrameworksBuildPhase section */ 100 | 0907FED31ACDF7AC00C37C2E /* Frameworks */ = { 101 | isa = PBXFrameworksBuildPhase; 102 | buildActionMask = 2147483647; 103 | files = ( 104 | ); 105 | runOnlyForDeploymentPostprocessing = 0; 106 | }; 107 | 0907FEEC1ACDF7AC00C37C2E /* Frameworks */ = { 108 | isa = PBXFrameworksBuildPhase; 109 | buildActionMask = 2147483647; 110 | files = ( 111 | ); 112 | runOnlyForDeploymentPostprocessing = 0; 113 | }; 114 | 0907FF001ACDF7EE00C37C2E /* Frameworks */ = { 115 | isa = PBXFrameworksBuildPhase; 116 | buildActionMask = 2147483647; 117 | files = ( 118 | ); 119 | runOnlyForDeploymentPostprocessing = 0; 120 | }; 121 | /* End PBXFrameworksBuildPhase section */ 122 | 123 | /* Begin PBXGroup section */ 124 | 0907FECD1ACDF7AC00C37C2E = { 125 | isa = PBXGroup; 126 | children = ( 127 | 0907FED81ACDF7AC00C37C2E /* JBInterfaceController */, 128 | 0907FEF21ACDF7AC00C37C2E /* JBInterfaceControllerTests */, 129 | 0907FF041ACDF7EE00C37C2E /* JBInterfaceController WatchKit Extension */, 130 | 0907FF121ACDF7EE00C37C2E /* JBInterfaceController WatchKit App */, 131 | 0907FED71ACDF7AC00C37C2E /* Products */, 132 | ); 133 | sourceTree = ""; 134 | }; 135 | 0907FED71ACDF7AC00C37C2E /* Products */ = { 136 | isa = PBXGroup; 137 | children = ( 138 | 0907FED61ACDF7AC00C37C2E /* JBInterfaceController.app */, 139 | 0907FEEF1ACDF7AC00C37C2E /* JBInterfaceControllerTests.xctest */, 140 | 0907FF031ACDF7EE00C37C2E /* JBInterfaceController WatchKit Extension.appex */, 141 | 0907FF0E1ACDF7EE00C37C2E /* JBInterfaceController WatchKit App.app */, 142 | ); 143 | name = Products; 144 | sourceTree = ""; 145 | }; 146 | 0907FED81ACDF7AC00C37C2E /* JBInterfaceController */ = { 147 | isa = PBXGroup; 148 | children = ( 149 | 0907FEDD1ACDF7AC00C37C2E /* AppDelegate.h */, 150 | 0907FEDE1ACDF7AC00C37C2E /* AppDelegate.m */, 151 | 0907FEE01ACDF7AC00C37C2E /* ViewController.h */, 152 | 0907FEE11ACDF7AC00C37C2E /* ViewController.m */, 153 | 0907FEE31ACDF7AC00C37C2E /* Main.storyboard */, 154 | 0907FEE61ACDF7AC00C37C2E /* Images.xcassets */, 155 | 0907FEE81ACDF7AC00C37C2E /* LaunchScreen.xib */, 156 | 0907FED91ACDF7AC00C37C2E /* Supporting Files */, 157 | ); 158 | path = JBInterfaceController; 159 | sourceTree = ""; 160 | }; 161 | 0907FED91ACDF7AC00C37C2E /* Supporting Files */ = { 162 | isa = PBXGroup; 163 | children = ( 164 | 0907FEDA1ACDF7AC00C37C2E /* Info.plist */, 165 | 0907FEDB1ACDF7AC00C37C2E /* main.m */, 166 | ); 167 | name = "Supporting Files"; 168 | sourceTree = ""; 169 | }; 170 | 0907FEF21ACDF7AC00C37C2E /* JBInterfaceControllerTests */ = { 171 | isa = PBXGroup; 172 | children = ( 173 | 0907FEF51ACDF7AC00C37C2E /* JBInterfaceControllerTests.m */, 174 | 0907FEF31ACDF7AC00C37C2E /* Supporting Files */, 175 | ); 176 | path = JBInterfaceControllerTests; 177 | sourceTree = ""; 178 | }; 179 | 0907FEF31ACDF7AC00C37C2E /* Supporting Files */ = { 180 | isa = PBXGroup; 181 | children = ( 182 | 0907FEF41ACDF7AC00C37C2E /* Info.plist */, 183 | ); 184 | name = "Supporting Files"; 185 | sourceTree = ""; 186 | }; 187 | 0907FF041ACDF7EE00C37C2E /* JBInterfaceController WatchKit Extension */ = { 188 | isa = PBXGroup; 189 | children = ( 190 | 09EC1CB51ACE43A100D3A29D /* JBInterfaceController */, 191 | 0907FF071ACDF7EE00C37C2E /* InterfaceController.h */, 192 | 0907FF081ACDF7EE00C37C2E /* InterfaceController.m */, 193 | 09EC1CB61ACE44BF00D3A29D /* JBModalInterfaceController.h */, 194 | 09EC1CB71ACE44BF00D3A29D /* JBModalInterfaceController.m */, 195 | 0907FF0A1ACDF7EE00C37C2E /* Images.xcassets */, 196 | 0907FF051ACDF7EE00C37C2E /* Supporting Files */, 197 | ); 198 | path = "JBInterfaceController WatchKit Extension"; 199 | sourceTree = ""; 200 | }; 201 | 0907FF051ACDF7EE00C37C2E /* Supporting Files */ = { 202 | isa = PBXGroup; 203 | children = ( 204 | 0907FF061ACDF7EE00C37C2E /* Info.plist */, 205 | ); 206 | name = "Supporting Files"; 207 | sourceTree = ""; 208 | }; 209 | 0907FF121ACDF7EE00C37C2E /* JBInterfaceController WatchKit App */ = { 210 | isa = PBXGroup; 211 | children = ( 212 | 0907FF151ACDF7EE00C37C2E /* Interface.storyboard */, 213 | 0907FF181ACDF7EE00C37C2E /* Images.xcassets */, 214 | 0907FF131ACDF7EE00C37C2E /* Supporting Files */, 215 | ); 216 | path = "JBInterfaceController WatchKit App"; 217 | sourceTree = ""; 218 | }; 219 | 0907FF131ACDF7EE00C37C2E /* Supporting Files */ = { 220 | isa = PBXGroup; 221 | children = ( 222 | 0907FF141ACDF7EE00C37C2E /* Info.plist */, 223 | ); 224 | name = "Supporting Files"; 225 | sourceTree = ""; 226 | }; 227 | 09EC1CB51ACE43A100D3A29D /* JBInterfaceController */ = { 228 | isa = PBXGroup; 229 | children = ( 230 | 0988B3FD1ACDF9FB00A4EF18 /* JBInterface.h */, 231 | 0988B4041ACE03FC00A4EF18 /* JBInterface_Protected.h */, 232 | 0988B3FE1ACDF9FB00A4EF18 /* JBInterface.m */, 233 | 0988B4001ACDF9FB00A4EF18 /* JBInterfaceController.h */, 234 | 0988B3FF1ACDF9FB00A4EF18 /* JBInterfaceController_Protected.h */, 235 | 0988B4011ACDF9FB00A4EF18 /* JBInterfaceController.m */, 236 | ); 237 | name = JBInterfaceController; 238 | sourceTree = ""; 239 | }; 240 | /* End PBXGroup section */ 241 | 242 | /* Begin PBXNativeTarget section */ 243 | 0907FED51ACDF7AC00C37C2E /* JBInterfaceController */ = { 244 | isa = PBXNativeTarget; 245 | buildConfigurationList = 0907FEF91ACDF7AC00C37C2E /* Build configuration list for PBXNativeTarget "JBInterfaceController" */; 246 | buildPhases = ( 247 | 0907FED21ACDF7AC00C37C2E /* Sources */, 248 | 0907FED31ACDF7AC00C37C2E /* Frameworks */, 249 | 0907FED41ACDF7AC00C37C2E /* Resources */, 250 | 0907FF231ACDF7EE00C37C2E /* Embed App Extensions */, 251 | ); 252 | buildRules = ( 253 | ); 254 | dependencies = ( 255 | 0907FF1B1ACDF7EE00C37C2E /* PBXTargetDependency */, 256 | ); 257 | name = JBInterfaceController; 258 | productName = JBInterfaceController; 259 | productReference = 0907FED61ACDF7AC00C37C2E /* JBInterfaceController.app */; 260 | productType = "com.apple.product-type.application"; 261 | }; 262 | 0907FEEE1ACDF7AC00C37C2E /* JBInterfaceControllerTests */ = { 263 | isa = PBXNativeTarget; 264 | buildConfigurationList = 0907FEFC1ACDF7AC00C37C2E /* Build configuration list for PBXNativeTarget "JBInterfaceControllerTests" */; 265 | buildPhases = ( 266 | 0907FEEB1ACDF7AC00C37C2E /* Sources */, 267 | 0907FEEC1ACDF7AC00C37C2E /* Frameworks */, 268 | 0907FEED1ACDF7AC00C37C2E /* Resources */, 269 | ); 270 | buildRules = ( 271 | ); 272 | dependencies = ( 273 | 0907FEF11ACDF7AC00C37C2E /* PBXTargetDependency */, 274 | ); 275 | name = JBInterfaceControllerTests; 276 | productName = JBInterfaceControllerTests; 277 | productReference = 0907FEEF1ACDF7AC00C37C2E /* JBInterfaceControllerTests.xctest */; 278 | productType = "com.apple.product-type.bundle.unit-test"; 279 | }; 280 | 0907FF021ACDF7EE00C37C2E /* JBInterfaceController WatchKit Extension */ = { 281 | isa = PBXNativeTarget; 282 | buildConfigurationList = 0907FF221ACDF7EE00C37C2E /* Build configuration list for PBXNativeTarget "JBInterfaceController WatchKit Extension" */; 283 | buildPhases = ( 284 | 0907FEFF1ACDF7EE00C37C2E /* Sources */, 285 | 0907FF001ACDF7EE00C37C2E /* Frameworks */, 286 | 0907FF011ACDF7EE00C37C2E /* Resources */, 287 | ); 288 | buildRules = ( 289 | ); 290 | dependencies = ( 291 | 0907FF111ACDF7EE00C37C2E /* PBXTargetDependency */, 292 | ); 293 | name = "JBInterfaceController WatchKit Extension"; 294 | productName = "JBInterfaceController WatchKit Extension"; 295 | productReference = 0907FF031ACDF7EE00C37C2E /* JBInterfaceController WatchKit Extension.appex */; 296 | productType = "com.apple.product-type.watchkit-extension"; 297 | }; 298 | 0907FF0D1ACDF7EE00C37C2E /* JBInterfaceController WatchKit App */ = { 299 | isa = PBXNativeTarget; 300 | buildConfigurationList = 0907FF211ACDF7EE00C37C2E /* Build configuration list for PBXNativeTarget "JBInterfaceController WatchKit App" */; 301 | buildPhases = ( 302 | 0907FF0C1ACDF7EE00C37C2E /* Resources */, 303 | ); 304 | buildRules = ( 305 | ); 306 | dependencies = ( 307 | ); 308 | name = "JBInterfaceController WatchKit App"; 309 | productName = "JBInterfaceController WatchKit App"; 310 | productReference = 0907FF0E1ACDF7EE00C37C2E /* JBInterfaceController WatchKit App.app */; 311 | productType = "com.apple.product-type.application.watchapp"; 312 | }; 313 | /* End PBXNativeTarget section */ 314 | 315 | /* Begin PBXProject section */ 316 | 0907FECE1ACDF7AC00C37C2E /* Project object */ = { 317 | isa = PBXProject; 318 | attributes = { 319 | LastUpgradeCheck = 0620; 320 | ORGANIZATIONNAME = "Michael Swanson"; 321 | TargetAttributes = { 322 | 0907FED51ACDF7AC00C37C2E = { 323 | CreatedOnToolsVersion = 6.2; 324 | }; 325 | 0907FEEE1ACDF7AC00C37C2E = { 326 | CreatedOnToolsVersion = 6.2; 327 | TestTargetID = 0907FED51ACDF7AC00C37C2E; 328 | }; 329 | 0907FF021ACDF7EE00C37C2E = { 330 | CreatedOnToolsVersion = 6.2; 331 | }; 332 | 0907FF0D1ACDF7EE00C37C2E = { 333 | CreatedOnToolsVersion = 6.2; 334 | }; 335 | }; 336 | }; 337 | buildConfigurationList = 0907FED11ACDF7AC00C37C2E /* Build configuration list for PBXProject "JBInterfaceController" */; 338 | compatibilityVersion = "Xcode 3.2"; 339 | developmentRegion = English; 340 | hasScannedForEncodings = 0; 341 | knownRegions = ( 342 | en, 343 | Base, 344 | ); 345 | mainGroup = 0907FECD1ACDF7AC00C37C2E; 346 | productRefGroup = 0907FED71ACDF7AC00C37C2E /* Products */; 347 | projectDirPath = ""; 348 | projectRoot = ""; 349 | targets = ( 350 | 0907FED51ACDF7AC00C37C2E /* JBInterfaceController */, 351 | 0907FEEE1ACDF7AC00C37C2E /* JBInterfaceControllerTests */, 352 | 0907FF021ACDF7EE00C37C2E /* JBInterfaceController WatchKit Extension */, 353 | 0907FF0D1ACDF7EE00C37C2E /* JBInterfaceController WatchKit App */, 354 | ); 355 | }; 356 | /* End PBXProject section */ 357 | 358 | /* Begin PBXResourcesBuildPhase section */ 359 | 0907FED41ACDF7AC00C37C2E /* Resources */ = { 360 | isa = PBXResourcesBuildPhase; 361 | buildActionMask = 2147483647; 362 | files = ( 363 | 0907FEE51ACDF7AC00C37C2E /* Main.storyboard in Resources */, 364 | 0907FEEA1ACDF7AC00C37C2E /* LaunchScreen.xib in Resources */, 365 | 0907FEE71ACDF7AC00C37C2E /* Images.xcassets in Resources */, 366 | ); 367 | runOnlyForDeploymentPostprocessing = 0; 368 | }; 369 | 0907FEED1ACDF7AC00C37C2E /* Resources */ = { 370 | isa = PBXResourcesBuildPhase; 371 | buildActionMask = 2147483647; 372 | files = ( 373 | ); 374 | runOnlyForDeploymentPostprocessing = 0; 375 | }; 376 | 0907FF011ACDF7EE00C37C2E /* Resources */ = { 377 | isa = PBXResourcesBuildPhase; 378 | buildActionMask = 2147483647; 379 | files = ( 380 | 0907FF0F1ACDF7EE00C37C2E /* JBInterfaceController WatchKit App.app in Resources */, 381 | 0907FF0B1ACDF7EE00C37C2E /* Images.xcassets in Resources */, 382 | ); 383 | runOnlyForDeploymentPostprocessing = 0; 384 | }; 385 | 0907FF0C1ACDF7EE00C37C2E /* Resources */ = { 386 | isa = PBXResourcesBuildPhase; 387 | buildActionMask = 2147483647; 388 | files = ( 389 | 0907FF171ACDF7EE00C37C2E /* Interface.storyboard in Resources */, 390 | 0907FF191ACDF7EE00C37C2E /* Images.xcassets in Resources */, 391 | ); 392 | runOnlyForDeploymentPostprocessing = 0; 393 | }; 394 | /* End PBXResourcesBuildPhase section */ 395 | 396 | /* Begin PBXSourcesBuildPhase section */ 397 | 0907FED21ACDF7AC00C37C2E /* Sources */ = { 398 | isa = PBXSourcesBuildPhase; 399 | buildActionMask = 2147483647; 400 | files = ( 401 | 0907FEE21ACDF7AC00C37C2E /* ViewController.m in Sources */, 402 | 0907FEDF1ACDF7AC00C37C2E /* AppDelegate.m in Sources */, 403 | 0907FEDC1ACDF7AC00C37C2E /* main.m in Sources */, 404 | ); 405 | runOnlyForDeploymentPostprocessing = 0; 406 | }; 407 | 0907FEEB1ACDF7AC00C37C2E /* Sources */ = { 408 | isa = PBXSourcesBuildPhase; 409 | buildActionMask = 2147483647; 410 | files = ( 411 | 0907FEF61ACDF7AC00C37C2E /* JBInterfaceControllerTests.m in Sources */, 412 | ); 413 | runOnlyForDeploymentPostprocessing = 0; 414 | }; 415 | 0907FEFF1ACDF7EE00C37C2E /* Sources */ = { 416 | isa = PBXSourcesBuildPhase; 417 | buildActionMask = 2147483647; 418 | files = ( 419 | 0988B4031ACDF9FB00A4EF18 /* JBInterfaceController.m in Sources */, 420 | 09EC1CB81ACE44BF00D3A29D /* JBModalInterfaceController.m in Sources */, 421 | 0907FF091ACDF7EE00C37C2E /* InterfaceController.m in Sources */, 422 | 0988B4021ACDF9FB00A4EF18 /* JBInterface.m in Sources */, 423 | ); 424 | runOnlyForDeploymentPostprocessing = 0; 425 | }; 426 | /* End PBXSourcesBuildPhase section */ 427 | 428 | /* Begin PBXTargetDependency section */ 429 | 0907FEF11ACDF7AC00C37C2E /* PBXTargetDependency */ = { 430 | isa = PBXTargetDependency; 431 | target = 0907FED51ACDF7AC00C37C2E /* JBInterfaceController */; 432 | targetProxy = 0907FEF01ACDF7AC00C37C2E /* PBXContainerItemProxy */; 433 | }; 434 | 0907FF111ACDF7EE00C37C2E /* PBXTargetDependency */ = { 435 | isa = PBXTargetDependency; 436 | target = 0907FF0D1ACDF7EE00C37C2E /* JBInterfaceController WatchKit App */; 437 | targetProxy = 0907FF101ACDF7EE00C37C2E /* PBXContainerItemProxy */; 438 | }; 439 | 0907FF1B1ACDF7EE00C37C2E /* PBXTargetDependency */ = { 440 | isa = PBXTargetDependency; 441 | target = 0907FF021ACDF7EE00C37C2E /* JBInterfaceController WatchKit Extension */; 442 | targetProxy = 0907FF1A1ACDF7EE00C37C2E /* PBXContainerItemProxy */; 443 | }; 444 | /* End PBXTargetDependency section */ 445 | 446 | /* Begin PBXVariantGroup section */ 447 | 0907FEE31ACDF7AC00C37C2E /* Main.storyboard */ = { 448 | isa = PBXVariantGroup; 449 | children = ( 450 | 0907FEE41ACDF7AC00C37C2E /* Base */, 451 | ); 452 | name = Main.storyboard; 453 | sourceTree = ""; 454 | }; 455 | 0907FEE81ACDF7AC00C37C2E /* LaunchScreen.xib */ = { 456 | isa = PBXVariantGroup; 457 | children = ( 458 | 0907FEE91ACDF7AC00C37C2E /* Base */, 459 | ); 460 | name = LaunchScreen.xib; 461 | sourceTree = ""; 462 | }; 463 | 0907FF151ACDF7EE00C37C2E /* Interface.storyboard */ = { 464 | isa = PBXVariantGroup; 465 | children = ( 466 | 0907FF161ACDF7EE00C37C2E /* Base */, 467 | ); 468 | name = Interface.storyboard; 469 | sourceTree = ""; 470 | }; 471 | /* End PBXVariantGroup section */ 472 | 473 | /* Begin XCBuildConfiguration section */ 474 | 0907FEF71ACDF7AC00C37C2E /* Debug */ = { 475 | isa = XCBuildConfiguration; 476 | buildSettings = { 477 | ALWAYS_SEARCH_USER_PATHS = NO; 478 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 479 | CLANG_CXX_LIBRARY = "libc++"; 480 | CLANG_ENABLE_MODULES = YES; 481 | CLANG_ENABLE_OBJC_ARC = YES; 482 | CLANG_WARN_BOOL_CONVERSION = YES; 483 | CLANG_WARN_CONSTANT_CONVERSION = YES; 484 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 485 | CLANG_WARN_EMPTY_BODY = YES; 486 | CLANG_WARN_ENUM_CONVERSION = YES; 487 | CLANG_WARN_INT_CONVERSION = YES; 488 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 489 | CLANG_WARN_UNREACHABLE_CODE = YES; 490 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 491 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 492 | COPY_PHASE_STRIP = NO; 493 | ENABLE_STRICT_OBJC_MSGSEND = YES; 494 | GCC_C_LANGUAGE_STANDARD = gnu99; 495 | GCC_DYNAMIC_NO_PIC = NO; 496 | GCC_OPTIMIZATION_LEVEL = 0; 497 | GCC_PREPROCESSOR_DEFINITIONS = ( 498 | "DEBUG=1", 499 | "$(inherited)", 500 | ); 501 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 502 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 503 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 504 | GCC_WARN_UNDECLARED_SELECTOR = YES; 505 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 506 | GCC_WARN_UNUSED_FUNCTION = YES; 507 | GCC_WARN_UNUSED_VARIABLE = YES; 508 | IPHONEOS_DEPLOYMENT_TARGET = 8.2; 509 | MTL_ENABLE_DEBUG_INFO = YES; 510 | ONLY_ACTIVE_ARCH = YES; 511 | SDKROOT = iphoneos; 512 | }; 513 | name = Debug; 514 | }; 515 | 0907FEF81ACDF7AC00C37C2E /* Release */ = { 516 | isa = XCBuildConfiguration; 517 | buildSettings = { 518 | ALWAYS_SEARCH_USER_PATHS = NO; 519 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 520 | CLANG_CXX_LIBRARY = "libc++"; 521 | CLANG_ENABLE_MODULES = YES; 522 | CLANG_ENABLE_OBJC_ARC = YES; 523 | CLANG_WARN_BOOL_CONVERSION = YES; 524 | CLANG_WARN_CONSTANT_CONVERSION = YES; 525 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 526 | CLANG_WARN_EMPTY_BODY = YES; 527 | CLANG_WARN_ENUM_CONVERSION = YES; 528 | CLANG_WARN_INT_CONVERSION = YES; 529 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 530 | CLANG_WARN_UNREACHABLE_CODE = YES; 531 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 532 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 533 | COPY_PHASE_STRIP = NO; 534 | ENABLE_NS_ASSERTIONS = NO; 535 | ENABLE_STRICT_OBJC_MSGSEND = YES; 536 | GCC_C_LANGUAGE_STANDARD = gnu99; 537 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 538 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 539 | GCC_WARN_UNDECLARED_SELECTOR = YES; 540 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 541 | GCC_WARN_UNUSED_FUNCTION = YES; 542 | GCC_WARN_UNUSED_VARIABLE = YES; 543 | IPHONEOS_DEPLOYMENT_TARGET = 8.2; 544 | MTL_ENABLE_DEBUG_INFO = NO; 545 | SDKROOT = iphoneos; 546 | VALIDATE_PRODUCT = YES; 547 | }; 548 | name = Release; 549 | }; 550 | 0907FEFA1ACDF7AC00C37C2E /* Debug */ = { 551 | isa = XCBuildConfiguration; 552 | buildSettings = { 553 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 554 | INFOPLIST_FILE = JBInterfaceController/Info.plist; 555 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 556 | PRODUCT_NAME = "$(TARGET_NAME)"; 557 | }; 558 | name = Debug; 559 | }; 560 | 0907FEFB1ACDF7AC00C37C2E /* Release */ = { 561 | isa = XCBuildConfiguration; 562 | buildSettings = { 563 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 564 | INFOPLIST_FILE = JBInterfaceController/Info.plist; 565 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 566 | PRODUCT_NAME = "$(TARGET_NAME)"; 567 | }; 568 | name = Release; 569 | }; 570 | 0907FEFD1ACDF7AC00C37C2E /* Debug */ = { 571 | isa = XCBuildConfiguration; 572 | buildSettings = { 573 | BUNDLE_LOADER = "$(TEST_HOST)"; 574 | FRAMEWORK_SEARCH_PATHS = ( 575 | "$(SDKROOT)/Developer/Library/Frameworks", 576 | "$(inherited)", 577 | ); 578 | GCC_PREPROCESSOR_DEFINITIONS = ( 579 | "DEBUG=1", 580 | "$(inherited)", 581 | ); 582 | INFOPLIST_FILE = JBInterfaceControllerTests/Info.plist; 583 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 584 | PRODUCT_NAME = "$(TARGET_NAME)"; 585 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/JBInterfaceController.app/JBInterfaceController"; 586 | }; 587 | name = Debug; 588 | }; 589 | 0907FEFE1ACDF7AC00C37C2E /* Release */ = { 590 | isa = XCBuildConfiguration; 591 | buildSettings = { 592 | BUNDLE_LOADER = "$(TEST_HOST)"; 593 | FRAMEWORK_SEARCH_PATHS = ( 594 | "$(SDKROOT)/Developer/Library/Frameworks", 595 | "$(inherited)", 596 | ); 597 | INFOPLIST_FILE = JBInterfaceControllerTests/Info.plist; 598 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 599 | PRODUCT_NAME = "$(TARGET_NAME)"; 600 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/JBInterfaceController.app/JBInterfaceController"; 601 | }; 602 | name = Release; 603 | }; 604 | 0907FF1D1ACDF7EE00C37C2E /* Debug */ = { 605 | isa = XCBuildConfiguration; 606 | buildSettings = { 607 | GCC_PREPROCESSOR_DEFINITIONS = ( 608 | "DEBUG=1", 609 | "$(inherited)", 610 | ); 611 | INFOPLIST_FILE = "JBInterfaceController WatchKit Extension/Info.plist"; 612 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; 613 | PRODUCT_NAME = "${TARGET_NAME}"; 614 | SKIP_INSTALL = YES; 615 | }; 616 | name = Debug; 617 | }; 618 | 0907FF1E1ACDF7EE00C37C2E /* Release */ = { 619 | isa = XCBuildConfiguration; 620 | buildSettings = { 621 | INFOPLIST_FILE = "JBInterfaceController WatchKit Extension/Info.plist"; 622 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; 623 | PRODUCT_NAME = "${TARGET_NAME}"; 624 | SKIP_INSTALL = YES; 625 | }; 626 | name = Release; 627 | }; 628 | 0907FF1F1ACDF7EE00C37C2E /* Debug */ = { 629 | isa = XCBuildConfiguration; 630 | buildSettings = { 631 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 632 | GCC_PREPROCESSOR_DEFINITIONS = ( 633 | "DEBUG=1", 634 | "$(inherited)", 635 | ); 636 | IBSC_MODULE = JBInterfaceController_WatchKit_Extension; 637 | INFOPLIST_FILE = "JBInterfaceController WatchKit App/Info.plist"; 638 | PRODUCT_NAME = "$(TARGET_NAME)"; 639 | SKIP_INSTALL = YES; 640 | TARGETED_DEVICE_FAMILY = 4; 641 | "TARGETED_DEVICE_FAMILY[sdk=iphonesimulator*]" = "1,4"; 642 | }; 643 | name = Debug; 644 | }; 645 | 0907FF201ACDF7EE00C37C2E /* Release */ = { 646 | isa = XCBuildConfiguration; 647 | buildSettings = { 648 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 649 | IBSC_MODULE = JBInterfaceController_WatchKit_Extension; 650 | INFOPLIST_FILE = "JBInterfaceController WatchKit App/Info.plist"; 651 | PRODUCT_NAME = "$(TARGET_NAME)"; 652 | SKIP_INSTALL = YES; 653 | TARGETED_DEVICE_FAMILY = 4; 654 | "TARGETED_DEVICE_FAMILY[sdk=iphonesimulator*]" = "1,4"; 655 | }; 656 | name = Release; 657 | }; 658 | /* End XCBuildConfiguration section */ 659 | 660 | /* Begin XCConfigurationList section */ 661 | 0907FED11ACDF7AC00C37C2E /* Build configuration list for PBXProject "JBInterfaceController" */ = { 662 | isa = XCConfigurationList; 663 | buildConfigurations = ( 664 | 0907FEF71ACDF7AC00C37C2E /* Debug */, 665 | 0907FEF81ACDF7AC00C37C2E /* Release */, 666 | ); 667 | defaultConfigurationIsVisible = 0; 668 | defaultConfigurationName = Release; 669 | }; 670 | 0907FEF91ACDF7AC00C37C2E /* Build configuration list for PBXNativeTarget "JBInterfaceController" */ = { 671 | isa = XCConfigurationList; 672 | buildConfigurations = ( 673 | 0907FEFA1ACDF7AC00C37C2E /* Debug */, 674 | 0907FEFB1ACDF7AC00C37C2E /* Release */, 675 | ); 676 | defaultConfigurationIsVisible = 0; 677 | defaultConfigurationName = Release; 678 | }; 679 | 0907FEFC1ACDF7AC00C37C2E /* Build configuration list for PBXNativeTarget "JBInterfaceControllerTests" */ = { 680 | isa = XCConfigurationList; 681 | buildConfigurations = ( 682 | 0907FEFD1ACDF7AC00C37C2E /* Debug */, 683 | 0907FEFE1ACDF7AC00C37C2E /* Release */, 684 | ); 685 | defaultConfigurationIsVisible = 0; 686 | defaultConfigurationName = Release; 687 | }; 688 | 0907FF211ACDF7EE00C37C2E /* Build configuration list for PBXNativeTarget "JBInterfaceController WatchKit App" */ = { 689 | isa = XCConfigurationList; 690 | buildConfigurations = ( 691 | 0907FF1F1ACDF7EE00C37C2E /* Debug */, 692 | 0907FF201ACDF7EE00C37C2E /* Release */, 693 | ); 694 | defaultConfigurationIsVisible = 0; 695 | defaultConfigurationName = Release; 696 | }; 697 | 0907FF221ACDF7EE00C37C2E /* Build configuration list for PBXNativeTarget "JBInterfaceController WatchKit Extension" */ = { 698 | isa = XCConfigurationList; 699 | buildConfigurations = ( 700 | 0907FF1D1ACDF7EE00C37C2E /* Debug */, 701 | 0907FF1E1ACDF7EE00C37C2E /* Release */, 702 | ); 703 | defaultConfigurationIsVisible = 0; 704 | defaultConfigurationName = Release; 705 | }; 706 | /* End XCConfigurationList section */ 707 | }; 708 | rootObject = 0907FECE1ACDF7AC00C37C2E /* Project object */; 709 | } 710 | -------------------------------------------------------------------------------- /Example/JBInterfaceController/JBInterfaceController.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Example/JBInterfaceController/JBInterfaceController/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // JBInterfaceController 4 | // 5 | // Created by Michael Swanson on 4/2/15. 6 | // Copyright (c) 2015 Michael Swanson. 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 | -------------------------------------------------------------------------------- /Example/JBInterfaceController/JBInterfaceController/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // JBInterfaceController 4 | // 5 | // Created by Michael Swanson on 4/2/15. 6 | // Copyright (c) 2015 Michael Swanson. 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 | -------------------------------------------------------------------------------- /Example/JBInterfaceController/JBInterfaceController/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 20 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /Example/JBInterfaceController/JBInterfaceController/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 | -------------------------------------------------------------------------------- /Example/JBInterfaceController/JBInterfaceController/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 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /Example/JBInterfaceController/JBInterfaceController/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | com.juicybitssoftware.$(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 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /Example/JBInterfaceController/JBInterfaceController/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // JBInterfaceController 4 | // 5 | // Created by Michael Swanson on 4/2/15. 6 | // Copyright (c) 2015 Michael Swanson. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /Example/JBInterfaceController/JBInterfaceController/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // JBInterfaceController 4 | // 5 | // Created by Michael Swanson on 4/2/15. 6 | // Copyright (c) 2015 Michael Swanson. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | 11 | @interface ViewController () 12 | 13 | @end 14 | 15 | @implementation ViewController 16 | 17 | - (void)viewDidLoad { 18 | [super viewDidLoad]; 19 | // Do any additional setup after loading the view, typically from a nib. 20 | } 21 | 22 | - (void)didReceiveMemoryWarning { 23 | [super didReceiveMemoryWarning]; 24 | // Dispose of any resources that can be recreated. 25 | } 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /Example/JBInterfaceController/JBInterfaceController/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // JBInterfaceController 4 | // 5 | // Created by Michael Swanson on 4/2/15. 6 | // Copyright (c) 2015 Michael Swanson. 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 | -------------------------------------------------------------------------------- /Example/JBInterfaceController/JBInterfaceControllerTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | com.juicybitssoftware.$(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 | -------------------------------------------------------------------------------- /Example/JBInterfaceController/JBInterfaceControllerTests/JBInterfaceControllerTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // JBInterfaceControllerTests.m 3 | // JBInterfaceControllerTests 4 | // 5 | // Created by Michael Swanson on 4/2/15. 6 | // Copyright (c) 2015 Michael Swanson. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface JBInterfaceControllerTests : XCTestCase 13 | 14 | @end 15 | 16 | @implementation JBInterfaceControllerTests 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 | -------------------------------------------------------------------------------- /JBInterfaceController.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | 3 | s.name = 'JBInterfaceController' 4 | s.version = '1.0.0' 5 | s.summary = 'Adds UIViewController-like functionality to WatchKit interface controllers' 6 | s.homepage = 'https://github.com/mikeswanson/JBInterfaceController' 7 | s.license = { :type => 'MIT', :file => 'LICENSE' } 8 | s.author = 'Mike Swanson' 9 | s.ios.platform = :ios, '8.2' 10 | s.source = { :git => 'https://github.com/mikeswanson/JBInterfaceController.git', :tag => s.version.to_s } 11 | s.source_files = 'Source/*.{h,m}' 12 | s.requires_arc = true 13 | s.ios.deployment_target = '8.0' 14 | 15 | end 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Mike Swanson 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | JBInterfaceController 2 | ===================== 3 | 4 | By [Mike Swanson](http://blog.mikeswanson.com/) 5 | 6 | JBInterfaceController is a WKInterfaceController subclass that makes it easier to manage interface controllers with WatchKit. 7 | 8 | I don't have time to write documentation at the moment, as I'm working on my own WatchKit app. I'd recommend looking at the example project and the headers. All that said, here are a few quick comments: 9 | 10 | * The example shows how to present interface controllers that display a replacement status bar when the "modal bug" happens. 11 | * The example also shows how to configure delegates if you need to communicate between a presented interface controller and its presenting controller. 12 | * The subclasses make it easy to update interface elements by calling an updateInterface method (modeled lightly after UIView drawRect and layoutSubviews concepts). 13 | * My table rows (which I didn't include in this example) are JBInterface subclasses that simply invalidate themselves. They're then updated at the next opportunity. 14 | * There's a prepareForActivation method that lets your interface controller do speculative work like pre-caching an image before the next page is displayed. 15 | * You can pass dismiss blocks when you present an interface controller. 16 | * There are calls to the presenting controller letting it know about events that happen to a presented controller (like presentedControllerWillActivate:, etc.). 17 | 18 | This was a pretty quick extraction, so I'm sure I didn't get everything right. But, I'm buried at the moment and figured that anything was better than nothing. 19 | 20 | ## Requirements 21 | 22 | Because JBInterfaceController is based on WatchKit, it requires iOS 8.2 or later. -------------------------------------------------------------------------------- /Source/JBInterface.h: -------------------------------------------------------------------------------- 1 | // 2 | // JBInterface.h 3 | // 4 | // Created by Mike Swanson on 2/9/2015 5 | // Copyright (c) 2015 Mike Swanson. All rights reserved. 6 | // http://blog.mikeswanson.com 7 | // 8 | // Permission is hereby granted, free of charge, to any person 9 | // obtaining a copy of this software and associated documentation 10 | // files (the "Software"), to deal in the Software without 11 | // restriction, including without limitation the rights to use, 12 | // copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the 14 | // Software is furnished to do so, subject to the following 15 | // conditions: 16 | // 17 | // The above copyright notice and this permission notice shall be 18 | // included in all copies or substantial portions of the Software. 19 | // 20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 22 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 24 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 25 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 26 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 27 | // OTHER DEALINGS IN THE SOFTWARE. 28 | // 29 | 30 | #import 31 | 32 | /** 33 | * @class JBInterface 34 | * 35 | * @brief The @p JBInterface class represents an interface for managing visible content. 36 | */ 37 | @interface JBInterface : NSObject 38 | 39 | /** 40 | * @brief The receiver’s superinterface, or nil if it has none. (read-only) 41 | */ 42 | @property (nonatomic, readonly, weak) JBInterface *superinterface; 43 | 44 | /** 45 | * @brief The receiver’s immediate subinterfaces. (read-only) 46 | */ 47 | @property (nonatomic, readonly, strong) NSArray *subinterfaces; 48 | 49 | /** 50 | * Adds an interface to the end of the receiver’s list of subinterfaces. 51 | * 52 | * This method establishes a strong reference to @p interface and sets its superinterface to the receiver. 53 | * 54 | * Interfaces can have only one superinterface. If @p interface already has a superinterface and that 55 | * interface is not the receiver, this method removes the previous superview before making the receiver 56 | * its new superview. 57 | * 58 | * @param interface The interface to be added. 59 | */ 60 | - (void)addSubinterface:(JBInterface *)interface; 61 | 62 | /** 63 | * Unlinks the interface from its superinterface. 64 | * 65 | * If the interface’s superinterface is not nil, the superinterface releases the view. 66 | */ 67 | - (void)removeFromSuperinterface; 68 | 69 | /** 70 | * Marks the receiver as needing to be updated. 71 | * 72 | * You can use this method to notify the system that your interface’s contents need to be updated. 73 | */ 74 | - (void)setNeedsUpdate; 75 | 76 | /** 77 | * Updates the interface and subinterfaces immediately. 78 | */ 79 | - (void)updateIfNeeded; 80 | 81 | /** 82 | * Updates the interface. 83 | * 84 | * Subclasses can override this method as needed to update the interface. You should not call this method 85 | * directly. If you want to force an interface update at the next opportunity, call the @p setNeedsUpdate 86 | * method. If you want to update the interface immediately, call the @p updateIfNeeded method. 87 | */ 88 | - (void)updateInterface; 89 | 90 | @end 91 | -------------------------------------------------------------------------------- /Source/JBInterface.m: -------------------------------------------------------------------------------- 1 | // 2 | // JBInterface.m 3 | // 4 | // Created by Mike Swanson on 2/9/2015 5 | // Copyright (c) 2015 Mike Swanson. All rights reserved. 6 | // http://blog.mikeswanson.com 7 | // 8 | // Permission is hereby granted, free of charge, to any person 9 | // obtaining a copy of this software and associated documentation 10 | // files (the "Software"), to deal in the Software without 11 | // restriction, including without limitation the rights to use, 12 | // copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the 14 | // Software is furnished to do so, subject to the following 15 | // conditions: 16 | // 17 | // The above copyright notice and this permission notice shall be 18 | // included in all copies or substantial portions of the Software. 19 | // 20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 22 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 24 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 25 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 26 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 27 | // OTHER DEALINGS IN THE SOFTWARE. 28 | // 29 | 30 | #import "JBInterface_Protected.h" 31 | 32 | @interface JBInterface () 33 | 34 | @property (nonatomic, readwrite, weak) JBInterface *superinterface; 35 | @property (nonatomic, readwrite, strong) NSMutableArray *mutableSubinterfaces; 36 | 37 | @end 38 | 39 | @implementation JBInterface 40 | 41 | #pragma mark - Object management 42 | 43 | - (instancetype)init { 44 | 45 | self = [super init]; 46 | if (self) { 47 | 48 | _superinterface = nil; 49 | _mutableSubinterfaces = [NSMutableArray array]; 50 | _needsUpdate = YES; 51 | } 52 | return self; 53 | } 54 | 55 | #pragma mark - Properties 56 | 57 | - (NSArray *)subinterfaces { 58 | 59 | return [NSArray arrayWithArray:_mutableSubinterfaces]; 60 | } 61 | 62 | #pragma mark - Methods 63 | 64 | - (void)addSubinterface:(JBInterface *)interface { 65 | 66 | NSParameterAssert(interface); 67 | NSAssert(![self.mutableSubinterfaces containsObject:interface], @"Interface has already been added"); 68 | 69 | if (interface.superinterface && 70 | interface.superinterface != self) { 71 | 72 | [interface removeFromSuperinterface]; 73 | } 74 | else { 75 | 76 | interface.superinterface = self; 77 | } 78 | 79 | [self.mutableSubinterfaces addObject:interface]; 80 | 81 | // Make sure we do an update pass on our new child 82 | [interface setNeedsUpdate]; 83 | } 84 | 85 | - (void)removeSubinterface:(JBInterface *)interface { 86 | 87 | NSParameterAssert(interface); 88 | NSAssert([self.mutableSubinterfaces containsObject:interface], @"Interface not found"); 89 | 90 | interface.superinterface = nil; 91 | [self.mutableSubinterfaces removeObject:interface]; 92 | } 93 | 94 | - (void)removeFromSuperinterface { 95 | 96 | if (self.superinterface) { 97 | 98 | [self.superinterface removeSubinterface:self]; 99 | } 100 | } 101 | 102 | - (void)setNeedsUpdate { 103 | 104 | self.needsUpdate = YES; 105 | 106 | // Up the chain 107 | if (self.superinterface) { 108 | 109 | [self.superinterface setNeedsUpdate]; 110 | } 111 | } 112 | 113 | - (void)updateIfNeeded { 114 | 115 | if (self.needsUpdate) { 116 | 117 | [self updateInterface]; 118 | 119 | NSAssert(!self.needsUpdate, @"updateInterface must call super"); 120 | 121 | for (JBInterface *interface in self.mutableSubinterfaces) { 122 | 123 | [interface updateIfNeeded]; 124 | } 125 | } 126 | } 127 | 128 | - (void)updateInterface { 129 | 130 | self.needsUpdate = NO; 131 | } 132 | 133 | @end 134 | -------------------------------------------------------------------------------- /Source/JBInterfaceController.h: -------------------------------------------------------------------------------- 1 | // 2 | // JBInterfaceController.h 3 | // 4 | // Created by Mike Swanson on 2/9/2015 5 | // Copyright (c) 2015 Mike Swanson. All rights reserved. 6 | // http://blog.mikeswanson.com 7 | // 8 | // Permission is hereby granted, free of charge, to any person 9 | // obtaining a copy of this software and associated documentation 10 | // files (the "Software"), to deal in the Software without 11 | // restriction, including without limitation the rights to use, 12 | // copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the 14 | // Software is furnished to do so, subject to the following 15 | // conditions: 16 | // 17 | // The above copyright notice and this permission notice shall be 18 | // included in all copies or substantial portions of the Software. 19 | // 20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 22 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 24 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 25 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 26 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 27 | // OTHER DEALINGS IN THE SOFTWARE. 28 | // 29 | 30 | #import 31 | #import 32 | #import "JBInterface.h" 33 | 34 | @class JBInterfaceControllerConfigurator; 35 | 36 | /** 37 | * @class JBInterfaceController 38 | * 39 | * @brief The @p JBInterfaceController class is a subclass of @p WKInterfaceController 40 | * that makes it easier to manage your Watch app’s interface. 41 | */ 42 | @interface JBInterfaceController : WKInterfaceController 43 | 44 | /** 45 | * @brief The interface controller that presented this interface controller - if known. (read-only) 46 | */ 47 | @property (nonatomic, readonly, weak) JBInterfaceController *presentingController; 48 | 49 | /** 50 | * @brief The interface controllers that have been presented by this interface controller. (read-only) 51 | */ 52 | @property (nonatomic, readonly, strong) NSPointerArray *presentedControllers; 53 | 54 | /** 55 | * @brief The interface stored in this property represents the root interface for the interface 56 | * controller'€™s interface hierarchy. (read-only) 57 | */ 58 | @property (nonatomic, readonly, strong) JBInterface *interface; 59 | 60 | /** 61 | * @brief A Boolean value that represents whether the interface is currently visible. (read-only) 62 | */ 63 | @property (nonatomic, readonly, assign, 64 | getter=isVisible) BOOL visible; 65 | 66 | /** 67 | * @brief A Boolean value that represents whether the interface is currently presenting 68 | * another interface. (read-only) 69 | */ 70 | @property (nonatomic, readonly, assign, 71 | getter=isPresenting) BOOL presenting; 72 | 73 | /** 74 | * Like @p presentControllerWithName:context:, but allows a configurator block. 75 | * 76 | * @param configurator The configurator block used to configure the presented interface controller. 77 | */ 78 | - (void)presentControllerWithConfigurator:(JBInterfaceControllerConfigurator *)configurator; 79 | 80 | /** 81 | * Like @p presentControllerWithNames:contexts:, but allows an array of configurator blocks. 82 | * 83 | * @param configurators An array of configurator blocks used to configure the presented interface 84 | * controllers. 85 | */ 86 | - (void)presentControllersWithConfigurators:(NSArray *)configurators; 87 | 88 | /** 89 | * Called to notify the interface controller to perform speculative work (like pre-caching an image). 90 | * 91 | * NOTE: This is called when a prior interface controller (depending on paging direction) is activated. 92 | */ 93 | - (void)prepareForActivation; 94 | 95 | /** 96 | * Called to notify the interface controller that a presented interface controller has awakened. 97 | */ 98 | - (void)presentedController:(JBInterfaceController *)presentedController 99 | didAwakeWithContext:(id)context; 100 | 101 | /** 102 | * Called to notify the interface controller that a presented interface controller will activate. 103 | */ 104 | - (void)presentedControllerWillActivate:(JBInterfaceController *)presentedController; 105 | 106 | /** 107 | * Called to notify the interface controller that a presented interface controller did deactivate. 108 | */ 109 | - (void)presentedControllerDidDeactivate:(JBInterfaceController *)presentedController; 110 | 111 | /** 112 | * Called to notify the interface controller that its interface has just been updated. 113 | * 114 | * NOTE: This method can be called repeatedly for small interface updates, so update intelligently. 115 | */ 116 | - (void)didUpdateInterface; 117 | 118 | /** 119 | * Dismisses the current interface controller from the screen and executes the @p completion block. 120 | * 121 | * @param completion The completion block to execute. 122 | */ 123 | - (void)dismissControllerWithCompletion:(void (^)(void))completion; 124 | 125 | @end 126 | 127 | /** 128 | * @typedef JBInterfaceControllerConfigureBlock 129 | * 130 | * @brief This block is used to configure a presented interface controller. For example, cast 131 | * @p controller to a subtype and set a delegate, etc. 132 | */ 133 | typedef void (^JBInterfaceControllerConfigureBlock)(JBInterfaceController *controller); 134 | 135 | /** 136 | * @typedef JBInterfaceControllerDismissBlock 137 | * 138 | * @brief This block is executed after a presented interface controller is dismissed. 139 | */ 140 | typedef void (^JBInterfaceControllerDismissBlock)(JBInterfaceController *controller); 141 | 142 | /** 143 | * @class JBInterfaceControllerConfigurator 144 | * 145 | * @brief The @p JBInterfaceControllerConfigurator class encapsulates properties that are used to 146 | * configure a presented interface controller. 147 | */ 148 | @interface JBInterfaceControllerConfigurator : NSObject 149 | 150 | /** 151 | * @brief The name of the interface controller to display. (read-only) 152 | */ 153 | @property (nonatomic, readonly, copy) NSString *name; 154 | 155 | /** 156 | * @brief An object to pass to the new interface controller. (read-only) 157 | */ 158 | @property (nonatomic, readonly, strong) id context; 159 | 160 | /** 161 | * @brief A block that is used to configure the new interface controller. (read-only) 162 | */ 163 | @property (nonatomic, readonly, copy) JBInterfaceControllerConfigureBlock configureBlock; 164 | 165 | /** 166 | * @brief A block that is executed when the new interface controller is dismissed. (read-only) 167 | */ 168 | @property (nonatomic, readonly, copy) JBInterfaceControllerDismissBlock dismissBlock; 169 | 170 | /** 171 | * Creates a new interface controller configurator. 172 | * 173 | * @param name The name of the interface controller you want to display. 174 | * This parameter must not be nil. 175 | * 176 | * @param context An object to pass to the new interface controller. 177 | * 178 | * @param configureBlock A block that is used to configure the new interface controller. 179 | * 180 | * @param dismissBlock A block that is executed when the new interface controller is dismissed. 181 | */ 182 | + (JBInterfaceControllerConfigurator *)configuratorWithName:(NSString *)name 183 | context:(id)context 184 | configureBlock:(JBInterfaceControllerConfigureBlock)configureBlock 185 | dismissBlock:(JBInterfaceControllerDismissBlock)dismissBlock; 186 | 187 | @end -------------------------------------------------------------------------------- /Source/JBInterfaceController.m: -------------------------------------------------------------------------------- 1 | // 2 | // JBInterfaceController.m 3 | // 4 | // Created by Mike Swanson on 2/9/2015 5 | // Copyright (c) 2015 Mike Swanson. All rights reserved. 6 | // http://blog.mikeswanson.com 7 | // 8 | // Permission is hereby granted, free of charge, to any person 9 | // obtaining a copy of this software and associated documentation 10 | // files (the "Software"), to deal in the Software without 11 | // restriction, including without limitation the rights to use, 12 | // copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the 14 | // Software is furnished to do so, subject to the following 15 | // conditions: 16 | // 17 | // The above copyright notice and this permission notice shall be 18 | // included in all copies or substantial portions of the Software. 19 | // 20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 22 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 24 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 25 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 26 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 27 | // OTHER DEALINGS IN THE SOFTWARE. 28 | // 29 | 30 | #import "JBInterfaceController_Protected.h" 31 | #import "JBInterface_Protected.h" 32 | 33 | typedef NS_ENUM(NSInteger, JBInterfaceControllerPagingDirection) { 34 | 35 | JBInterfaceControllerPagingDirectionUnknown, 36 | JBInterfaceControllerPagingDirectionForwards, 37 | JBInterfaceControllerPagingDirectionBackwards 38 | }; 39 | 40 | #pragma mark - JBInterfaceControllerContext class 41 | 42 | /** 43 | * @class JBInterfaceControllerContext 44 | * 45 | * @brief Additional context to use when presenting interface controllers. 46 | */ 47 | @interface JBInterfaceControllerContext : NSObject 48 | 49 | @property (nonatomic, readwrite, weak) JBInterfaceController *presentingController; 50 | @property (nonatomic, readwrite, assign) NSUInteger index; 51 | @property (nonatomic, readwrite, strong) id context; 52 | 53 | + (JBInterfaceControllerContext *)contextWithPresentingController:(JBInterfaceController *)presentingController 54 | index:(NSUInteger)index 55 | context:(id)context; 56 | 57 | @end 58 | 59 | @implementation JBInterfaceControllerContext 60 | 61 | + (JBInterfaceControllerContext *)contextWithPresentingController:(JBInterfaceController *)presentingController 62 | index:(NSUInteger)index 63 | context:(id)context { 64 | 65 | NSParameterAssert(presentingController); 66 | 67 | JBInterfaceControllerContext *controllerContext = [[JBInterfaceControllerContext alloc] init]; 68 | controllerContext.presentingController = presentingController; 69 | controllerContext.index = index; 70 | controllerContext.context = context; 71 | 72 | return controllerContext; 73 | } 74 | 75 | @end 76 | 77 | #pragma mark - JBInterfaceControllerConfigurator class 78 | 79 | @interface JBInterfaceControllerConfigurator () 80 | 81 | @property (nonatomic, readwrite, copy) NSString *name; 82 | @property (nonatomic, readwrite, strong) id context; 83 | @property (nonatomic, readwrite, copy) JBInterfaceControllerConfigureBlock configureBlock; 84 | @property (nonatomic, readwrite, copy) JBInterfaceControllerDismissBlock dismissBlock; 85 | 86 | @end 87 | 88 | @implementation JBInterfaceControllerConfigurator 89 | 90 | + (JBInterfaceControllerConfigurator *)configuratorWithName:(NSString *)name 91 | context:(id)context 92 | configureBlock:(JBInterfaceControllerConfigureBlock)configureBlock 93 | dismissBlock:(JBInterfaceControllerDismissBlock)dismissBlock { 94 | 95 | NSParameterAssert(name && name.length > 0); 96 | 97 | JBInterfaceControllerConfigurator *configurator = [[JBInterfaceControllerConfigurator alloc] init]; 98 | configurator.name = name; 99 | configurator.context = context; 100 | configurator.configureBlock = configureBlock; 101 | configurator.dismissBlock = dismissBlock; 102 | 103 | return configurator; 104 | } 105 | 106 | @end 107 | 108 | #pragma mark - JBInterfaceController class 109 | 110 | static BOOL isInBackground = NO; 111 | 112 | @interface JBInterfaceController () 113 | 114 | @property (nonatomic, readwrite, weak) JBInterfaceController *presentingController; 115 | @property (nonatomic, readwrite, strong) JBInterface *interface; 116 | @property (nonatomic, readwrite, assign, 117 | getter=isVisible) BOOL visible; 118 | @property (nonatomic, readwrite, copy) void (^dismissCompletion)(void); 119 | @property (nonatomic, readwrite, strong) NSPointerArray *presentedControllers; 120 | @property (nonatomic, readwrite, assign) NSUInteger lastActivatedIndex; 121 | @property (nonatomic, readwrite, assign) JBInterfaceControllerPagingDirection pagingDirection; 122 | @property (nonatomic, readwrite, strong) NSArray *configurators; 123 | 124 | @property (nonatomic, readonly, assign) BOOL isMenuBugPossible; 125 | @property (nonatomic, readonly, assign) BOOL isInBackground; 126 | @property (nonatomic, readwrite, weak) JBInterfaceController *lastActivatedController; 127 | 128 | @end 129 | 130 | @implementation JBInterfaceController 131 | 132 | #pragma mark - Class methods 133 | 134 | + (void)initialize { 135 | 136 | if (self == [JBInterfaceController class]) { 137 | 138 | [[NSNotificationCenter defaultCenter] addObserver:self 139 | selector:@selector(extensionHostWillEnterForegroundNotification:) 140 | name:NSExtensionHostWillEnterForegroundNotification 141 | object:nil]; 142 | 143 | [[NSNotificationCenter defaultCenter] addObserver:self 144 | selector:@selector(extensionHostWillResignActiveNotification:) 145 | name:NSExtensionHostWillResignActiveNotification 146 | object:nil]; 147 | } 148 | } 149 | 150 | + (void)extensionHostWillEnterForegroundNotification:(NSNotification *)notification { 151 | 152 | isInBackground = NO; 153 | } 154 | 155 | + (void)extensionHostWillResignActiveNotification:(NSNotification *)notification { 156 | 157 | isInBackground = YES; 158 | } 159 | 160 | #pragma mark - Object management 161 | 162 | - (void)dealloc { 163 | 164 | if (_interface) { 165 | 166 | [_interface removeObserver:self forKeyPath:@"needsUpdate"]; 167 | } 168 | } 169 | 170 | #pragma mark - Methods 171 | 172 | - (void)awakeWithContext:(id)context { 173 | 174 | [super awakeWithContext:context]; // NOTE: WKInterfaceController's implementatation does nothing 175 | 176 | // If we recognize our context, unwrap and process 177 | if ([context isKindOfClass:[JBInterfaceControllerContext class]]) { 178 | 179 | JBInterfaceControllerContext *controllerContext = (JBInterfaceControllerContext *)context; 180 | self.presentingController = controllerContext.presentingController; 181 | NSUInteger index = controllerContext.index; 182 | 183 | [self.presentingController configurePresentedController:self index:index]; 184 | 185 | [self.presentingController presentedController:self 186 | didAwakeWithContext:controllerContext.context]; 187 | } 188 | } 189 | 190 | - (void)willActivate { 191 | 192 | [super willActivate]; // NOTE: WKInterfaceController's implementatation does nothing 193 | 194 | // If we're presenting (and now becoming visible), dismiss our presented controllers 195 | if (self.isPresenting) { 196 | 197 | [self dismissPresentedControllers]; 198 | } 199 | 200 | self.visible = YES; 201 | 202 | [self updateInterfaceIfNeeded]; 203 | 204 | // Called after modal dismiss 205 | if (self.dismissCompletion) { 206 | 207 | self.dismissCompletion(); 208 | self.dismissCompletion = nil; 209 | } 210 | 211 | if (self.presentingController) { 212 | 213 | [self.presentingController prepareNextControllerForActivationWithPresentedController:self]; 214 | [self.presentingController presentedControllerWillActivate:self]; 215 | } 216 | } 217 | 218 | - (void)didDeactivate { 219 | 220 | [super didDeactivate]; // NOTE: WKInterfaceController's implementatation does nothing 221 | 222 | self.visible = NO; 223 | 224 | if (self.presentingController) { 225 | 226 | [self.presentingController presentedControllerDidDeactivate:self]; 227 | } 228 | } 229 | 230 | - (void)didDismiss { 231 | 232 | // Override in subclass 233 | } 234 | 235 | #pragma mark - Properties 236 | 237 | - (BOOL)isMenuBugPossible { 238 | 239 | NSOperatingSystemVersion version = [NSProcessInfo processInfo].operatingSystemVersion; 240 | 241 | // Assume this is fixed after 8.2.0 (but we'll have to change this if that isn't the case) 242 | // 4/12/2015 - The bug still exists in 8.3.0 243 | return ((version.majorVersion == 8 && 244 | version.minorVersion == 2 && 245 | version.patchVersion == 0) || 246 | (version.majorVersion == 8 && 247 | version.minorVersion == 3 && 248 | version.patchVersion == 0));} 249 | 250 | - (JBInterface *)interface { 251 | 252 | if (!_interface) { 253 | 254 | _interface = [[JBInterface alloc] init]; 255 | 256 | [_interface addObserver:self 257 | forKeyPath:@"needsUpdate" 258 | options:NSKeyValueObservingOptionNew 259 | context:nil]; 260 | } 261 | 262 | return _interface; 263 | } 264 | 265 | - (BOOL)isPresenting { 266 | 267 | // While it's simply the inverse of isVisible (at least for now), it's handy mentally 268 | return !_visible; 269 | } 270 | 271 | - (BOOL)isInBackground { 272 | 273 | return isInBackground; 274 | } 275 | 276 | #pragma mark - KVO 277 | 278 | - (void)observeValueForKeyPath:(NSString *)keyPath 279 | ofObject:(id)object 280 | change:(NSDictionary *)change 281 | context:(void *)context { 282 | 283 | if ([keyPath isEqualToString:@"needsUpdate"]) { 284 | 285 | if (self.isVisible) { 286 | 287 | [self updateInterfaceIfNeeded]; 288 | } 289 | } 290 | } 291 | 292 | #pragma mark - Context handling 293 | 294 | - (id)wrappedContext:(id)context index:(NSUInteger)index { 295 | 296 | id wrappedContext = nil; 297 | 298 | if ([context isKindOfClass:[JBInterfaceControllerContext class]]) { 299 | 300 | JBInterfaceControllerContext *controllerContext = (JBInterfaceControllerContext *)context; 301 | controllerContext.presentingController = self; 302 | controllerContext.index = index; 303 | 304 | wrappedContext = controllerContext; 305 | } 306 | else { 307 | 308 | wrappedContext = [JBInterfaceControllerContext contextWithPresentingController:self 309 | index:index 310 | context:context]; 311 | } 312 | 313 | return wrappedContext; 314 | } 315 | 316 | - (NSArray *)wrappedContexts:(NSArray *)contexts { 317 | 318 | NSMutableArray *wrappedContexts = [NSMutableArray array]; 319 | for (NSUInteger index = 0; index < contexts.count; index++) { 320 | 321 | id context = contexts[index]; 322 | 323 | [wrappedContexts addObject:[self wrappedContext:context index:index]]; 324 | } 325 | 326 | return [NSArray arrayWithArray:wrappedContexts]; 327 | } 328 | 329 | - (id)unwrappedContext:(id)context { 330 | 331 | return ([context isKindOfClass:[JBInterfaceControllerContext class]] ? 332 | ((JBInterfaceControllerContext *)context).context : 333 | context); 334 | } 335 | 336 | #pragma mark - Presented controller management 337 | 338 | - (void)configurePresentedController:(JBInterfaceController *)presentedController 339 | index:(NSUInteger)index { 340 | 341 | NSParameterAssert(presentedController); 342 | NSParameterAssert(index < self.presentedControllers.count); 343 | 344 | [self.presentedControllers replacePointerAtIndex:index withPointer:(__bridge void *)(presentedController)]; 345 | 346 | // Have a a configuration block? 347 | if (self.configurators) { 348 | 349 | JBInterfaceControllerConfigurator *configurator = self.configurators[index]; 350 | if (configurator.configureBlock) { 351 | 352 | configurator.configureBlock(presentedController); 353 | 354 | // No need to keep configuration blocks 355 | configurator.configureBlock = nil; 356 | } 357 | } 358 | 359 | // If this is our first controller, give it a chance to prepare 360 | if (index == 0) { 361 | 362 | [presentedController prepareForActivation]; 363 | } 364 | } 365 | 366 | - (NSUInteger)indexOfPresentedController:(JBInterfaceController *)presentedInterfaceController { 367 | 368 | NSParameterAssert(presentedInterfaceController); 369 | 370 | NSUInteger index = NSNotFound; 371 | 372 | for (NSUInteger searchIndex = 0; searchIndex < self.presentedControllers.count; searchIndex++) { 373 | 374 | if (presentedInterfaceController == [self.presentedControllers pointerAtIndex:searchIndex]) { 375 | 376 | index = searchIndex; 377 | break; 378 | } 379 | } 380 | 381 | return index; 382 | } 383 | 384 | - (void)prepareNextControllerForActivationWithPresentedController:(JBInterfaceController *)presentedController { 385 | 386 | NSParameterAssert(presentedController); 387 | 388 | // Let the next interface controller (if we have one) know that it can prepare for activation 389 | 390 | NSUInteger index = [self indexOfPresentedController:presentedController]; 391 | 392 | NSAssert(index != NSNotFound, @"Incorrect presentedControllers state"); 393 | 394 | // Determine direction 395 | self.pagingDirection = (index < self.lastActivatedIndex ? 396 | JBInterfaceControllerPagingDirectionBackwards : 397 | JBInterfaceControllerPagingDirectionForwards); 398 | self.lastActivatedIndex = index; 399 | 400 | // Depending on direction, find the "next" interface controller 401 | id nextController = nil; 402 | if (self.pagingDirection == JBInterfaceControllerPagingDirectionBackwards) { 403 | 404 | if (index > 0) { 405 | 406 | nextController = [self.presentedControllers pointerAtIndex:(index - 1)]; 407 | } 408 | } 409 | else if (self.pagingDirection == JBInterfaceControllerPagingDirectionForwards) { 410 | 411 | if (index < (self.presentedControllers.count - 1)) { 412 | 413 | nextController = [self.presentedControllers pointerAtIndex:(index + 1)]; 414 | } 415 | } 416 | 417 | if (nextController) { 418 | 419 | JBInterfaceController *controller = (JBInterfaceController *)nextController; 420 | 421 | [controller prepareForActivation]; 422 | } 423 | 424 | // Check for modal bug 425 | if (presentedController == self.lastActivatedController) { 426 | 427 | [self informPresentedControllersOfMenuBug]; 428 | } 429 | 430 | self.lastActivatedController = presentedController; 431 | } 432 | 433 | - (void)informPresentedControllersOfMenuBug { 434 | 435 | // 3/26/2015 - While nobody has confirmed when (or even if) this bug will be fixed, we assume it's bad enough that 436 | // it'll be fixed in the next update. If we're wrong, we'll have to issue an update that modifies this 437 | // logic. But honestly, we'd have to issue an update anyway, so this guess is worth it. 438 | 439 | if (self.isMenuBugPossible) { 440 | 441 | // NOTE: willActivate: is called *before* we're informed on the extension notification. So, we can 442 | // use our current state to determine if we've just come from the background. 443 | if (!self.isInBackground) { 444 | 445 | for (JBInterfaceController *controller in self.presentedControllers) { 446 | 447 | [controller fixMenuBug]; 448 | } 449 | } 450 | } 451 | } 452 | 453 | - (void)dismissPresentedControllers { 454 | 455 | for (NSUInteger index = 0; index < self.presentedControllers.count; index++) { 456 | 457 | JBInterfaceController *presentedController = [self.presentedControllers pointerAtIndex:index]; 458 | JBInterfaceControllerConfigurator *configurator = self.configurators[index]; 459 | 460 | if (self.configurators) { 461 | 462 | // Have a a dismiss block? 463 | if (configurator.dismissBlock) { 464 | 465 | configurator.dismissBlock(presentedController); 466 | 467 | // No need to keep dismiss blocks 468 | configurator.dismissBlock = nil; 469 | } 470 | } 471 | 472 | [presentedController didDismiss]; 473 | 474 | // Drop our strong reference 475 | [self.presentedControllers replacePointerAtIndex:index withPointer:nil]; 476 | } 477 | } 478 | 479 | #pragma mark - Presented controller status 480 | 481 | - (void)presentedController:(JBInterfaceController *)presentedController 482 | didAwakeWithContext:(id)context { 483 | 484 | // Override in subclass 485 | } 486 | 487 | - (void)presentedControllerWillActivate:(JBInterfaceController *)presentedController { 488 | 489 | // Override in subclass 490 | } 491 | 492 | - (void)presentedControllerDidDeactivate:(JBInterfaceController *)presentedController { 493 | 494 | // Override in subclass 495 | } 496 | 497 | - (void)presentedControllerDidDismiss:(JBInterfaceController *)presentedController { 498 | 499 | // Override in subclass 500 | } 501 | 502 | - (void)fixMenuBug { 503 | 504 | // Override in subclass to fix menu bug 505 | } 506 | 507 | - (void)prepareForActivation { 508 | 509 | // Override in subclass to perform speculative work (like pre-caching an image) 510 | } 511 | 512 | #pragma mark - Presentation overrides 513 | 514 | - (void)pushControllerWithName:(NSString *)name context:(id)context { 515 | 516 | self.configurators = nil; 517 | [self prepareToPresentControllerCount:1]; 518 | 519 | [super pushControllerWithName:name 520 | context:[self wrappedContext:context index:0]]; 521 | } 522 | 523 | - (void)popController { 524 | 525 | [super popController]; 526 | } 527 | 528 | - (void)popToRootController { 529 | 530 | [super popToRootController]; 531 | } 532 | 533 | - (void)presentControllerWithName:(NSString *)name context:(id)context { 534 | 535 | self.configurators = nil; 536 | [self prepareToPresentControllerCount:1]; 537 | 538 | [super presentControllerWithName:name context:[self wrappedContext:context index:0]]; 539 | } 540 | 541 | - (void)presentControllerWithNames:(NSArray *)names contexts:(NSArray *)contexts { 542 | 543 | self.configurators = nil; 544 | [self prepareToPresentControllerCount:contexts.count]; 545 | 546 | [super presentControllerWithNames:names contexts:[self wrappedContexts:contexts]]; 547 | } 548 | 549 | - (void)dismissController { 550 | 551 | [super dismissController]; 552 | } 553 | 554 | - (void)dismissControllerWithCompletion:(void (^)(void))completion { 555 | 556 | self.dismissCompletion = completion; 557 | 558 | [self dismissController]; 559 | } 560 | 561 | - (void)presentTextInputControllerWithSuggestions:(NSArray *)suggestions 562 | allowedInputMode:(WKTextInputMode)inputMode 563 | completion:(void (^)(NSArray *))completion { 564 | 565 | self.configurators = nil; 566 | [self prepareToPresentControllerCount:1]; 567 | 568 | [super presentTextInputControllerWithSuggestions:suggestions 569 | allowedInputMode:inputMode 570 | completion:completion]; 571 | } 572 | 573 | - (void)dismissTextInputController { 574 | 575 | [super dismissTextInputController]; 576 | } 577 | 578 | - (void)prepareToPresentControllerCount:(NSUInteger)count { 579 | 580 | NSParameterAssert(count > 0); 581 | 582 | self.visible = NO; 583 | 584 | self.presentedControllers = [NSPointerArray strongObjectsPointerArray]; 585 | self.presentedControllers.count = count; 586 | self.lastActivatedIndex = 0; 587 | } 588 | 589 | #pragma mark - Interface update logic 590 | 591 | - (void)updateInterfaceIfNeeded { 592 | 593 | BOOL needsUpdate = self.interface.needsUpdate; 594 | 595 | [self.interface updateIfNeeded]; 596 | 597 | if (needsUpdate) { 598 | 599 | [self didUpdateInterface]; 600 | } 601 | } 602 | 603 | - (void)didUpdateInterface { 604 | 605 | // Override in subclasses. Called after interfaces have been updated. 606 | } 607 | 608 | #pragma mark - Extensions 609 | 610 | - (void)presentControllerWithConfigurator:(JBInterfaceControllerConfigurator *)configurator { 611 | 612 | NSParameterAssert(configurator); 613 | 614 | [self presentControllersWithConfigurators:@[ configurator ]]; 615 | } 616 | 617 | - (void)presentControllersWithConfigurators:(NSArray *)configurators { 618 | 619 | NSParameterAssert(configurators && configurators.count > 0); 620 | 621 | self.configurators = configurators; 622 | [self prepareToPresentControllerCount:configurators.count]; 623 | 624 | NSMutableArray *names = [NSMutableArray array]; 625 | NSMutableArray *contexts = [NSMutableArray array]; 626 | for (JBInterfaceControllerConfigurator *configurator in self.configurators) { 627 | 628 | [names addObject:configurator.name]; 629 | [contexts addObject:configurator.context]; 630 | } 631 | 632 | [super presentControllerWithNames:[NSArray arrayWithArray:names] 633 | contexts:[self wrappedContexts:contexts]]; 634 | } 635 | 636 | @end 637 | 638 | 639 | 640 | -------------------------------------------------------------------------------- /Source/JBInterfaceController_Protected.h: -------------------------------------------------------------------------------- 1 | // 2 | // JBInterfaceController_Protected.h 3 | // 4 | // Created by Mike Swanson on 2/9/2015 5 | // Copyright (c) 2015 Mike Swanson. All rights reserved. 6 | // http://blog.mikeswanson.com 7 | // 8 | // Permission is hereby granted, free of charge, to any person 9 | // obtaining a copy of this software and associated documentation 10 | // files (the "Software"), to deal in the Software without 11 | // restriction, including without limitation the rights to use, 12 | // copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the 14 | // Software is furnished to do so, subject to the following 15 | // conditions: 16 | // 17 | // The above copyright notice and this permission notice shall be 18 | // included in all copies or substantial portions of the Software. 19 | // 20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 22 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 24 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 25 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 26 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 27 | // OTHER DEALINGS IN THE SOFTWARE. 28 | // 29 | 30 | 31 | #import "JBInterfaceController.h" 32 | 33 | @interface JBInterfaceController () 34 | 35 | // Unwrapped context (i.e. stripped of other JBInterfaceControllerContext values) 36 | - (id)unwrappedContext:(id)context; 37 | 38 | // Called when a controller has been dismissed 39 | - (void)didDismiss; 40 | 41 | // Opportunity to workaround bug where showing a menu in a modal causes the status bar to disappear 42 | // See: https://devforums.apple.com/thread/264602?tstart=0 43 | - (void)fixMenuBug; 44 | 45 | @end -------------------------------------------------------------------------------- /Source/JBInterface_Protected.h: -------------------------------------------------------------------------------- 1 | // 2 | // JBInterface_Protected.h 3 | // 4 | // Created by Mike Swanson on 2/9/2015 5 | // Copyright (c) 2015 Mike Swanson. All rights reserved. 6 | // http://blog.mikeswanson.com 7 | // 8 | // Permission is hereby granted, free of charge, to any person 9 | // obtaining a copy of this software and associated documentation 10 | // files (the "Software"), to deal in the Software without 11 | // restriction, including without limitation the rights to use, 12 | // copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the 14 | // Software is furnished to do so, subject to the following 15 | // conditions: 16 | // 17 | // The above copyright notice and this permission notice shall be 18 | // included in all copies or substantial portions of the Software. 19 | // 20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 22 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 24 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 25 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 26 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 27 | // OTHER DEALINGS IN THE SOFTWARE. 28 | // 29 | 30 | #import "JBInterface.h" 31 | 32 | @interface JBInterface () 33 | 34 | @property (nonatomic, readwrite, assign) BOOL needsUpdate; 35 | 36 | @end 37 | --------------------------------------------------------------------------------