2012-02-09 30 views
6

Tôi không chắc chắn mình đang làm đúng cách, hoặc nếu tôi có tất cả bị tấn công.Làm thế nào để hiển thị và quản lý một hộp thoại ứng dụng đơn giản trong Cocoa

Tôi có một ứng dụng thử nghiệm thực sự đơn giản (không dựa trên tài liệu) mà tôi đã tạo để tìm hiểu cách làm việc với các hộp thoại ứng dụng theo phương thức trong các ứng dụng Cocoa.

Với dự án ứng dụng "TestModalDialog", tôi có một MainMenu.xib đơn giản với chế độ xem mặc định và nút "Hiển thị hộp thoại", tôi đã thêm. Tôi tạo ra một XIB thứ hai được gọi là TheDialog.xib có nút "Cancel" và "OK". Xib đó là chủ sở hữu của nó một lớp học có nguồn gốc từ NSWindowController gọi là "TheDialogController"; cửa sổ và đại biểu được kết nối với bộ điều khiển.

Chọn "Hiển thị hộp thoại" trên giao diện chính sẽ khởi chạy hộp thoại. Lựa chọn "Hủy" hoặc "OK" sẽ loại bỏ hộp thoại. Đây là mã khá đơn giản:

// TestModalDialogAppDelegate.h 
// TestModalDialog 

#import <Cocoa/Cocoa.h> 

@class TheDialogController; 

@interface TestModalDialogAppDelegate : NSObject <NSApplicationDelegate> 
{ 
    NSWindow *window; 
    TheDialogController* theDialogController; 
} 

@property (assign) IBOutlet NSWindow *window; 
- (IBAction)showDialog:(id)sender; 

@end 

// TestModalDialogAppDelegate.m 
// TestModalDialog 

#import "TestModalDialogAppDelegate.h" 
#import "TheDialogController.h" 

@implementation TestModalDialogAppDelegate 

@synthesize window; 

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification 
{ 
    theDialogController= [[TheDialogController alloc] init]; 
} 

- (void)dealloc 
{ 
    if(nil != theDialogController) 
    [theDialogController release]; 

    [super dealloc]; 
} 

- (IBAction)showDialog:(id)sender 
{ 
    if(nil == theDialogController) 
    { 
    NSAlert* alert= [NSAlert alertWithMessageText:@"Dialog Error" defaultButton:nil alternateButton:nil otherButton:nil informativeTextWithFormat:@"The dialog controller was not allocated."]; 
    [alert runModal]; 
    return; 
    } 

    NSInteger result= [NSApp runModalForWindow:[theDialogController window]]; 
    // Do something with result.... 
} 
@end 

// TheDialogController.h 
// TestModalDialog 

#import <Cocoa/Cocoa.h> 

@interface TheDialogController : NSWindowController 
{ 
    // BOOL userClickedCloseOrOk; // Removed based on answer. 
    // Should declare a common define - just being lazy. 
    NSInteger userClickedOk; // Added based on answer. 
    UInt16 timesShown; 
} 

- (IBAction)showWindow:(id)sender; 
- (IBAction)closeDialog:(id)sender; 
- (IBAction)okDialog:(id)sender; 
//- (BOOL)windowShouldClose:(id)sender; // Removed based on answer. 
- (void)windowWillClose:(NSNotification*)notification; // Added based on answer. 
- (void)windowDidBecomeKey:(NSNotification*)notification; // To set title when modal. 
@end 

// TheDialogController.m 
// TestModalDialog 

#import "TheDialogController.h" 

@implementation TheDialogController 

- (id)init 
{ 
    self = [super initWithWindowNibName:@"TheDialog"]; 

    userClickedOk= 0; // Added based on answer. 
    // userClickedCloseOrOk= FALSE; // Removed based on answer. 

    return self; 
} 

-(void)dealloc 
{ 
    // Do member cleanup if needed. 
    [super dealloc]; 
} 

- (void)windowDidLoad 
{ 
    [super windowDidLoad]; 

    // Initialize as needed.... 
    [[self window] center]; // Center the window. 
} 

// Does not show with runModalForWindow. 
- (IBAction)showWindow:(id)sender 
{ 
    // Just playing with the window title.... 
    ++timesShown; 
    NSString* newTitle= [NSString stringWithFormat:@"Shown %d Times", timesShown]; 
    [[self window] setTitle:newTitle]; 
    return [super showWindow:sender]; 
} 

// This method no longer used for this solution based on the answer. 
//- (BOOL)windowShouldClose:(id)sender 
//{ 
// if(!userClickedCloseOrOk) // The user did not click one of our buttons. 
// [NSApp abortModal]; 
// else 
// userClickedCloseOrOk= FALSE; // Clear for next time. 
// 
// return TRUE; 
//} 

// Added based on answer. 
- (void)windowWillClose:(NSNotification*)notification 
{ 
    [NSApp stopModalWithCode:userClickedOk]; 
    userClickedOk= 0; // Reset for next time. 
} 

// Note - the title will update every time the window becomes key. To do the 
// update only once per modal session, a flag can be added. There might be a better 
// notification to catch. 
- (void)windowDidBecomeKey:(NSNotification*)notification 
{ 
    ++timesShown; 
    NSString* newTitle= [NSString stringWithFormat:@"Shown %d Times", timesShown]; 
    [[self window] setTitle:newTitle]; 
} 

- (IBAction)closeDialog:(id)sender 
{ 
    //userClickedCloseOrOk= TRUE; // Removed based on answer. 
    //[NSApp abortModal]; // Removed based on answer. 
    //[[self window] performClose:self]; // Removed based on answer. 
    [[self window] close]; // Know we want to close - based on answer. 
} 

- (IBAction)okDialog:(id)sender 
{ 
    userClickedOk= 1; // Added based on answer. 
    //userClickedCloseOrOk= TRUE; // Removed based on answer. 
    //[NSApp stopModal]; // Removed based on answer. 
    //[[self window] performClose:self]; // Removed based on answer. 
    [[self window] close]; // Know we want to close - based on answer. 
}  

@end 

tôi đã gặp rắc rối với các phương thức - trước khi tôi đặt trong userClickedCloseOrOk và kiểm tra, nếu người dùng nhấn vào nút close (góc trên bên trái dấu chấm màu đỏ), hộp thoại sẽ đóng nhưng phiên phương thức vẫn đang chạy.

Tôi nhận ra rằng tôi chỉ có thể rời khỏi nút tắt khỏi hộp thoại để bắt đầu, nhưng với nó ở đó, là những gì tôi đã chứng minh một cách tốt để nắm bắt kịch bản đó, hoặc có cách nào tốt hơn không? Hoặc, tôi đang làm điều gì đó sai trái để bắt đầu, điều đó tạo ra vấn đề đó cho tôi?

Mọi lời khuyên sẽ được đánh giá cao.

LƯU Ý - Mã từ ví dụ ban đầu đã nhận xét và được thay thế bằng mã dựa trên câu trả lời. Cũng đã thêm một trình xử lý thông báo mới.

+0

Có cách nào tốt hơn so với windowDidBecomeKey: hoặc windowDidBecomeMain: để biết khi nào cửa sổ đã được hiển thị trong một phiên phương thức? Tôi hỏi vì tôi muốn biết khi nào phiên thực sự bắt đầu. Tôi có thể tạo/phá hủy cửa sổ mỗi lần, và sau đó sử dụng windowDidLoad, nhưng điều đó có vẻ không hiệu quả lắm. Tôi cũng có thể gửi một lá cờ để nói với cửa sổ sắp xuất hiện trong một phiên làm việc, và quản lý nó từ một trong các thông báo - một lần nữa, nó giống như một hack. Hoặc, tôi chỉ nên sử dụng một tờ thay thế? – GTAE86

Trả lời

2

Trong okDialog:closeDialog: phương pháp gọi close thay vì performClose: để tránh cửa sổ gọi phương thức windowShouldClose: đại biểu. Bằng cách đó bạn không cần userClickedCloseOrOk.

Ngoài ra tôi nghĩ bạn muốn gọi số stopModalWithCode: thay vì stopModal vì trong ứng dụng, bạn dường như quan tâm đến kết quả. Và bạn có thể gọi stopModal hoặc stopModalWithCode: thay vì abortModal vì bạn luôn ở trong vòng lặp khi bạn gọi nó (hủy bỏ là khi bạn ở bên ngoài runloop phương thức, như trong chuỗi khác hoặc runloop của bộ hẹn giờ).

Trong windowShouldClose: bạn đang thực hiện một hành động (abortModal) khi bạn chỉ nên trả lời câu hỏi "nếu cửa sổ này đóng". Phương thức ủy quyền windowWillClose: là nơi bạn nên thực hiện hành động nếu cần.

Trang tính hữu ích khi có một cửa sổ và nó cho người dùng biết họ không thể làm bất cứ điều gì với nó cho đến khi họ hoàn thành mọi thứ trong trang tính. Các cửa sổ ứng dụng có ích khi bạn có nhiều cửa sổ mà người dùng không thể tương tác cho đến khi họ hoàn thành bất kỳ thứ gì trong cửa sổ phương thức hoặc nơi có lỗi liên quan đến toàn bộ ứng dụng nhưng không liên quan đến nội dung của một cửa sổ. Trong HIG Apple của họ cho thấy tránh việc sử dụng các cửa sổ ứng dụng phương thức bất cứ khi nào có thể.

+0

Thay đổi từ thực hiệnĐóng để đóng thực sự không thay đổi bất cứ điều gì. Lý do tôi có kiểm tra cho userClickedCloseOrOk trong windowShouldClose là để bắt khi người dùng nhấn nút đóng trên khung (nút đóng tiêu chuẩn). Khi tôi bắt nó, tôi có thể gọi abortModal - hoặc thậm chí stopModal. Nếu không, phiên phương thức không kết thúc và tôi phải ép buộc thoát khỏi ứng dụng. Những gì tôi đang làm có vẻ như một hack, và nó cảm thấy như nó sẽ không cần thiết nếu tôi quản lý các hộp thoại phương thức "đúng" cách - bất cứ điều gì có thể được. Cảm ơn bạn đã làm rõ về abortModal vs. stopModal. – GTAE86

+0

Aaahhh Tôi nghĩ rằng tôi thấy nhiều hơn những gì bạn có ý nghĩa bây giờ - di chuyển các công cụ kết thúc phương thức để windowWillClose, và tôi sẽ không cần nó bất cứ nơi nào khác. Sau đó tôi có thể thiết lập một mã như thích hợp trong okDialog: hoặc closeDialog: trong và chỉ cần gọi stopModalWithCode :. Tốt đẹp. – GTAE86

1

Tôi đã thực sự chỉ phải vật lộn với vấn đề tương tự và thấy liên kết này:

Stopping modal when window is closed (Cocoa)

+0

Các loại trong tĩnh mạch của "Tôi đang làm điều đúng ở nơi đầu tiên". Ban đầu tôi gặp khó khăn với việc liệu tôi có nên sử dụng một hộp thoại hay tờ thông tin hay không. Tôi không thể dễ dàng tìm được câu trả lời hay. Tôi đã vật lộn trong một ngày làm thế nào để có được điều để hành xử. Nó đã đi xuống để tìm ra cách để kết nối các cửa sổ cửa ra vào bộ điều khiển ... và tìm ra câu hỏi để thực sự yêu cầu. – GTAE86

+0

Tôi cũng xem xét việc sử dụng trang tính nhưng tôi muốn cửa sổ của mình có thể di chuyển được và không được đính kèm với cửa sổ chính. Nếu bạn sử dụng OS X exposé với các trang tính, bạn sẽ không thấy cửa sổ phương thức là một cửa sổ phụ. Gọi cho tôi một mọt sách, nhưng tôi không muốn điều đó :) – guitarflow

Các vấn đề liên quan