Objective-C: How to hold the thread until the NSSheet is closed? - objective-c

I am opening an NSWindow as NSSheet and I want to hold the thread until the NSSheet is closed.
_license = [[LicenseArgWindow alloc] initWithWindowNibName:#"LicenseArgWindow"];
[self.window beginSheet:self.license.window completionHandler:^(NSModalResponse returnCode) {}];
[NSApp beginSheet: _license
modalForWindow: _window
modalDelegate: self
didEndSelector: nil
contextInfo: nil];
///here is the problem
///the thread reaches here wheather the nswindow is closed or not
BOOL isAgreed = _license.isAgreed;
How to stop the thread at NSSheet launch and the thread should not pass until the NSWindow is closed
Please help. Many Thanks

Set the didEndSelector parameter in:
[NSApp beginSheet: _license
modalForWindow: _window
modalDelegate: self
didEndSelector: #selector(didEndSheet:returnCode:contextInfo:)
contextInfo: nil];
and have that do the "accepted license" bit:
- (void)didEndSheet:(NSWindow *)sheet
returnCode:(NSInteger)returnCode
contextInfo:(void *)contextInfo
{
[sheet orderOut:self];
BOOL isAgreed = _license.isAgreed;
}
Here is a guide from Apple.

The whole point of sheets is that they are modal for just the one window. The rest of the app has to continue to function. The user can work with other windows or do global actions like open a new file, etc. To block the whole app's execution waiting for the sheet is entirely against the purpose of sheets as a UI mechanism.
If you want an app-modal dialog, run the dialog as a modal window (not sheet). You can either use an NSAlert which has a runModal method, or you can use an arbitrary window using -[NSApplication runModalForWindow:].

Related

Close WindowController Without loosing Data in objective c

So i have a windowController which i pop up using NSApp this window is called from mainWindowController.
ScriptSettingWindowController *mySettings = [[ScriptSettingWindowController alloc]initWithWindowNibName:#"ScriptSettingWindowController" owner:self];
[NSApp runModalForWindow:[mySettings window]];
There are other UIElements which gets displayed only when using Owner:self. When using owner:self it takes over mainWindowController.
enter image description here
When i close the NSApp The control returns to the mainWindowController. But ends up crashing the application. I believe the control has to be returned back to mainWindowController before closing the NSApp.
To close the modal i use
[NSApp stopModal];
[NSApp orderOut:self];
[NSApp close];
but they do not close the popup.
[[self window] close];
[[self window] close]; this does close the window along with some variables being cleared and crashing the app.
Im dealing with an old project here and have no clue about mac development. Any help would be appreciated. thanks in advance

Display a sheet but still let my main app do work

The loadFile method starts a NSTimer to load an process a file over time without blocking the application in a while loop. This timer is not firing with the first bit of code, and is with the second bit. The issue is the second bit shows the sheet as a panel style window, not as a sheet.
How do I get a sheet that can still do work?
Show Sheet but no work is done
[self.sheetView loadFile:filename];
[[NSApplication sharedApplication] beginSheet: self.sheetView
modalForWindow: self.window
modalDelegate: self
didEndSelector: nil
contextInfo: nil];
[[NSApplication sharedApplication] runModalForWindow: self.sheetView];
Show window and work is done
NSModalSession session = [NSApp beginModalSessionForWindow:self.sheetView];
NSInteger result = NSRunContinuesResponse;
[self.sheetView loadFile:filename];
// Loop until some result other than continues:
while (result == NSRunContinuesResponse)
{
// Run the window modally until there are no events to process:
result = [NSApp runModalSession:session];
// Give the main loop some time:
[[NSRunLoop currentRunLoop] limitDateForMode:NSDefaultRunLoopMode];
}
[NSApp endModalSession:session];
Is there a reason why NSThread can't be used? That's the obvious answer ...

Better way to manage lifetime of instance created in class method

In a pet application I'm writing, on the main window, I have a few custom views aligned under each other, each with a label, a combobox and a button.
Clicking the button invokes code that finds the combobox in the same view, and then calls the following function (a class method of RVListEditorController):
+ (void) editComboBox: (NSComboBox *) aComboBox
{
// Analyze says "possible leak", but both methods ending the panel will release the controller.
RVListEditorController *controller = [[RVListEditorController alloc] initWithComboBox: aComboBox];
[NSApp beginSheet: [controller window]
modalForWindow: [aComboBox window]
modalDelegate: controller
didEndSelector: NULL
contextInfo: nil];
}
The code creates an instance of RVListEditorController. That controls a panel that allows me to edit the list in the combobox (remove items, sort items, etc.). It has, among other controls, two buttons that close it, Cancel and OK.
The code for the two buttons is:
- (IBAction) closeSheetWithOK: (id) sender
{
[NSApp endSheet: editingPanel];
[editingPanel orderOut: self];
[comboBoxValues setArray: valuesCopy];
if (comboBoxValues.count > 0)
[editedComboBox setStringValue: [comboBoxValues objectAtIndex: 0]];
[self release];
}
- (IBAction) closeSheetWithCancel: (id) sender
{
[NSApp endSheet: editingPanel];
[editingPanel orderOut: self];
[self release];
}
These are the only two buttons that close the sheet. My question is about the lifetime management of the instance. It is allocated in the class method, but then control is handed to Cocoa again and the class method ends. The only place I could find to release the instance is in the two handlers for the closing buttons. My problem is that beginSheet:modalForWindow:modalDelegate:didEndSelector:contextInfo: doesn't simply open the sheet and then waits until it closes again, returning a value how it was closed. If that were the case, I could close the instance in the class method, and I would feel better.
My question: Is there perhaps a better way to handle the lifetime of the instance, or is there something in Cocoa that allows me to open a sheet window-modally and then wait for it to close again, so I could release the instance right after that? I can't think of any, but I am a relative newbie, after all.
FWIW, the code works, so there are no errors. I am simply not very happy with the construct that I allocate something in a class method that must then be released in two instance methods of itself.
That looks to me like something which should not be a class method and the problems you are having defining its lifecycle are a warning sign that it is being created without clear ownership.
I am simply not very happy with the construct that I allocate something in a class method that must then be released in two instance methods of itself.
There is a certain logic to that - but I would also claim that a window-modal sheet would more naturally be initiated by an instance method. The window is after all a representation of some object, not just a class.
That didn't answer your more general question about life cycles, though.
I managed to get something that worked to my satisfaction. I provided beginSheet: with a method to be called after the sheet ended, giving controller as the context info. IOW:
[NSApp beginSheet: [controller window]
modalForWindow: [aComboBox window]
modalDelegate: controller
didEndSelector: #selector(sheetDidEnd:returnCode:contextInfo)
contextInfo: (void *)controller];
}
The code for the two buttons is now:
- (IBAction) closeSheetWithOK: (id) sender
{
[comboBoxValues setArray: valuesCopy];
if (comboBoxValues.count > 0)
[editedComboBox setStringValue: [comboBoxValues objectAtIndex: 0]];
[NSApp endSheet: editingPanel];
}
- (IBAction) closeSheetWithCancel: (id) sender
{
[NSApp endSheet: editingPanel];
}
and the code for sheetDidEnd:returnCode:contextInfo: is:
- (void) sheetDidEnd: (NSWindow *) sheet returnCode: (NSInteger) returnCode contextInfo: (void *) contextInfo
{
[sheet orderOut: (id)contextInfo];
[(id)contextInfo release];
}
That is, IMO, the best that can be done for situations like this. The procedure would have been the same if this had been called from an instance method of the window controller, AFAICT.

NSWindow windowDidResignKey not getting invoked after redisplaying window

I have a custom NSWindow subclass that the user can toggle the display of with the click of a button. I'd also like the window to disappear when the window resigns key status (e.g. by the user clicking outside the window).
I have a delegate that implements windowDidResignKey: but I find that this delegate method is only invoked the first time the window resigns key.
Here's how I toggle the display of the window (via user action or windowDidResignKey):
- (void) toggleWindowAtPoint:(NSPoint)point
{
// Attach/detach window.
if (!attachedWindow)
{
attachedWindow = [[CustomWindow alloc] attachedToPoint:point];
attachedWindow.delegate = self;
[attachedWindow setLevel:NSMainMenuWindowLevel+1]; // show window in front of all other apps on desktop
[attachedWindow makeKeyAndOrderFront:self];
}
else
{
attachedWindow.delegate = nil;
[attachedWindow orderOut:self];
[attachedWindow release];
attachedWindow = nil;
}
}
Here's my implementation of windowDidResignKey:
- (void) windowDidResignKey:(NSNotification *)note
{
[self toggleWindowAtPoint:NSMakePoint(0, 0)];
}
I'm finding that the first time the custom window is displayed, windowDidResignKey: gets called. Every time the custom window is re-displayed after that, windowDidResignKey: is not getting invoked.
The issue was that in some cases, the custom window was not actually becoming the key window after calling [attachedWindow makeKeyAndOrderFront:self].
I fixed this by adding the following line before re-creating the window:
[[NSApplication sharedApplication] activateIgnoringOtherApps:YES];
In the context of the code snippet above:
- (void) toggleWindowAtPoint:(NSPoint)point
{
// Attach/detach window.
if (!attachedWindow)
{
[[NSApplication sharedApplication] activateIgnoringOtherApps:YES];
attachedWindow = [[CustomWindow alloc] attachedToPoint:point];
....
Have you tried calling [attachedWindow makeFirstResponder:attachedWindow] in your toggle method?
If you want to activate a window without using activateIgnoringOtherApps: you should use a NSPanel with a NSNonactivatingPanelMask:
[[CustomPanel alloc]
initWithContentRect: NSZeroRect
styleMask: NSNonactivatingPanelMask
backing: NSBackingStoreBuffered
defer: NO];

Can't open a sheet on a window twice

I'm opening a sheet on a window , the first time the sheet opens
correctly, but if I close it, and try to open again it doesn't work, I
just get the system alert sound.
- (IBAction) showSpeedSheet:(id)sender
{
[NSApp beginSheet:addEditPackagePanel
modalForWindow:[[NSApp delegate] window]
modalDelegate:nil
didEndSelector:nil
contextInfo:nil];
}
-(IBAction)endSpeedSheet:(id)sender
{
[NSApp endSheet:addEditPackagePanel];
[addEditPackagePanel orderOut:sender];
}
I can't find what's wrong, the app doesn't print any error on the log.
A delegate is not required.
The beep occurs because the system believes there is already a sheet open on the window (whether or not that sheet is technically visible). It's not the greatest error reporting, but that's what it is.
In my code sheets have window controllers and I do both of the following steps in every action that is attached to a sheet-closing button:
[NSApp endSheet:[windowController window]];
[windowController close];
With these steps, subsequent sheets are able to display without beeping.
I think you may need to implement the modal delegate and didEndSelector. The orderOut should be called from the did-end selector.
[NSApp beginSheet:addEditPackagePanel
modalForWindow:[[NSApp delegate] window]
modalDelegate: self
didEndSelector: #selector(didEndSheet:returnCode:contextInfo:)
contextInfo: nil];
and
- (void)didEndSheet:(NSWindow *)sheet returnCode:(NSInteger)returnCode contextInfo:(void*)contextInfo
{
[sheet orderOut:self];
}
I believe control is sent to the did-end selector has soon as endSheet is called.