문제

이와 같은 nsalert를 표시하면 즉시 응답을 얻습니다.

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

문제는 이것이 응용 프로그램 모달이고 내 응용 프로그램은 문서 기반이라는 것입니다. 다음과 같은 시트를 사용하여 현재 문서 창에 경고를 표시합니다.

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;
}

이것의 유일한 문제는 그 것입니다 beginSheetModalForWindow: 바로 반환하여 사용자에게 신뢰할 수 없을 정도로 질문을하고 응답을 기다릴 수 없습니다. 작업을 두 영역으로 나눌 수 있지만 할 수는 없다면 이것은 큰 문제가되지 않습니다.

나는 약 40 개의 다른 물체 (나무에있는)를 처리하는 고리가 있습니다. 하나의 객체가 실패하면 경고가 계속 표시 될지 여부를 표시하고 묻고 싶습니다 (현재 지점에서 처리를 계속할 것인지 (현재 지점에서 처리)) 응용 프로그램 기반이므로 Apple Human Interface 지침은 경고가있을 때 시트를 사용하도록 지시합니다. 문서에만 해당됩니다.

경고 시트를 표시하고 응답을 기다리는 방법은 무엇입니까?

도움이 되었습니까?

해결책

불행히도, 여기서 할 수있는 일은 많지 않습니다. 귀하는 기본적으로 결정을 내려야합니다. 응용 프로그램을 비동기 방식으로 처리하거나 응용 프로그램 모달 경고를 제시하는 비 승인 된 더 이상 사용되지 않은 아키텍처를 사용할 수 있도록 응용 프로그램을 재구성해야합니다.

실제 디자인에 대한 정보와 이러한 객체를 처리하는 방법을 알지 못하면 추가 정보를 제공하기가 어렵습니다. 그러나 내 머리 꼭대기에서 몇 가지 생각이있을 수 있습니다.

  • 어떤 종류의 실행 루프 신호 또는 큐를 통해 메인 스레드와 통신하는 다른 스레드에서 객체를 처리하십시오. 창의 객체 트리가 중단되면 중단 된 기본 스레드를 신호하고 메인 스레드에서해야 할 일에 대한 정보가있는 신호를 기다립니다 (이 분기 또는 중단을 계속하십시오). 그런 다음 기본 스레드는 문서 모달 창을 제시하고 사용자가 수행 할 작업을 선택한 후 프로세스 스레드를 신호합니다.

그러나 이것은 필요한 것에 대해 실제로 과도하게 복합 될 수 있습니다. 이 경우 내 권장 사항은 더 이상 사용되지 않은 사용법을 사용하는 것이지만 실제로 사용자 요구 사항에 따라 다릅니다.

다른 팁

우리는 a 카테고리 켜기 NSAlert 알림을 동기식으로 실행합니다, 응용 프로그램 대체 대화 상자와 마찬가지로 :

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];

코드는이를 통해 사용할 수 있습니다 github, 및 현재 버전은 완전성을 위해 아래에 게시되었습니다.


헤더 파일 NSAlert+SynchronousSheet.h:

#import <Cocoa/Cocoa.h>


@interface NSAlert (SynchronousSheet)

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

@end

구현 파일 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

해결책은 전화하는 것입니다

[NSApp runModalForWindow:alert];

시작 시트 modalforwindow 이후. 또한 "대화 상자가 닫힌"작업을 포착하는 대의원을 구현하고 응답으로 [NSAPP StopModal]을 호출해야합니다.

다음은 문제를 해결하는 NSALERT 범주입니다 (Frederick이 제안한 솔루션과 Laurent P.에서 개선 된 솔루션과 함께 Philipp이 제안한대로 : 대의원 대신 코드 블록을 사용하므로 다시 한 번 단순화됩니다).

@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

누군가가 이것을 찾고있는 경우 (나는 did) 다음과 같이 해결했습니다.

@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

그런 다음 nsalert를 동시에 실행하는 것만 큼 간단합니다.

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

논의 된 바와 같이 재창조 문제의 가능성이 있으므로이를 수행하는 경우 조심하십시오.

내 대답은 다음과 같습니다.

글로벌 클래스 변수 '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;
}

도움이되기를 바랍니다

이것은 위의 Laurent 등의 버전으로, Xcode 6.4의 Swift 1.2 (오늘의 최신 작업 버전)로 변환되고 내 앱에서 테스트되었습니다. 이 작업을 수행하는 데 기여한 모든 분들께 감사드립니다! 애플의 표준 문서는 적어도 내가 찾을 수있는 곳이 아닌 방법에 대한 단서를 주었다.

하나의 미스터리는 나에게 남아 있습니다. 왜 최종 기능에서 이중 느낌표를 사용해야했는지. nsApplication.MainWindow는 선택적 NSWINDOW (NSWINDOW?) 일뿐입니다. 그러나 컴파일러는 두 번째 '!'를 사용할 때까지 오류가 표시되었습니다.

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!!)
    }
}

Windows와 달리 모달 대화 상자를 차단할 수있는 방법이 있다고 생각하지 않습니다. 입력 (예 : 버튼을 클릭하는 사용자)은 기본 스레드에서 처리되므로 차단 방법이 없습니다.

작업을 위해서는 메시지를 스택 위로 전달한 다음 중단 된 곳으로 계속해야합니다.

한 객체가 실패하면 트리의 물체 처리를 중지하고 어떤 객체가 실패했는지 기록하십시오 (순서가 있다고 가정하고 중단 된 곳에서 픽업 할 수 있음). 사용자가 시트를 기각하면 didEndSelector: 메소드는 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;
}

당신이 사용할 수있는 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

도움이되기를 바랍니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top