Frage

Wenn ich ein NSAlert wie diese, ich bekomme Sie die Antwort sofort:

int response;
NSAlert *alert = [NSAlert alertWithMessageText:... ...];
response = [alert runModal];

Das problem ist, dass diese Anwendung modal, und meine Anwendung ist Dokument basiert.Ich habe die Warnung angezeigt, in der aktuellen Dokuments Fenster mit Bogen, wie diese:

int response;
NSAlert *alert = [NSAlert alertWithMessageText:... ...];
[alert beginSheetModalForWindow:aWindow
                  modalDelegate:self
                 didEndSelector:@selector(alertDidEnd:returnCode:contextInfo:)
                    contextInfo:&response];

//elsewhere
- (void) alertDidEnd:(NSAlert *) alert returnCode:(int) returnCode contextInfo:(int *) contextInfo
{
    *contextInfo = returnCode;
}

Das einzige Problem mit diesem ist, dass beginSheetModalForWindow: zurück sofort, so kann ich nicht zuverlässig den Benutzer auffordern, eine Frage und eine Antwort warten.Wäre dies nicht eine große Sache, wenn ich könnte, teilen Sie die Aufgabe in zwei Bereiche, aber ich kann nicht.

Ich habe eine Schleife, die Prozesse über 40 verschiedene Objekte, die (in einer Baumstruktur).Wenn ein Objekt fehlschlägt, möchte ich die Warnung zu zeigen und den Benutzer bitten, ob Sie fortfahren oder Abbrechen (weiter-Verarbeitung in den current-Zweig), aber da meine Anwendung ist Dokument basiert, die Apple Human Interface Richtlinien diktieren verwenden Sie Blätter, wenn der Alarm zu einer bestimmten Dokument.

Wie kann ich die Warnung angezeigt, Blatt und auf eine Antwort warten?

War es hilfreich?

Lösung

Leider, es gibt nicht viel Sie tun können, hier.Sie haben im Grunde eine Entscheidung treffen:überarbeiten Sie Ihre Anwendung so, dass es kann das Objekt in einer asynchronen Art und Weise oder die Verwendung der nicht zugelassenen, veraltete Architektur der Anwendung präsentiert modal Warnungen.

Ohne zu wissen, alle Informationen über Ihre eigentliche design und wie Sie verarbeitet diese Objekte, es ist schwer, keine weiteren Informationen geben.Aus der Spitze von meinem Kopf, aber ein paar Gedanken:

  • Prozess der Objekte in einem anderen thread, der kommuniziert mit der Haupt-thread durch einige Art von run-loop-signal oder queue.Wenn das Fenster " Objekt-Baum unterbrochen wird, signalisiert der Haupt-thread, der es unterbrochen wurde, und wartet auf ein signal von den Haupt-thread mit Informationen darüber, was zu tun ist (weiterhin in diesem Zweig oder Abbrechen).Der Haupt-thread dann zeigt das Dokument-modal-Fenster und Signale, die die Prozess-thread nachdem der Benutzer wählt, was zu tun ist.

Dies kann wirklich kompliziert für das, was Sie brauchen, aber.In diesem Fall meine Empfehlung wäre, gehen Sie einfach mit dem veralteten Gebrauch, aber es hängt wirklich davon ab, Ihrem Benutzer-Anforderungen.

Andere Tipps

Wir erstellten ein Kategorie NSAlert zum ausführen Warnungen synchron, genau wie Anwendung-modale Dialoge:

NSInteger result;

// Run the alert as a sheet on the main window
result = [alert runModalSheet];

// Run the alert as a sheet on some other window
result = [alert runModalSheetForWindow:window];

Der code ist zur Verfügung über GitHub, und die aktuelle version unten geschrieben auf Vollständigkeit.


Header-Datei NSAlert+SynchronousSheet.h:

#import <Cocoa/Cocoa.h>


@interface NSAlert (SynchronousSheet)

-(NSInteger) runModalSheetForWindow:(NSWindow *)aWindow;
-(NSInteger) runModalSheet;

@end

Umsetzung Datei NSAlert+SynchronousSheet.m:

#import "NSAlert+SynchronousSheet.h"


// Private methods -- use prefixes to avoid collisions with Apple's methods
@interface NSAlert ()
-(IBAction) BE_stopSynchronousSheet:(id)sender;   // hide sheet & stop modal
-(void) BE_beginSheetModalForWindow:(NSWindow *)aWindow;
@end


@implementation NSAlert (SynchronousSheet)

-(NSInteger) runModalSheetForWindow:(NSWindow *)aWindow {
    // Set ourselves as the target for button clicks
    for (NSButton *button in [self buttons]) {
        [button setTarget:self];
        [button setAction:@selector(BE_stopSynchronousSheet:)];
    }

    // Bring up the sheet and wait until stopSynchronousSheet is triggered by a button click
    [self performSelectorOnMainThread:@selector(BE_beginSheetModalForWindow:) withObject:aWindow waitUntilDone:YES];
    NSInteger modalCode = [NSApp runModalForWindow:[self window]];

    // This is called only after stopSynchronousSheet is called (that is,
    // one of the buttons is clicked)
    [NSApp performSelectorOnMainThread:@selector(endSheet:) withObject:[self window] waitUntilDone:YES];

    // Remove the sheet from the screen
    [[self window] performSelectorOnMainThread:@selector(orderOut:) withObject:self waitUntilDone:YES];

    return modalCode;
}

-(NSInteger) runModalSheet {
    return [self runModalSheetForWindow:[NSApp mainWindow]];
}


#pragma mark Private methods

-(IBAction) BE_stopSynchronousSheet:(id)sender {
    // See which of the buttons was clicked
    NSUInteger clickedButtonIndex = [[self buttons] indexOfObject:sender];

    // Be consistent with Apple's documentation (see NSAlert's addButtonWithTitle) so that
    // the fourth button is numbered NSAlertThirdButtonReturn + 1, and so on
    NSInteger modalCode = 0;
    if (clickedButtonIndex == NSAlertFirstButtonReturn)
        modalCode = NSAlertFirstButtonReturn;
    else if (clickedButtonIndex == NSAlertSecondButtonReturn)
        modalCode = NSAlertSecondButtonReturn;
    else if (clickedButtonIndex == NSAlertThirdButtonReturn)
        modalCode = NSAlertThirdButtonReturn;
    else
        modalCode = NSAlertThirdButtonReturn + (clickedButtonIndex - 2);

    [NSApp stopModalWithCode:modalCode];
}

-(void) BE_beginSheetModalForWindow:(NSWindow *)aWindow {
    [self beginSheetModalForWindow:aWindow modalDelegate:nil didEndSelector:nil contextInfo:nil];
}

@end

Die Lösung ist der Aufruf

[NSApp runModalForWindow:alert];

nach beginSheetModalForWindow.Auch, Sie implementieren müssen, um einen Delegaten, der die Fänge der "dialog closed" - Aktion, und fordert [NSApp stopModal] in der Antwort.

Hier ist ein NSAlert Kategorie, löst das Problem (wie vorgeschlagen von Philipp mit der vorgeschlagenen Lösung, die von Friedrich und verbessert Laurent P.:Ich verwende einen code-block anstelle einer Stellvertretung, so ist es einfacher, wenn wieder).

@implementation NSAlert (Cat)

-(NSInteger) runModalSheetForWindow:(NSWindow *)aWindow
{
    [self beginSheetModalForWindow:aWindow completionHandler:^(NSModalResponse returnCode)
        { [NSApp stopModalWithCode:returnCode]; } ];
    NSInteger modalCode = [NSApp runModalForWindow:[self window]];
    return modalCode;
}

-(NSInteger) runModalSheet {
    return [self runModalSheetForWindow:[NSApp mainWindow]];
}

@end

Nur falls jemand kommt auf der Suche nach diesem (ich habe es), ich löste dies mit der folgenden:

@interface AlertSync: NSObject {
    NSInteger returnCode;
}

- (id) initWithAlert: (NSAlert*) alert asSheetForWindow: (NSWindow*) window;
- (NSInteger) run;

@end

@implementation AlertSync
- (id) initWithAlert: (NSAlert*) alert asSheetForWindow: (NSWindow*) window {
    self = [super init];

    [alert beginSheetModalForWindow: window
           modalDelegate: self didEndSelector: @selector(alertDidEnd:returnCode:) contextInfo: NULL];

    return self;
}

- (NSInteger) run {
    [[NSApplication sharedApplication] run];
    return returnCode;
}

- (void) alertDidEnd: (NSAlert*) alert returnCode: (NSInteger) aReturnCode {
    returnCode = aReturnCode;
    [[NSApplication sharedApplication] stopModal];
}
@end

Dann läuft eine NSAlert synchron ist so einfach wie:

AlertSync* sync = [[AlertSync alloc] initWithAlert: alert asSheetForWindow: window];
int returnCode = [sync run];
[sync release];

Hinweis: es gibt Potenzial für re-entrancy Themen diskutiert, so werden Sie vorsichtig, wenn dies zu tun.

hier ist meine Antwort:

Erstellen Sie eine Globale Klassenvariable 'NSInteger alertReturnStatus'

- (void)alertDidEndSheet:(NSWindow *)sheet returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo
{
    [[sheet window] orderOut:self];
    // make the returnCode publicly available after closing the sheet
    alertReturnStatus = returnCode;
}


- (BOOL)testSomething
{

    if(2 != 3) {

        // Init the return value
        alertReturnStatus = -1;

        NSAlert *alert = [[[NSAlert alloc] init] autorelease];
        [alert addButtonWithTitle:@"OK"];
        [alert addButtonWithTitle:@"Cancel"];
        [alert setMessageText:NSLocalizedString(@"Warning", @"warning")];
        [alert setInformativeText:@"Press OK for OK"];
        [alert setAlertStyle:NSWarningAlertStyle];
        [alert setShowsHelp:NO];
        [alert setShowsSuppressionButton:NO];

        [alert beginSheetModalForWindow:[self window] modalDelegate:self didEndSelector:@selector(alertDidEndSheet:returnCode:contextInfo:) contextInfo:nil];

        // wait for the sheet
        NSModalSession session = [NSApp beginModalSessionForWindow:[alert window]];
        for (;;) {
            // alertReturnStatus will be set in alertDidEndSheet:returnCode:contextInfo:
            if(alertReturnStatus != -1)
                break;

            // Execute code on DefaultRunLoop
            [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode 
                                     beforeDate:[NSDate distantFuture]];

            // Break the run loop if sheet was closed
            if ([NSApp runModalSession:session] != NSRunContinuesResponse 
                || ![[alert window] isVisible]) 
                break;

            // Execute code on DefaultRunLoop
            [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode 
                                     beforeDate:[NSDate distantFuture]];

        }
        [NSApp endModalSession:session];
        [NSApp endSheet:[alert window]];

        // Check the returnCode by using the global variable alertReturnStatus
        if(alertReturnStatus == NSAlertFirstButtonReturn) {
            return YES;
        }

        return NO;
    }
    return YES;
}

Hoffe, es wird Ihnen helfen, Cheers --Hans

Dies ist die version von Laurent, et al., oben übersetzt in Swift 1.2 für Xcode 6.4 (die Letzte funktionierende version von heute) und getestet in my app.Vielen Dank an alle diejenigen, die beigetragen haben, diese Arbeit zu machen!Die standard-Dokumentation von Apple gab mir keine Hinweise darauf, wie Sie gehen über dieses, zumindest nicht überall, dass ich finden konnte.

Ein Rätsel bleibt für mich:warum musste ich das doppelte Ausrufezeichen in die Letzte Funktion.NSApplication.mainWindow soll nur ein optionales NSWindow (NSWindow?), Recht?Aber der compiler gab der Fehler angezeigt, bis ich benutzte die zweite '!'.

extension NSAlert {
    func runModalSheetForWindow( aWindow: NSWindow ) -> Int {
        self.beginSheetModalForWindow(aWindow) { returnCode in
            NSApp.stopModalWithCode(returnCode)
        }
        let modalCode = NSApp.runModalForWindow(self.window as! NSWindow)
        return modalCode
    }

    func runModalSheet() -> Int {
        // Swift 1.2 gives the following error if only using one '!' below:
        // Value of optional type 'NSWindow?' not unwrapped; did you mean to use '!' or '?'?
        return runModalSheetForWindow(NSApp.mainWindow!!)
    }
}

Im Gegensatz zu Windows, ich glaube nicht, dass es einen Weg gibt, zu blockieren, auf modalen Dialogen.Der Eingang (z.B.die Benutzer auf eine Schaltfläche klickt) verarbeitet werden, auf Ihrem Haupt-thread, so dass es keine Möglichkeit gibt, zu blockieren.

Für Ihre Aufgabe müssen Sie entweder übergeben Sie die Nachricht in den stack und dann weitermachen, wo Sie aufgehört haben.

Wenn ein Objekt fehlschlägt, beenden Sie die Bearbeitung der Objekte in der Struktur, notieren Sie sich das Objekt konnte (vorausgesetzt, es ist ein Auftrag, und Sie können Sie abholen, wo Sie aufgehört haben) und werfen Sie das Blatt.Wenn der Benutzer schließt das Blatt, die didEndSelector: Methode der Bearbeitung beginnen wieder von dem Objekt, dass es aufgehört hat oder nicht, abhängig von der returnCode.

- (bool) windowShouldClose: (id) sender
 {// printf("windowShouldClose..........\n");
  NSAlert *alert=[[NSAlert alloc ]init];
  [alert setMessageText:@"save file before closing?"];
  [alert setInformativeText:@"voorkom verlies van laatste wijzigingen"];
  [alert addButtonWithTitle:@"save"];
  [alert addButtonWithTitle:@"Quit"];
  [alert addButtonWithTitle:@"cancel"];
  [alert beginSheetModalForWindow: _window modalDelegate: self
              didEndSelector: @selector(alertDidEnd: returnCode: contextInfo:)
                 contextInfo: nil];
  return false;
}

Sie können verwenden dispatch_group_wait(group, DISPATCH_TIME_FOREVER);:

dispatch_group_t group = dispatch_group_create();
dispatch_group_enter(group);

NSAlert *alert = [[NSAlert alloc] init];
[alert setMessageText:@"alertMessage"];
[alert addButtonWithTitle:@"Cancel"];
[alert addButtonWithTitle:@"Ok"];

dispatch_async(dispatch_get_main_queue(), ^{
    [alert beginSheetModalForWindow:progressController.window completionHandler:^(NSModalResponse returnCode) {
         if (returnCode == NSAlertSecondButtonReturn) {
             // do something when the user clicks Ok

         } else {
             // do something when the user clicks Cancel
         }

         dispatch_group_leave(group);
     }];
});

dispatch_group_wait(group, DISPATCH_TIME_FOREVER);

//you can continue your code here

Hoffe, das hilft.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top