├── .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 |
31 |
32 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
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 |
--------------------------------------------------------------------------------