I am developing an app for the Apple Watch and I would like to use the action demonstrated at the launch event where the user pushes into the screen (rather than a tap). Do you know what this is called and how I can access this?
You can only show a menu on that action. You use addMenuItem methods on WKInterfaceController.
The gesture you are referring to is called a Force Touch.
It is possible to use Force Touch as an input method in third party apps. You cannot register to receive notifications of Force Touch events however; there is nothing equivalent to a UIGestureRecogniser in WatchKit at present.
When you have a contextual menu in the current screen of your WatchKit app, it will automatically be activated by the OS when the user initiates a force touch. You can simulate this in the Apple Watch simulator by a click and hold with the mouse... the resulting animation will make it clear when a Force Touch even has been initiated, even on screens that do not have a contextual menu enabled.
To utilise this via Interface Builder, you simply:
Drag a menu into the relevant Watch App scene in interface builder.
Add between one and four menu items to the menu by dragging those into the menu.
Set names and images for those menu items.
Wire those menu items to IBActions in your WatchKit Extension.
Alternatively, you can set and clear menu items programatically from your WatchKit Extension, as outlined in the WatchKit API documentation. There are four relevant WKInterfaceController methods. In Swift:
func addMenuItemWithItemIcon(_ itemIcon: WKMenuItemIcon,
title title: String,
action action: Selector)
func addMenuItemWithImageNamed(_ imageName: String,
title title: String,
action action: Selector)
func addMenuItemWithImage(_ image: UIImage,
title title: String,
action action: Selector)
func clearAllMenuItems()
In Objective-C:
- (void)addMenuItemWithItemIcon:(WKMenuItemIcon)itemIcon
title:(NSString *)title
action:(SEL)action
- (void)addMenuItemWithImageNamed:(NSString *)imageName
title:(NSString *)title
action:(SEL)action
- (void)addMenuItemWithImage:(UIImage *)image
title:(NSString *)title
action:(SEL)action
- (void)clearAllMenuItems
Full information is in the API documentation for Configuring the Contextual Menu in WatchKit.
The first time you do create one of these menus, particularly if you do it in Interface Builder, it may well feel like you must have missed a step, since you did not have to connect the menu to something equivalent to a force touch gesture recogniser, but when you try it, you'll just find it works. It may well be that this will continue to be the only access third party developers have to force touch, even after we have the ability to make native apps for the Watch later in 2015.
it is called a contextual menu. you can do that using addMenuItem or in storyboards you add a menu which comes with an item, and then add additional items.
Related
Is there a way to customize the context menu that pops op when right clicking on a toolbar (or the title bar of the window containing it)?
Several Apple applications (XCode, Safari, Mail and probably others seem to be able to remove or hide certain menu items), but I can't find a way to access the NSMenu.
Here's what I tried so far:
tried to get to the menu as I did on previous OS X versions (as explained here: How to customize the context menu of NSToolbar?): the returned NSMenu is nil
tried to override rightMouseDown: in the NSWindow containing the toolbar: the method is not called
tried to override menuForEvent: in the window's content view (I replaced the default content view with a custom NSView for this): the method is not called
tried to override rightMouseDown: in the window's content view (I replaced the default content view with a custom NSView for this): the method is not called
Any help would be greatly appreciated.
I actually found the answer by inspecting things in the debugger and tinkering a bit. On El Capitan, a working solution is to get the menu as such:
NSView * theContentView = myWindow.contentView;
NSMenu * theCustomizeMenu = theContentView.superview.menu;
I have a menu item "foobar" that i need to enable on my main window (app delegate), but disable on all other windows. I'd like to know how to do that, because the first responder business is very confusing to me. Here's what i have now:
"foobar" item is connected to first responder's "foobar:" custom action in MainMenu.xib
there is a "foobar:" action in the main app delegate so the menu item is enabled and works
now i load and "makeKeyAndOrderFront" another window
i focus some control on that new window
this is the place where my "foobar" item should be disabled, but it's not
I can see that the "validateMenuItem" is being called in the app delegate, but it's not being called in the second window's controller.
Could someone explain it to me in very simple terms why this is happening and what options i have to solve this?
Thanks for your thoughts
Set your app delegate as delegate of the main window, and implement these method:
- (void)windowDidBecomeKey:(NSNotification *)notification
{
[foobar setEnabled: YES];
}
- (void)windowDidResignKey:(NSNotification *)notification
{
[foobar setEnabled: NO];
}
About first responder
The first responder is a NSControl that is inside the window.For example on a particular window you have two text fields: textField1 and textField2.When you type the input goes just to the first responder (one between all the controls that accept the first responder).So what you need is to know which window is key, you don't need to know who is the first responder in your specific case.
EDIT
There is also another (maybe faster, but depends on personal preferences) way to do it: through interface builder, select the menu item that you want to make enabled only when a certain window is key.Let's suppose that this window is an ivar of the app delegate named window1.Then click on that menu item, go to the bindings inspector, under "enabled" select bind to: app delegate, model key path: self.window.isKeyWindow.
A little image hint:
I'm developing a project in objective-c for ios, and I have a view with multiple tabs using a subclass of UITabBarController. Each tab has it's own UINavigationController. When a view loads on a tab, the appropriate activation events fire (viewWillAppear, viewDidLoad, etc.). However, once you tap on a different tab, and tap back, not all these events will fire again since the view is already the visible view for that specific tab (viewDidLoad for example).
My question is this: is there a notification or delegate that I can simply register for and get notified when the visible view in the window changes? I've done some research and I didn't find anything specific for this. What I plan on doing is:
Check the visible view when the tab bar index changes: tabBarController:didSelectViewController
Register for this event on each navigation controller: navigationController:didShowViewController:animated:
By doing this, I should be notified whenever the visibleViewController changes by either changing the tab, or navigating within the tab's navigation flow (except for modals, in this case, I don't care about them. They are handled already).
Is this the right approach?
Have you looked at UITabBarControllerDelegate? This method sounds what you are looking for:
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController
From the documentation:
In iOS v3.0 and later, the tab bar controller calls this method regardless
of whether the selected view controller changed. In addition, it is called only
in response to user taps in the tab bar and is not called when your code
changes the tab bar contents programmatically.
Here's the link: http://developer.apple.com/library/ios/#documentation/uikit/reference/UITabBarControllerDelegate_Protocol/Reference/Reference.html
Hope that helps!
First implement the UITabBarController delegate method "tabBarController:didSelectViewController" and register for it in the app delegate. You can't register for it in each navigation controller. Only one object can be the delegate. In that method, typecast it to a UINavigationController.
Then get the UIViewController by calling "topViewController" on that UINavigationController. Then call the viewWillAppear: method directly on it.
Short answer is inside the title :)
Explaining: in my MainMenu.xib I have only the Main Menu of the application, that must be same for all NSWindows I open. There is one particular NSWindowController that has, let me say, all answers about when menu item must be enabled (via cases on selector in validateUserInterfaceItem) and what to do with all actions. When NSWindow associated with that NSWindowController is currently focused, there is no problem, but as I focus on another NSWindow all menus are grayed.
I have this flow now: MainMenu is created by reference to it as Main nib into info.plist, then AppDelegate do some init stuff and create MainWinController with MainWindow, at some point MainWinController creates 1+ DetailsWinController with DetailsWindow. AppDelegate manage my custom menu by calling each time functions from MainWinController.
So, how can I force the responder chain to query always that particular NSWindowController (MainWinController)?
You don't give many details to go on, and it isn't clear what you are trying to achieve.
My first reaction is that if you want share Menu items why are you creating multiple windows rather than Views within your MainWindow?
Assuming both your MainWindow and DetailsWindow implement the same selectors you could direct Menu Actions to the First Responder
To add the DetailsWinController in the InterfaceBuilder drag an NSObject from the Object Library, then in Identity Inspector change its class to your DetailsWinController class.
If your Main Menu has different items for the DetailsWindow just connect these to the actions in that instance.
NOTE if you so this you should NOT create the DetailsWinController in code.
If you really want to do this in code you will need to add actions and targets to your menu in code.
The structure of my MainStoryboard is:
->Tab Bar Controller -> Navigation Controller -> View Controller (Search)
The behaviour I want to have is that when the user re-selects the Search tab, the UIScrollView on it scrolls to the top. I am unsure how to get the event from the TabBarController, however.
I've been looking at a lot of stuff about UITabBarDelegate, particularly:
- (void)tabBar:(UITabBar *)tabBar didSelectItem:(UITabBarItem *)item
I have, not quite managed to get this to work properly though. I am very unsure about how to go about setting the delegate (assuming that is the way it's done). I've tried hooking it up in IB, but it wouldn't let me. I also tried to get the UITabBar from the AppDelegate (after looking at some seemingly-related answers).
Any pointers will be greatly appreciated (unless they're null).
UITabBar *aTabBar = [UITabBarItem alloc] init];
....Any other modifications you want to make to aTabBar....
[aTabBar setDelegate:self]
Don't forget to add "<UITabBarDelegate>" to the "#interface" part of whatever object you're trying to designate as the delegate.
For my own code, I usually use some object that isn't the application delegate (as the app delegate is usually meant for application level events like "application is suspending" or "application is coming back into foreground"). If you add "<UITabBarDelegate>" to your Search view controller, make sure that whatever you do with the "didSelectItem" method is applicable only to the Search view controller. Otherwise instantiate some different object if you want to do actions on various view controllers based on which tab bar item is being displayed.