xcode tvos app exiting issue when overriding menu button - xcode

I am currently writing a tvOS app. I've been detecting and overriding the menu button with tapRecognizer to switch between storyboards and other functions. My issue is when I am on my home screen and press menu it does not exit the app. Instead it remembers the last function I used when overriding the menu button and performs that function. Any thoughts on how to clear the tapRecognizer? Or a function that will exit the app?
I'm overriding the menu button with
in Storyboard1
tapRecognizer = [[UITapGestureRecognizer alloc]initWithTarget:self action:#selector(home)];
tapRecognizer.allowedPressTypes = #[[NSNumber numberWithInteger:UIPressTypeMenu]];
[self.view addGestureRecognizer:tapRecognizer];
in my home subroutine I send the user back to my home page storyboard. But from then on the menu button will not exit the app but send me back to storyboard1.
thanks,
SW

Instead of using your own gesture recognizer, override pressesBegan:
override func pressesBegan(presses: Set<UIPress>, withEvent event: UIPressesEvent?) {
if(presses.first?.type == UIPressType.Menu) {
// handle event
} else {
// perform default action (in your case, exit)
super.pressesBegan(presses, withEvent: event)
}
}

If you are using UIGestureRecognizer instead of responding to presses, all you need to do is to disable the recognizer:
tapRecognizer.enabled = NO;
So if no recognizer with UIPressTypeMenu is listening, tvOS suspends the app and displays the home screen. (I've tested this)

You have to override 2 methods to prevent exiting app by pressing Menu button.
Here is ready-to-use template:
override func pressesBegan(presses: Set<UIPress>, withEvent event: UIPressesEvent?) {
for press in presses {
switch press.type {
case .Menu:
break
default:
super.pressesBegan(presses, withEvent: event)
}
}
}
override func pressesEnded(presses: Set<UIPress>, withEvent event: UIPressesEvent?) {
for press in presses {
switch press.type {
case .Menu:
//Do some staff there!
self.menuButtonPressed()
default:
super.pressesEnded(presses, withEvent: event)
}
}
}

If you overwrite the menu button, the app won't be accepted:
EDIT: You can overwrite, but the menu button has to work as a back button to homescreen from the entry point of the app.
10.1 Details
The Menu button on the Siri Remote does not behave as expected in your
app.
Specifically, when the user launches the app and taps the Menu button
on the Siri remote, the app does not exit to the Apple TV Home screen.
Next Steps
Please revise your app to ensure that the Siri remote buttons behave
as expected and comply with the Apple TV Human Interface Guidelines.

It may be help you...
it is swift code.
let menuPressRecognizer = UITapGestureRecognizer()
menuPressRecognizer.addTarget(self, action: #selector(YourViewController.menuButtonAction(_:)))
menuPressRecognizer.allowedPressTypes = [NSNumber(integer: UIPressType.Menu.hashValue)]
self.view.addGestureRecognizer(menuPressRecognizer)

As per Apple's documentation, for custom press handling, we should override all four of these methods-
- (void)pressesBegan:(NSSet<UIPress *> *)presses withEvent:(nullable UIPressesEvent *)event NS_AVAILABLE_IOS(9_0);
- (void)pressesChanged:(NSSet<UIPress *> *)presses withEvent:(nullable UIPressesEvent *)event NS_AVAILABLE_IOS(9_0);
- (void)pressesEnded:(NSSet<UIPress *> *)presses withEvent:(nullable UIPressesEvent *)event NS_AVAILABLE_IOS(9_0);
- (void)pressesCancelled:(NSSet<UIPress *> *)presses withEvent:(nullable UIPressesEvent *)event NS_AVAILABLE_IOS(9_0);
This is the official documentation from XCode:
Generally, all responders which do custom press handling should
override all four of these methods.
Your responder will receive either pressesEnded:withEvent or pressesCancelled:withEvent: for each
press it is handling (those presses it received in pressesBegan:withEvent:).
pressesChanged:withEvent: will be invoked for presses that provide
an analog value
(like thumbsticks or analog push buttons)
*** You must handle cancelled presses to ensure correct behavior in
your application. Failure to
do so is very likely to lead to incorrect behavior or crashes.

SwiftUI Seekers:
I don't know how much this answer helps, but just adding available actions in SwiftUI.
YourAnyView
.onExitCommand(perform: {
print("onExitCommand")
})
.onMoveCommand { direction in
print("onMoveCommand", direction)
}
REF:
onmovecommand
onexitcommand

Related

Mac Catalyst cannot capture .command key modifier with pressesBegan override

I'm developing a remote desktop control application for iOS (and MacOS through Mac Catalyst) that must be able to capture all keyboard input on the device including the Cmd key (equivalent to Super / Start key on non Mac keyboards) when the app is in the foreground in order to send them to the remote desktop.
I have not yet tried to see if an iOS device with an external keyboard sees the .command key modifier, but when I enabled Mac Catalyst support and installed the app on my Mac and added the following methods to AppDelegate:
override func pressesBegan(_ presses: Set<UIPress>,
with event: UIPressesEvent?) {
super.pressesBegan(presses, with: event)
print(presses.first?.key, presses.first?.key?.modifierFlags)
}
override func pressesEnded(_ presses: Set<UIPress>,
with event: UIPressesEvent?) {
super.pressesEnded(presses, with: event)
print(presses.first?.key, presses.first?.key?.modifierFlags)
}
override func pressesCancelled(_ presses: Set<UIPress>,
with event: UIPressesEvent?) {
super.pressesCancelled(presses, with: event)
print(presses.first?.key, presses.first?.key?.modifierFlags)
}
I was able to capture pretty much any key combination I try except when the Cmd/Start/Super key is also in the key-combination. When the Cmd key is in the key combination or pressed alone, there is absolutely nothing sent to the app. The event appears to be reserved and consumed by Mac OS X completely.
For completeness to this post, I'd like to add that I tried removing all the menus from the app as well just in case the menu was to blame for consuming the Cmd key events, but nothing changed:
override func buildMenu(with builder: UIMenuBuilder) {
if builder.system == .main {
builder.remove(menu: .edit)
builder.remove(menu: .format)
builder.remove(menu: .help)
builder.remove(menu: .file)
builder.remove(menu: .window)
builder.remove(menu: .view)
let dummyCommand = UICommand(title: "Dummy",
action: #selector(dummy),
discoverabilityTitle: "dummy")
let mainMenu = UIMenu(title: "Dummy", image: nil, identifier: UIMenu.Identifier("dummy"), options: .displayInline, children: [dummyCommand])
builder.replace(menu: .application, with: mainMenu)
}
}
I've also tried putting the app into fullscreen mode to no avail.
Any other suggestions on how I can capture the .command modifier?
Next I'm going to try capturing input through the AppKit bundle, but that's not ideal.
Thank you very much!
Hopefully you have an answer already, but in case not:
You will want to inspect the press with code like below
override func pressesBegan(_ presses: Set<UIPress>, with event: UIPressesEvent?) {
var didHandleEvent : Bool = true
print("presses began \(presses.count)")
for press in presses {
guard let key = press.key else { continue }
if key.modifierFlags.contains(.command) {
commandDown = true
print("commandDown")
}
...
You should also only call super.pressesBegan if you want the system to handle the keypress in addition to your code. In your case, I suspect that you don't want the system to do this, so track what presses you have handled, and only if not handled would you call super.
You will also likely want to set your view controller to .becomeFirstResponder()
(assuming you are using a UIVIewController )

How to create a action for UIReturnKeyType.Done in UITextView in xamarin iOS

I am trying to change the UITextView keyboard return button in to Done button.
I need to close the keyboard when I press the Done button.
I have create a UITextView and change the return button in to Done button.
This is my code
PhotoTitle = new UITextView
{
TranslatesAutoresizingMaskIntoConstraints = false, Editable = true, AccessibilityIdentifier = "PhotoTitle",
ReturnKeyType = UIReturnKeyType.Done
}
The keyboard showing Done button successfully. if I press it just behave like return button.
And I can not find a particuler event for fire when tap on Done button.
First of all you have to keep remember that, UITexiView is a scrollable UI widget. So, when you change the keyboard's default Return key in to Done button, you are hiding the opportunity of go to new line inside your UITextView. Therefore my suggestion is, if you are using a UITextView and you need a Done button by keeping the default keyboard, you should add a UIToolBar on top of the keyboard where you can place the Done button.
Since I'm not aware of your real need, which may do not need go to a new line and just need to exit the keyboard when user tap on the Return/Done button, here how you need to do it.
public override void ViewDidLoad()
{
base.ViewDidLoad();
TextView.WeakDelegate = this;
}
[Export("textView:shouldChangeTextInRange:replacementText:")]
public bool ShouldChangeText(UITextView textView, NSRange range, string text)
{
if (text.Equals("\n"))
{
TextView.ResignFirstResponder();
return false;
}
return true;
}
That is quite easy actually subscribe to the ShouldReturn event of the UITextField with a delegate or anonymous method that will call ResignFirstResponder on the field.
You can check this example project that shows how to do it
this.txtDefault.ShouldReturn += (textField) => {
textField.ResignFirstResponder();
return true;
};

How do I reliably get windowDidBecomeMain and windowDidResignMain events?

I have a view controller in which I want to receive windowDidBecomeMain and windowDidResignMain events.
In viewWillApear() I set the window delegate to self.
view.window?.delegate = self
I have added an extension to my view controller that conforms to NSWindowDelegate and have implemented both methods in it thus:
extension CustomerListViewController: NSWindowDelegate
{
func windowDidBecomeMain(_ notification: Notification)
{
print("Customer list did become main")
}
func windowDidResignMain(_ notification: Notification)
{
print("Customer list did resign main")
}
}
This is not the initial window. It is opened via a menu item with a Window controller show segue.
When the window is first opened via the menu item, it does not receive windowDidBecomeMain.
When I click another window it does receive windowDidResignMain.
If I then click back into the newly opened window it does receive windowDidBecomeMain and will from that point on.
I suspect that I need to set my window delegate at a different point but don't have a clue where to do so, if that is the case.

How to stop NSPanel from stealing input of another app

I have an NSPanel. It's purpose is to sit on top of everything in all spaces and it does that just fine.
I don't want it to ever become firstResponder and take focus from another app.
I sub classed NSPanel like so
class SomePanelClass: NSPanel {
override var acceptsFirstResponder: Bool{
return false
}
}
When the window shows or I drag it around the app I was in still appears to have focus and it's name is in the menu but keystrokes do not register in that app until I click back into it's window.
Is there something else I can do to prevent my app and panel from stealing input?
You probably want to override canBecomeKeyWindow on your Panel subclass to return NO.
- (BOOL) canBecomeKeyWindow {
return NO;
}

NSSavePanel get cancel button object

I want to show a popover relative to the "cancel" button of my NSSavePanel but this seem not possible. This is a sample code that explain what I need to do:
// the save panel is called when the _window3 get closed..
[panel beginSheetModalForWindow:_window3 completionHandler:^(NSInteger result)
{
if (result == NSFileHandlingPanelOKButton)
{
// ok, saving my file! (also the app get closed)
}
else
{
/* (the app get closed w/o saving anything ATM)
but... you are really sure????
Since you have done a long job with this app you can also push the button by mistake.. (OMG)
Nice should be to add a Popover relative to the cancel button rect, where the user can see and push
on a "go back" and the "Exit w/o saving" buttons
*/
// normally I call a popover this way:
[_goBackOrExitPopover showRelativeToRect:[cancelBtn bounds] ofView:cancelBtn preferredEdge:NSMinYEdge];
// ... but how to intercept the cancel button (cancelBtn)??
}
}];
How can I find the cancel button object?

Resources