How do I run modals windows from code with cocoa? - objective-c

I`m trying to run a window as modal directly from the code.
My program starts and a main NSWindows is showed.A thread still running to see if the user has a valid distribution. if he doesn't I need to run a modal.I mean, I have no buttons clicked in the interface.I've designed a NSWindow on the interface builder for a password set, and I want to call it only when my validation is not successful.
I have tested and realized that these methods which are responsible for modal windows running only work in a IBAction environment.
//This doesn't work
-(void) showPasswordWindow
{
[NSApp runModalForWindow:[self window]];
}
//this works But its not useful for me =(
- (IBAction) passwordWindowButton:(id)sender
{
[NSApp runModalForWindow:[self window]];
}
Please, help this newbie =)

One thing to check: are you calling the method on the main thread?
to check, add this to showPasswordWindow
NSLog(#"Main thread? %d", [NSThread isMainThread]);

Related

Closing Window and Releasing NSWindowController

I have a relatively-lengthy task. So I bring up a separate window (NSWindowController) from AppDelegate to show progress. It goes like
//AppDelegate.m
if (self.progresswindow == nil) {
self.progresswindow = [[ProgressController alloc] initWithWindowNibName:#"ProgressController"];
}
[progresswindow showWindow:self];
//[[progresswindow window] setReleasedWhenClosed:NO];
[NSApp runModalForWindow:progresswindow.window];
When a task is complete, the progress window will close itself.
//ProgressController.m
[NSApp stopModal];
[self close];
It works fine. But when I click on a button to start another session of a task with the same window, the application won't run a task although it opens. It appears that the last instance hasn't be released. The progress window has the following lines.
- (void)windowDidLoad {
NSLog(#"Hey!");
}
And NSLog won't be called for the 2nd time. I wonder what I'm doing wrong? Calling setReleasedWhenClosed from AppDelegate has no effect. I have the Release When Closed checkbox enabled, anyway. I read something like I need to observe NSWindowWillCloseNotification the progress window in a different topic so that I can release it when it closes. But I'm using ARC. So I can't manually release it, can I? Meanwhile, if I open Apple's sample (TableViewPlayground), it seems that they use this notification. Furthermore, I've read this topic and this topic. But I don't know what the problem is.
I appreciate any advice. Thank you for your time.
Release the Progress-Window-Controller.

Menu Bar App Never Becomes Reactivated

I'm building a Mac app that only sits in the menu bar with no dock item and no key window and no main menu (it's LSUIElement in the info.plist is set to YES). When I first launch the app, applicationDidBecomeActive: is called, as I expect. However, once another app gains focus, applicationDidBecomeActive: is never called again.
This prevents a text field I have within my app from becoming the first responder. When I first open the app, the text field is editable:
But after another app comes to the foreground, the text field is not editable:
What I've tried:
When the menu is opened, menuWillOpen: is called on the NSMenu's delegate. I've tried placing the following with no success:
[NSApp unhide];
[NSApp arrangeInFront:self];
[NSApp activateIgnoringOtherApps:YES];
[NSApp requestUserAttention:NSCriticalRequest];
[[NSRunningApplication currentApplication] activateWithOptions:NSApplicationActivateIgnoringOtherApps];
[[NSRunningApplication currentApplication] unhide];
I think the issue is probably related to not having any windows to bring to the front. I feel like I'm grasping at straws here. Any help would be greatly appreciated.
I think the issue is with that how the runloop operates when a NSMenu is open, so you should try activating the app before you display the menu. If you're having the NSStatusItem display it, I'd suggest doing it yourself like this:
- (void)toggleMenu:(id)sender
{
// App might already be active
if ([NSApp isActive]) {
[self.statusItem popUpStatusItemMenu:self.menu];
} else {
[NSApp activateIgnoringOtherApps:YES];
}
}
- (void)applicationDidBecomeActive:(NSNotification *)notification
{
[self.statusItem popUpStatusItemMenu:self.menu];
}
That should work, but I think though in general you'll have better luck with an actual window instead of a menu.
You probably need to allow your input to -becomeFirstResponder, maybe by overriding -canBecomeFirstResponder or by calling the become method yourself.
You'd likely have to implement/call these methods for whatever view is housing your text input, or maybe tell your input view to become the first responder.
Either way, it smells like a responder chain issue.
Try calling -makeFirstResponder: on your window. NSWindow is usually the start of the NSResponder chain.
- (void)menuWillOpen:(NSMenu *)menu {
[[NSApp mainWindow] makeFirstResponder:yourTextInputField];
}
I'm assuming your text field already accepts first responder since you said your app launches initially with it as the first responder. If not, make sure your text field overrides -acceptsFirstResponder: to return YES
- (BOOL)acceptsFirstResponder {
return YES;
}
Edit: Ah, see that you don't have a key window. It looks like NSMenu actually has a window associated with it though, and it's safe to call -makeFirstResponder:. Some discussion here suggests overriding -viewDidMoveToWindow: on your view containing your text field in the NSMenu like so:
- (void)viewDidMoveToWindow {
[super viewDidMoveToWindow];
[[self window] makeFirstResponder:yourTextInputField];
}

customize an action of iphone home button to submit score in gamecenter

i have a button in my app a button that submit score to gamecenter and works.
this is the code:
-(void)subScore{
GKScore *scoreRepoter = [[[GKScore alloc] initWithCategory:#"123456"] autorelease];
scoreRepoter.value=100;
[scoreRepoter reportScoreWithCompletionHandler:^(NSError *error) {
if (error!=nil) {
NSLog(#"errr submitting");
}else
NSLog(#"ok!");
}];
now i'd like to submit score before app is closed with home button.
i thought to customize an action of home button (if it is possible)
or perhaps i make the same line of code in viewDidUload...or something like that...
will i be sure that that action will be performed before unloading the app?
i should make that code in dealloc method?
thanks
You can't customize behaviour of Home button directly, but iOS provides some methods in your application's delegate, by which you can control lifecycle of the application.
Method called right before the application goes to background is applicationWillResignActive: in your application's delegate (usually this method is located in AppDelegate.m file).
I think you can get needed effect by calling your method like that:
- (void)applicationWillResignActive:(UIApplication *)application {
[mygame subScore];
}
Also please note that iOS has time limit of execution for this method: you must do all saving-the-game work in less that five seconds or your application will be killed.

App stuck on runModalForWindow

I'm trying to display a modal dialog on top of my app but it's blocking my main app window when it closes. Here's my code:
TutorialWindowController* pTutorialController = [[TutorialWindowController alloc] initWithWindowNibName:#"TutorialWindow"];
NSWindow* pTutorialWindow = [pTutorialController window];
DDLogInfo(#"Tutorial window opening...");
[NSApp runModalForWindow: pTutorialWindow];
DDLogInfo(#"Tutorial window closed!"); // CODE NEVER GETS HERE
[NSApp endSheet: pTutorialWindow];
[pTutorialWindow orderOut: self];
In the modal dialog, my Close button runs this:
- (IBAction)closeButtonPressed:(id)sender {
[NSApp stopModal];
}
The modal dialog displays fine. However, when I click the Close button, the dialog disappears and my app's main window isn't responsive. I hear the bonk every time I try clicking. I'm pretty sure this is because the code never continues after runModalForWindow. Same thing happens if I close the modal dialo using the red X.
What am I doing wrong?
After ordering out the tutorial window, try doing a
[window makeKeyAndOrderFront:self];
on your main window.
You should call [pTutorialWindow orderOut:nil] first.
Not sure about the closeButtonPressed handler. But try adding to the delegate:
- (void) windowWillClose:(NSNotification *)notification
{
// ...
// In there, you should verify that you are calling:
[NSApp stopModal]
}
Adding the stopModal call solved the issue for me.
Verify that the Window delegate in the Interface Editor's Connection Inspector is connected to the File's Owner.
I had several modal dialogues working correctly except for one, and the missing connection was the only difference. Making the connection fixed the problem.

App modal NSPanel / sheet / dialog + NSThread == window hangs?

I'm in the midst of debugging an extremely unusual problem, and I was wondering if anybody might have any insight into what might be going wrong:
In a controller class from a NIB, I take an NSPanel from that same NIB, and then show it app modally on a NSWindow (that was created by hand in code):
[[NSApplication sharedApplication] beginSheet: myPanel
modalForWindow: window
modalDelegate: self
didEndSelector: #selector(sheetDidEnd:returnCode:contextInfo:)
contextInfo: nil];
[[NSApplication sharedApplication] runModalForWindow: myPanel];
Now, when the "finish" button on that sheet is clicked, I run some code to disable some buttons and fire off a thread to make sure the user input is valid (I have to validate with a remote service). This thread is fired from a separate validator object I create:
// controller calls:
[validator validateCreds: creds
notify: #selector(validationComplete:)
onObject: self];
// validator object
validateInfo: (NSDictionary *)parms
notify: (SEL)notifySelector
onObject: (id)notifyObject
{
// build up data with parms and notify info
[[NSThread detachNewThreadSelector: #selector(remotevalidate:)
toTarget: self withObject: data];
}
Next, when the validation is finished, the validator notifies my controller object:
[notifyObject performSelectorOnMainThread: notifySelector
withObject: results waitUntilDone: NO];
And then my controller object, in the method that the validator object calls, kills the dialog:
- (void)validationComplete: (id)data
{
[[NSApplication sharedApplication] stopModal];
[createTwitterPanel orderOut: nil];
[[NSApplication sharedApplication] endSheet: createTwitterPanel
returnCode: NSOKButton];
}
- (void)sheetDidEnd:(NSWindow *)sheet
returnCode:(int)returnCode
contextInfo:(void *)contextInfo
{
m_returnCode = returnCode;
}
My problem: Although the panel is closed / disappears, the top NSApp runModalForWindow: does not exit until some system event is sent to the window that was showing the dialog. Trying to move, resize, or do anything to the window, or otherwise switching away from the application suddenly causes the method to exit and execution to continue. No amount of waiting seems to help, otherwise, however.
I have verified that all methods being invoked on the controller class are all being invoked on the main app thread.
An even more interesting clue is that the dialog has two controls, a WebView, and an NSTextField: Even if I force the exit of runModalForWindow: by clicking on the window, TABbing between the two controls remains screwed up — it simply never works again. It's like my event loop is horked.
I've tried changing validationComplete: to instead post a notification to the main thread, and I've also played with the waitUntilDone on the performSelectorOnMainThread method, all to no effect.
Any ideas? Things I should try looking at?
From the NSApplication documentation:
abortModal must be used instead of
stopModal or stopModalWithCode: when
you need to stop a modal event loop
from anywhere other than a callout
from that event loop. In other words,
if you want to stop the loop in
response to a user’s actions within
the modal window, use stopModal;
otherwise, use abortModal. For
example, use abortModal when running
in a different thread from the
Application Kit’s main thread or when
responding to an NSTimer that you have
added to the NSModalPanelRunLoopMode
mode of the default NSRunLoop.
So, I learned something today.