NSSavePanel get cancel button object - cocoa

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?

Related

How to wait for completion of sheet after invoking beginSheet:completionHandler:

My code results in the sheet appearing and functioning properly but the invoker receives control back (as per Apple definition) before the sheet ends itself with endSheet.
How can I get the invoker to wait for the return from the end of the sheet processing, so the resultValue is updated.
Invoker:
[self.window beginSheet: sheetController.window
completionHandler:^(NSModalResponse returnCode) {
resultValue = returnCode;
}
];
...
Sheet:
...
[self.window.sheetParent endSheet:self.window returnCode:false];
I thought the new beginSheet: was meant to do the whole linkage, but that isn't the case. All it does is put up the sheet window. The passage and return of control remains as it was. So the following code works:
Invoker:
[self.window beginSheet: sheetController.window
completionHandler:nil
];
returnCodeValue = [NSApp runModalForWindow:sheetController.window];
// Sheet is active until stopModalWithCode issued.
[NSApp endSheet: sheetController.window];
[sheetController.window orderOut:self];
Sheet:
[NSApp stopModalWithCode:whatever];
Looks like you're most of the way there. The return code should be type NSModalResponse (ObjC) or NSApplication.ModalResponse (Swift). I've also found that you need to uncheck 'Visible at Launch'. Otherwise it won't launch as modal.
There was an NSAlert category from way back that used the older (deprecated) methods. I’m not all that great with Objective-C (and worse with Swift), but I’ve been using an updated equivalent for a while in one of my RubyMotion projects. Hopefully I’ve come close with reversing the code conversions, but basically it fires up a modal event loop with runModalForWindow: after calling beginSheetModalForWindow;, and the completionHandler exits with stopModalWithCode:
// NSAlert+SynchronousSheet.h
#import <Cocoa/Cocoa.h>
/* A category to allow NSAlerts to be run synchronously as sheets. */
#interface NSAlert (SynchronousSheet)
/* Runs the receiver modally as a sheet attached to the specified window.
Returns a value positionally identifying the button clicked */
-(NSInteger) runModalSheetForWindow:(NSWindow *)aWindow;
/* Same as above, but runs the receiver modally as a sheet attached to the
main window. */
-(NSInteger) runModalSheet;
#end
// NSAlert+SynchronousSheet.m
#import "NSAlert+SynchronousSheet.h"
#implementation NSAlert (SynchronousSheet)
-(NSInteger) runModalSheetForWindow:(NSWindow *)theWindow {
// Bring up the sheet and wait until it completes.
[self beginSheetModalForWindow:theWindow
completionHandler:^(NSModalResponse returnCode) {
// Get the button pressed - see NSAlert's Button Return Values.
[NSApp stopModalWithCode:returnCode];
}];
[NSApp runModalForWindow:self.window] // fire up the event loop
}
-(NSInteger) runModalSheet {
return [self runModalSheetForWindow:[NSApp mainWindow]];
}
#end
The application will wait for the alert to finish before continuing, with the modal response passed in the result. I don’t know about blocking a couple of thousand lines of code, but I found it useful when chaining two or three sheets, such as an alert before an open/save panel.

xcode tvos app exiting issue when overriding menu button

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

NSComboBoxCell eats mouseUp events

I have an NSComboBox child of an NSPopover. The popover is transient and is configured to dismiss when the user clicks outside its bounds.
When its combobox popup is active and displayed, and when the user mouses down on the popovers owning view, the view receives the mouse down as expected, the popover disappears and the combobox dismisses, however the very next mouseUp is never received by the view.
It turns out the NSComboBoxCell's trackMouse method (tracking loop) is not returning until it receives a mouseUp but unlike the case of mouseDown where it redispatches it nicely to the view that was clicked on, it never propagates the mouseUp.
I had to work around the issue with the following NSComboBoxCell trackMouse override.
Has anyone seen this issue prior or understand what may be going on?
--------
- (BOOL)trackMouse:(NSEvent *)theEvent inRect:(NSRect)cellFrame
ofView:(NSView *)controlView untilMouseUp:(BOOL)untilMouseUp {
BOOL trackingResult = [super trackMouse:theEvent inRect:cellFrame
ofView:controlView untilMouseUp:untilMouseUp];
// If we were dismissed due to mouse event and the current
// event (that NSComboBoxCell is currently eating), just redispatch
// it so it lands with its intended destination.
if (trackingResult) {
NSEvent* currentEvent = [NSApp currentEvent];
if (currentEvent.type == NSLeftMouseUp && currentEvent.window != nil) {
[NSApp postEvent:currentEvent atStart:YES];
}
}
return trackingResult;
}

UIAlertView cancel button return back to first view

I'm using Xcode 4.2 with storyboard.
My storyboard has navigation controller and root view, with a button on root view.
I set the button to push to a second view controller.
On click button, a pop up appears with Ok and Cancel button. If user clicks ok, it goes to second view.
But how to I make the Cancel button to go back to root view?
Here's my code to check on which button is clicked.
-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
if (buttonIndex==0) // cancel button{
[super viewWillAppear:YES]; // to go back to root view but not working
}
}
You Can use this.
maybe can help.
-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
if (buttonIndex==0) // cancel button{
[self dismissModalViewControllerAnimated:YES];
}
In your code , just dismiss the Alert View , when
(buttonIndex==1)

Is there an equivalent technique in Cocoa for the synchronous TrackPopupMenu in Windows?

In response to a rightMouse event I want to call a function that displays a context menu, runs it, and responds to the selected menu item. In Windows I can use TrackPopupMenu with the TPM_RETURNCMD flag.
What is the easiest way to implement this in Cocoa? It seems NSMenu:popUpContextMenu wants to post an event to the specified NSView. Must I create a dummy view and wait for the event before returning? If so, how do I "wait" or flush events given I am not returning to my main ?
The 'proper' way to do this in Cocoa is to have your menu item's target and action perform the required method. However, if you must do it within your initial call, you can use [NSView nextEventMatchingMask:] to continually fetch new events that interest you, handle them, and loop. Here's an example which just waits until the right mouse button is released. You'll probably want to use a more complex mask argument, and continually call [NSView nextEventMatchingMask:] until you get what you want.
NSEvent *localEvent = [[self window] nextEventMatchingMask: NSRightMouseUpMask];
I think you'll find the 'proper' way to go much easier.
It appears that popUpContextMenu is already synchronous. Since I didn't see a way to use NSMenu without having it send a notification to an NSView I came up with a scheme that instantiates a temporary NSView. The goal is to display a popup menu and return the selected item in the context of a single function call. Following is code snippets of my proposed solution:
// Dummy View class used to receive Menu Events
#interface DVFBaseView : NSView
{
NSMenuItem* nsMenuItem;
}
- (void) OnMenuSelection:(id)sender;
- (NSMenuItem*)MenuItem;
#end
#implementation DVFBaseView
- (NSMenuItem*)MenuItem
{
return nsMenuItem;
}
- (void)OnMenuSelection:(id)sender
{
nsMenuItem = sender;
}
#end
// Calling Code (in response to rightMouseDown event in my main NSView
void HandleRButtonDown (NSPoint pt)
{
NSRect graphicsRect; // contains an origin, width, height
graphicsRect = NSMakeRect(200, 200, 50, 100);
//-----------------------------
// Create Menu and Dummy View
//-----------------------------
nsMenu = [[[NSMenu alloc] initWithTitle:#"Contextual Menu"] autorelease];
nsView = [[[DVFBaseView alloc] initWithFrame:graphicsRect] autorelease];
NSMenuItem* item = [nsMenu addItemWithTitle:#"Menu Item# 1" action:#selector(OnMenuSelection:) keyEquivalent:#""];
[item setTag:ID_FIRST];
item = [nsMenu addItemWithTitle:#"Menu Item #2" action:#selector(OnMenuSelection:) keyEquivalent:#""];
[item setTag:ID_SECOND];
//---------------------------------------------------------------------------------------------
// Providing a valid windowNumber is key in getting the Menu to display in the proper location
//---------------------------------------------------------------------------------------------
int windowNumber = [(NSWindow*)myWindow windowNumber];
NSRect frame = [(NSWindow*)myWindow frame];
NSPoint wp = {pt.x, frame.size.height - pt.y}; // Origin in lower left
NSEvent* event = [NSEvent otherEventWithType:NSApplicationDefined
location:wp
modifierFlags:NSApplicationDefined
timestamp: (NSTimeInterval) 0
windowNumber: windowNumber
context: [NSGraphicsContext currentContext]
subtype:0
data1: 0
data2: 0];
[NSMenu popUpContextMenu:nsMenu withEvent:event forView:nsView];
NSMenuItem* MenuItem = [nsView MenuItem];
switch ([MenuItem tag])
{
case ID_FIRST: HandleFirstCommand(); break;
case ID_SECOND: HandleSecondCommand(); break;
}
}
There is no direct equivalent, except in Carbon, which is deprecated.
For detecting the right-click, follow these instructions. They ensure that you will properly detect right-clicks and right-holds and display the menu when you should and not display it when you shouldn't.
For following events, you might try [[NSRunLoop currentRunLoop] runMode:NSEventTrackingRunLoopMode untilDate:[NSDate distantFuture]]. You will need to call this repeatedly until the user has chosen one of the menu items.
Using nextEventMatchingMask:NSRightMouseUpMask will not work in all, or even most, cases. If the user right-clicks on your control, the right mouse button will go up immediately after it goes down, without selecting a menu item, and the menu item selection will probably (though not necessarily) happen through the left mouse button. Better to just run the run loop repeatedly until the user either selects something or dismisses the menu.
I don't know how to tell that the user has dismissed the menu without selecting anything.

Resources