├── .gitignore ├── LICENSE ├── README.md ├── TDWatchInterfaceMenu.podspec └── TDWatchInterfaceMenu ├── TDWatchInterfaceMenu.h ├── TDWatchInterfaceMenu.m ├── TDWatchInterfaceMenuItem+Private.h ├── TDWatchInterfaceMenuItem.h └── TDWatchInterfaceMenuItem.m /.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 | .DS_Store 20 | 21 | # CocoaPods 22 | # 23 | # We recommend against adding the Pods directory to your .gitignore. However 24 | # you should judge for yourself, the pros and cons are mentioned at: 25 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 26 | # 27 | # Pods/ 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Yu Ao 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TDWatchInterfaceMenu 2 | ![CocoaPods Platform](https://img.shields.io/cocoapods/p/TDWatchInterfaceMenu.svg?style=flat-square) 3 | ![CocoaPods Version](https://img.shields.io/cocoapods/v/TDWatchInterfaceMenu.svg?style=flat-square) 4 | ![CocoaPods License](https://img.shields.io/cocoapods/l/TDWatchInterfaceMenu.svg?style=flat-square) 5 | 6 | ##Usage 7 | 8 | ``` 9 | class InterfaceController: WKInterfaceController { 10 | override func awakeWithContext(context: AnyObject?) { 11 | super.awakeWithContext(context) 12 | 13 | let menuItem = TDWatchInterfaceMenuItem(itemIcon: WKMenuItemIcon.Accept, title: NSLocalizedString("Accept", comment: ""), actionHandler: { (interfaceController, menuItem) -> Void in 14 | println("Accepted!") 15 | }) 16 | 17 | let menu = TDWatchInterfaceMenu(forInterfaceController: self) 18 | menu.addMenuItem(menuItem) 19 | } 20 | } 21 | ``` 22 | 23 | ##Problem to Solve 24 | 25 | `WKInterfaceController` has some methods to add and remove menu items. 26 | ``` 27 | func addMenuItemWithImage(image: UIImage, title: String, action: Selector) 28 | func addMenuItemWithImageNamed(imageName: String, title: String, action: Selector) 29 | func addMenuItemWithItemIcon(itemIcon: WKMenuItemIcon, title: String, action: Selector) 30 | func clearAllMenuItems() 31 | ``` 32 | However, these methods require the `WKInterfaceController` object to be the one that handles the menu action. You cannot specify the target of the action. 33 | 34 | `TDWatchInterfaceMenu` provides a way to add a menu item with a block as the action handler. More importantly, it decouples the menu item from the implementation of interface controller, makes it easier to reuse the menu item or, more commonly, the interface controller. 35 | 36 | ##Contributing 37 | 38 | If you find a bug and know exactly how to fix it, please open a pull request. 39 | 40 | If you can't make the change yourself, please open an issue after making sure that one isn't already logged. 41 | 42 | ##License 43 | 44 | The MIT license 45 | -------------------------------------------------------------------------------- /TDWatchInterfaceMenu.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = 'TDWatchInterfaceMenu' 3 | s.version = '1.2' 4 | s.summary = "Add menu item with a block as it's action handler to `WKInterfaceController`" 5 | s.homepage = "https://github.com/YuAo/TDWatchInterfaceMenu" 6 | s.license = { :type => 'MIT', :file => 'LICENSE' } 7 | s.author = { 'YuAo' => 'me@imyuao.com' } 8 | s.requires_arc = true 9 | s.platform = :ios, '8.0' 10 | s.source_files = 'TDWatchInterfaceMenu/*.{h,m}' 11 | s.source = { :git => "https://github.com/YuAo/TDWatchInterfaceMenu.git", :tag => "1.2" } 12 | end 13 | -------------------------------------------------------------------------------- /TDWatchInterfaceMenu/TDWatchInterfaceMenu.h: -------------------------------------------------------------------------------- 1 | // 2 | // TDWatchInterfaceMenu.h 3 | // Pods 4 | // 5 | // Created by YuAo on 3/23/15. 6 | // 7 | // 8 | 9 | #import 10 | #import 11 | #import 12 | 13 | @interface TDWatchInterfaceMenu : NSObject 14 | 15 | + (instancetype)menuForInterfaceController:(WKInterfaceController *)interfaceController; 16 | 17 | - (void)addMenuItem:(TDWatchInterfaceMenuItem *)menuItem; 18 | - (void)clearAllMenuItems; 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /TDWatchInterfaceMenu/TDWatchInterfaceMenu.m: -------------------------------------------------------------------------------- 1 | // 2 | // TDWatchInterfaceMenu.m 3 | // Pods 4 | // 5 | // Created by YuAo on 3/23/15. 6 | // 7 | // 8 | 9 | #import "TDWatchInterfaceMenu.h" 10 | #import "TDWatchInterfaceMenuItem+Private.h" 11 | #import 12 | 13 | NSString * const TDWatchInterfaceMenuItemActionSelectorPrefix = @"td_watchInterfaceMenuItemAction_"; 14 | 15 | static char TDWKInterfaceControllerWatchInterfaceMenuKey; 16 | 17 | @interface TDWatchInterfaceMenu () 18 | 19 | @property (nonatomic,copy) NSString *identifier; 20 | 21 | @property (nonatomic,weak) WKInterfaceController *interfaceController; 22 | 23 | @property (nonatomic) Class interfaceControllerClass; 24 | 25 | @property (nonatomic,copy) NSArray *menuItems; 26 | 27 | @end 28 | 29 | @implementation TDWatchInterfaceMenu 30 | 31 | - (instancetype)initWithInterfaceController:(WKInterfaceController *)controller { 32 | if (self = [super init]) { 33 | NSParameterAssert(controller); 34 | self.identifier = NSProcessInfo.processInfo.globallyUniqueString; 35 | self.interfaceController = controller; 36 | self.interfaceControllerClass = controller.class; 37 | } 38 | return self; 39 | } 40 | 41 | + (instancetype)menuForInterfaceController:(WKInterfaceController *)interfaceController { 42 | TDWatchInterfaceMenu *menu = objc_getAssociatedObject(interfaceController, &TDWKInterfaceControllerWatchInterfaceMenuKey); 43 | if (!menu) { 44 | menu = [[TDWatchInterfaceMenu alloc] initWithInterfaceController:interfaceController]; 45 | objc_setAssociatedObject(interfaceController, &TDWKInterfaceControllerWatchInterfaceMenuKey, menu, OBJC_ASSOCIATION_RETAIN); 46 | } 47 | return menu; 48 | } 49 | 50 | - (SEL)actionSelectorForMenuItem:(TDWatchInterfaceMenuItem *)menuItem { 51 | return NSSelectorFromString([TDWatchInterfaceMenuItemActionSelectorPrefix stringByAppendingFormat:@"%lx_%lx",(unsigned long)menuItem.identifier.hash,(unsigned long)self.identifier.hash]); 52 | } 53 | 54 | - (void)td_watchInterfaceMenuItemAction_placeholder { 55 | 56 | } 57 | 58 | - (void)addMenuItem:(TDWatchInterfaceMenuItem *)menuItem { 59 | NSParameterAssert(menuItem); 60 | SEL actionSelector = [self actionSelectorForMenuItem:menuItem]; 61 | if (self.interfaceController && ![self.interfaceController respondsToSelector:actionSelector]) { 62 | BOOL added = 63 | class_addMethod(self.interfaceControllerClass, 64 | actionSelector, 65 | imp_implementationWithBlock(^(id receiver){ 66 | if (menuItem.actionHandler) { 67 | menuItem.actionHandler(receiver,menuItem); 68 | } 69 | }), 70 | method_getTypeEncoding(class_getInstanceMethod(self.class, @selector(td_watchInterfaceMenuItemAction_placeholder)))); 71 | if (added) { 72 | [menuItem addToInterfaceController:self.interfaceController actionSelector:actionSelector]; 73 | self.menuItems = [[NSArray arrayWithArray:self.menuItems] arrayByAddingObject:menuItem]; 74 | } 75 | } 76 | } 77 | 78 | - (void)clearAllMenuItems { 79 | for (TDWatchInterfaceMenuItem *menuItem in self.menuItems) { 80 | SEL actionSelector = [self actionSelectorForMenuItem:menuItem]; 81 | Method method = class_getInstanceMethod(self.interfaceControllerClass, actionSelector); 82 | imp_removeBlock(method_getImplementation(method)); 83 | method_setImplementation(method, NULL); 84 | } 85 | [self.interfaceController clearAllMenuItems]; 86 | self.menuItems = nil; 87 | } 88 | 89 | - (void)dealloc { 90 | [self clearAllMenuItems]; 91 | } 92 | 93 | @end 94 | -------------------------------------------------------------------------------- /TDWatchInterfaceMenu/TDWatchInterfaceMenuItem+Private.h: -------------------------------------------------------------------------------- 1 | // 2 | // TDWatchInterfaceMenuItem+Private.h 3 | // Pods 4 | // 5 | // Created by YuAo on 3/23/15. 6 | // 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface TDWatchInterfaceMenuItem (Private) 13 | 14 | - (void)addToInterfaceController:(WKInterfaceController *)interfaceController actionSelector:(SEL)actionSelector; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /TDWatchInterfaceMenu/TDWatchInterfaceMenuItem.h: -------------------------------------------------------------------------------- 1 | // 2 | // TDWatchInterfaceMenuItem.h 3 | // Pods 4 | // 5 | // Created by YuAo on 3/23/15. 6 | // 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @class TDWatchInterfaceMenuItem; 13 | 14 | typedef void (^TDWatchInterfaceMenuItemActionHandler)(WKInterfaceController *interfaceController, TDWatchInterfaceMenuItem *sender); 15 | 16 | @interface TDWatchInterfaceMenuItem : NSObject 17 | 18 | @property (nonatomic,copy,readonly) NSString *identifier; 19 | 20 | @property (nonatomic,copy,readonly) TDWatchInterfaceMenuItemActionHandler actionHandler; 21 | 22 | - (instancetype)initWithImage:(UIImage *)image title:(NSString *)title actionHandler:(TDWatchInterfaceMenuItemActionHandler)actionHandler NS_DESIGNATED_INITIALIZER; 23 | 24 | - (instancetype)initWithImageNamed:(NSString *)imageName title:(NSString *)title actionHandler:(TDWatchInterfaceMenuItemActionHandler)actionHandler NS_DESIGNATED_INITIALIZER; 25 | 26 | - (instancetype)initWithItemIcon:(WKMenuItemIcon)itemIcon title:(NSString *)title actionHandler:(TDWatchInterfaceMenuItemActionHandler)actionHandler NS_DESIGNATED_INITIALIZER; 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /TDWatchInterfaceMenu/TDWatchInterfaceMenuItem.m: -------------------------------------------------------------------------------- 1 | // 2 | // TDWatchInterfaceMenuItem.m 3 | // Pods 4 | // 5 | // Created by YuAo on 3/23/15. 6 | // 7 | // 8 | 9 | #import "TDWatchInterfaceMenuItem.h" 10 | 11 | @interface TDWatchInterfaceMenuItem () 12 | 13 | @property (nonatomic,copy) TDWatchInterfaceMenuItemActionHandler actionHandler; 14 | 15 | @property (nonatomic,copy) void (^actionForAddingMenuItemToInterfaceController)(WKInterfaceController *interfaceController, SEL selector); 16 | 17 | @end 18 | 19 | @implementation TDWatchInterfaceMenuItem 20 | 21 | @synthesize identifier = _identifier; 22 | 23 | - (NSString *)identifier { 24 | if (!_identifier) { 25 | _identifier = NSProcessInfo.processInfo.globallyUniqueString; 26 | } 27 | return _identifier; 28 | } 29 | 30 | - (instancetype)initWithImage:(UIImage *)image title:(NSString *)title actionHandler:(TDWatchInterfaceMenuItemActionHandler)actionHandler { 31 | if (self = [super init]) { 32 | self.actionHandler = actionHandler; 33 | self.actionForAddingMenuItemToInterfaceController = ^(WKInterfaceController *interfaceController, SEL selector) { 34 | [interfaceController addMenuItemWithImage:image title:title action:selector]; 35 | }; 36 | } 37 | return self; 38 | } 39 | 40 | - (instancetype)initWithImageNamed:(NSString *)imageName title:(NSString *)title actionHandler:(TDWatchInterfaceMenuItemActionHandler)actionHandler { 41 | if (self = [super init]) { 42 | self.actionHandler = actionHandler; 43 | self.actionForAddingMenuItemToInterfaceController = ^(WKInterfaceController *interfaceController, SEL selector) { 44 | [interfaceController addMenuItemWithImageNamed:imageName title:title action:selector]; 45 | }; 46 | } 47 | return self; 48 | } 49 | 50 | - (instancetype)initWithItemIcon:(WKMenuItemIcon)itemIcon title:(NSString *)title actionHandler:(TDWatchInterfaceMenuItemActionHandler)actionHandler { 51 | if (self = [super init]) { 52 | self.actionHandler = actionHandler; 53 | self.actionForAddingMenuItemToInterfaceController = ^(WKInterfaceController *interfaceController, SEL selector) { 54 | [interfaceController addMenuItemWithItemIcon:itemIcon title:title action:selector]; 55 | }; 56 | } 57 | return self; 58 | } 59 | 60 | @end 61 | 62 | #import "TDWatchInterfaceMenuItem+Private.h" 63 | 64 | @implementation TDWatchInterfaceMenuItem (Private) 65 | 66 | - (void)addToInterfaceController:(WKInterfaceController *)interfaceController actionSelector:(SEL)actionSelector { 67 | if (self.actionForAddingMenuItemToInterfaceController) { 68 | self.actionForAddingMenuItemToInterfaceController(interfaceController,actionSelector); 69 | } 70 | } 71 | 72 | @end 73 | --------------------------------------------------------------------------------