2011-11-08 2 views
11

하나의 진행 막대를 포함하는 창에 시트를 표시하여 Grand Central Dispatch를 사용하여 일부 긴 함수의 진행 상황을 비동기 적으로 표시하려고합니다. 나는 거의 그것을 가지고 있지만, 아마도 내가 runModalForWindow: 또는 이와 유사한 것을 사용하지 않았기 때문에 시트가 초점에있는 것처럼 보이게 할 수 없다.그랜드 센트럴 디스패치를 ​​사용하여 무언가를 처리하는 동안 "진행"시트를 모달로 올바르게 표시하는 방법은 무엇입니까?

이것은 내가 지금 뭘하는지 약, 그것은 메인 창에서 버튼을 눌러의 결과로 발생합니다

// Prepare sheet and show it... 

    [NSApp beginSheet:progressSheet modalForWindow:window modalDelegate:nil didEndSelector:NULL contextInfo:NULL]; 

    [progressSheet makeKeyAndOrderFront:self]; 

    [progressBar setIndeterminate:NO]; 
    [progressBar setDoubleValue:0.f]; 
    [progressBar startAnimation:self]; 


    // Start computation using GCD... 

    dispatch_async(dispatch_get_global_queue(0, 0), ^{ 

     for (int i = 0; i < 1000; i ++) { 
      // Do some large computation here 
      // ... 

      // Update the progress bar which is in the sheet: 
      dispatch_async(dispatch_get_main_queue(), ^{ 
       [progressBar setDoubleValue:(double)i]; 
      }); 
     } 


     // Calculation finished, remove sheet on main thread 

     dispatch_async(dispatch_get_main_queue(), ^{ 
      [progressBar setIndeterminate:YES]; 

      [NSApp endSheet:progressSheet]; 
      [progressSheet orderOut:self]; 
     }); 
    }); 

이 메인 윈도우를 제외하고, 작동 초점 여전히 시트에 초점이 맞지 않고 진행 막대가 애니메이션으로 표시되지 않습니다 (setUsesThreadedAnimation:YES을 사용하지 않은 경우).

제가 생각하는 문제는 비동기 계산을 시작하기 전에 주 스레드를 차단하지 않고 시트를 모달로 실행하는 방법을 모르겠다는 것입니다.

+0

시트를 모달 모드로 실행하면 주 스레드를 차단해서는 안됩니다 (그렇지 않으면 메소드의 시작 부분에있는 '-beginSheet :'비트가 실행 된 후에 아무 것도 나타나지 않습니다). 나는 여기에 내 응용 프로그램에서 거의 동일한 무언가를 사용한다. (진행 상태 표시 줄이있는 모달 시트가 여기에있다.) 그리고 백그라운드에서 실행되는 GCD 블록에서 잘 업데이트된다. 시트가 아래로 미끄러지는 창의 컨트롤이 회색으로 표시되어 윈도우가 시트에 포커스를 잃어 버렸음을 나타내므로 정상적인 것처럼 보입니다. 주 스레드에서 많은 작업을 대기중인 다른 것이있을 수 있습니까? –

+0

@BradLarson 내가 생각할 수있는 유일한 문제는 주 계산의 일부로 'dispatch_apply'를 문제가 발생시킬 수 있다고 생각할 수 있지만 표준 루프로 바꾸면 아무런 차이가 없습니다. 그렇지 않으면 메인 스레드에서 실행되는 것이 거의 없습니다. 시트 뒤에있는 메인 윈도우의 컨트롤과 여전히 상호 작용할 수 있으며 여전히 포커스가 표시됩니다 (즉, 회색으로 표시되지 않음). 예를 들어, 시트가 표시되는 동안 주 창에있는 텍스트 상자는 포커스 링과 함께 나타나고 여전히 텍스트를 입력 할 수 있습니다 (마우스로 텍스트를 선택할 수는 없지만). – Robert

답변

2

정확히 같은 문제가있었습니다. 시행 착오를 통해 해결책을 찾았습니다. 시트의 창은 (a) NSPanel이 아닌 NSWindow (이 것은 중요하지 않음)이며 창에 제목 표시 줄 (사용중인 시트이므로)이 표시되지 않습니다.

그런 이유로 제목 표시 줄이 꺼졌지만 어떻게 든 정확하게 초점을 맞추어야합니다. 제목 표시 줄 확인란을 선택하면 진행률 표시 줄에 초점이 맞춰집니다.

+0

그래, 제 창문에 제목 표시 줄이 없어서 문제가 해결되었습니다. – Robert

4

Brad의 말대로 작동해야합니다.

빠른 테스트를 위해 프로그래밍 방식으로 시트를 만들었습니다. 일반적으로 nib 파일을 사용하지만이 텍스트에 붙여 넣기는 어렵습니다. 아래 코드를 일반적인 Cocoa 윈도우의 버튼에서 호출하면 예상대로 작동합니다. 시트의 텍스트 필드는 First Responder이며, 열려있는 동안 키보드를 입력하면 입력을 받아들입니다. 추악한 시트

#define maxloop 1000 

- (IBAction)startTask:(id)sender 
{ 
    // Prepare sheet and show it... 

    breakLoop = NO; 

    NSRect sheetRect = NSMakeRect(0, 0, 400, 114); 

    NSWindow *progSheet = [[NSWindow alloc] initWithContentRect:sheetRect 
                 styleMask:NSTitledWindowMask 
                 backing:NSBackingStoreBuffered 
                  defer:YES]; 

    NSView *contentView = [[NSView alloc] initWithFrame:sheetRect]; 

    NSProgressIndicator *progInd = [[NSProgressIndicator alloc] initWithFrame:NSMakeRect(143, 74, 239, 20)]; 

    NSTextField *inputField = [[NSTextField alloc] initWithFrame:NSMakeRect(145, 48, 235, 22)]; 

    NSButton *cancelButton = [[NSButton alloc] initWithFrame:NSMakeRect(304, 12, 82, 32)]; 
    cancelButton.bezelStyle = NSRoundedBezelStyle; 
    cancelButton.title = @"Cancel"; 
    cancelButton.action = @selector(cancelTask:); 
    cancelButton.target = self; 

    [contentView addSubview:progInd]; 
    [contentView addSubview:inputField]; 
    [contentView addSubview:cancelButton]; 

    [progSheet setContentView:contentView]; 


    [NSApp beginSheet:progSheet 
     modalForWindow:self.window 
     modalDelegate:nil 
     didEndSelector:NULL 
      contextInfo:NULL]; 

    [progSheet makeKeyAndOrderFront:self]; 

    [progInd setIndeterminate:NO]; 
    [progInd setDoubleValue:0.f]; 
    [progInd startAnimation:self]; 


    // Start computation using GCD... 

    dispatch_async(dispatch_get_global_queue(0, 0), ^{ 

     for (int i = 0; i < maxloop; i++) { 

      [NSThread sleepForTimeInterval:0.01]; 

      if (breakLoop) 
      { 
       break; 
      } 

      // Update the progress bar which is in the sheet: 
      dispatch_async(dispatch_get_main_queue(), ^{ 
       [progInd setDoubleValue: (double)i/maxloop * 100]; 
      }); 
     } 


     // Calculation finished, remove sheet on main thread 

     dispatch_async(dispatch_get_main_queue(), ^{ 
      [progInd setIndeterminate:YES]; 

      [NSApp endSheet:progSheet]; 
      [progSheet orderOut:self]; 
     }); 
    }); 
} 

- (IBAction)cancelTask:(id)sender 
{ 
    NSLog(@"Cancelling"); 
    breakLoop = YES; 
} 

죄송하지만, 떨어져이 코드는 예상대로 작동합니다, 그래서 당신이보고있는 문제가 아마 GCD 관련이 있음을에서.

관련 문제