Mac開發基礎26-NSOpenPanel和NSSavePanel

Mr.陳發表於2024-08-06

NSOpenPanelNSSavePanel 是 macOS 應用中的兩個重要控制元件,分別用於檔案和資料夾的選擇(開啟)以及檔案的儲存(儲存)。

NSOpenPanel

NSOpenPanel 是用於展示系統的開啟檔案對話方塊的類,使用者可以透過它來選擇檔案或資料夾。

基本使用

Objective-C

#import <Cocoa/Cocoa.h>

// 建立並配置 NSOpenPanel
NSOpenPanel *openPanel = [NSOpenPanel openPanel];

// 允許使用者選擇檔案
[openPanel setCanChooseFiles:YES];

// 允許使用者選擇資料夾
[openPanel setCanChooseDirectories:YES];

// 允許多選
[openPanel setAllowsMultipleSelection:YES];

// 設定對話方塊標題
[openPanel setTitle:@"選擇檔案或資料夾"];

// 顯示對話方塊,並非同步處理使用者選擇
[openPanel beginWithCompletionHandler:^(NSModalResponse result) {
    if (result == NSModalResponseOK) {
        // 獲取使用者選擇的 URL 列表
        NSArray<NSURL *> *urls = [openPanel URLs];
        for (NSURL *url in urls) {
            NSLog(@"選擇的檔案/資料夾: %@", url.path);
        }
    } else {
        NSLog(@"使用者取消了選擇");
    }
}];

Swift

import Cocoa

// 建立並配置 NSOpenPanel
let openPanel = NSOpenPanel()

// 允許使用者選擇檔案
openPanel.canChooseFiles = true

// 允許使用者選擇資料夾
openPanel.canChooseDirectories = true

// 允許多選
openPanel.allowsMultipleSelection = true

// 設定對話方塊標題
openPanel.title = "選擇檔案或資料夾"

// 顯示對話方塊,並非同步處理使用者選擇
openPanel.begin { result in
    if result == .OK {
        // 獲取使用者選擇的 URL 列表
        let urls = openPanel.urls
        for url in urls {
            print("選擇的檔案/資料夾: \(url.path)")
        }
    } else {
        print("使用者取消了選擇")
    }
}

配置檔案型別過濾

Objective-C

// 只允許選擇特定型別的檔案(例如:只選擇圖片檔案)
[openPanel setAllowedFileTypes:@[@"jpg", @"png", @"gif"]];

Swift

// 只允許選擇特定型別的檔案(例如:只選擇圖片檔案)
openPanel.allowedFileTypes = ["jpg", "png", "gif"]

指定初始目錄

Objective-C

// 設定初始目錄
[openPanel setDirectoryURL:[NSURL fileURLWithPath:@"/Users/username/Documents"]];

Swift

// 設定初始目錄
openPanel.directoryURL = URL(fileURLWithPath: "/Users/username/Documents")

處理使用者取消操作

Objective-C

[openPanel beginWithCompletionHandler:^(NSModalResponse result) {
    if (result == NSModalResponseOK) {
        // 使用者選擇了檔案或資料夾
    } else {
        // 使用者取消了操作
        NSLog(@"使用者取消了選擇");
    }
}];

Swift

openPanel.begin { result in
    if result == .OK {
        // 使用者選擇了檔案或資料夾
    } else {
        // 使用者取消了操作
        print("使用者取消了選擇")
    }
}

NSSavePanel

NSSavePanel 是用於展示系統的儲存檔案對話方塊的類,使用者可以透過它來選擇儲存檔案的位置和名稱。

基本使用

Objective-C

#import <Cocoa/Cocoa.h>

// 建立並配置 NSSavePanel
NSSavePanel *savePanel = [NSSavePanel savePanel];

// 設定對話方塊標題
[savePanel setTitle:@"儲存檔案"];

// 設定預設檔名
[savePanel setNameFieldStringValue:@"Untitled.txt"];

// 顯示對話方塊,並非同步處理使用者選擇
[savePanel beginWithCompletionHandler:^(NSModalResponse result) {
    if (result == NSModalResponseOK) {
        NSURL *saveURL = [savePanel URL];
        NSLog(@"儲存路徑: %@", saveURL.path);
        
        // 在這裡可以進行檔案儲存操作,例如將資料寫入到 saveURL 所指示的檔案路徑中
    } else {
        NSLog(@"使用者取消了儲存");
    }
}];

Swift

import Cocoa

// 建立並配置 NSSavePanel
let savePanel = NSSavePanel()

// 設定對話方塊標題
savePanel.title = "儲存檔案"

// 設定預設檔名
savePanel.nameFieldStringValue = "Untitled.txt"

// 顯示對話方塊,並非同步處理使用者選擇
savePanel.begin { result in
    if result == .OK {
        if let saveURL = savePanel.url {
            print("儲存路徑: \(saveURL.path)")
            
            // 在這裡可以進行檔案儲存操作,例如將資料寫入到 saveURL 所指示的檔案路徑中
        }
    } else {
        print("使用者取消了儲存")
    }
}

配置檔案型別限制

Objective-C

// 只允許儲存特定型別的檔案(例如:只儲存為文字檔案)
[savePanel setAllowedFileTypes:@[@"txt"]];

Swift

// 只允許儲存特定型別的檔案(例如:只儲存為文字檔案)
savePanel.allowedFileTypes = ["txt"]

指定初始目錄和檔名

Objective-C

// 設定初始目錄
[savePanel setDirectoryURL:[NSURL fileURLWithPath:@"/Users/username/Documents"]];

// 設定預設檔名
[savePanel setNameFieldStringValue:@"MyDocument.txt"];

Swift

// 設定初始目錄
savePanel.directoryURL = URL(fileURLWithPath: "/Users/username/Documents")

// 設定預設檔名
savePanel.nameFieldStringValue = "MyDocument.txt"

強制副檔名

Objective-C

// 強制附加的副檔名
[savePanel setExtensionHidden:NO];

Swift

// 強制附加的副檔名
savePanel.isExtensionHidden = false

封裝工具類

為了更方便地使用 NSOpenPanelNSSavePanel,可以封裝一個工具類,提供常見功能的高層介面。

Objective-C

#import <Cocoa/Cocoa.h>

@interface FileDialogHelper : NSObject

+ (void)showOpenPanelWithTitle:(NSString *)title
               canChooseFiles:(BOOL)canChooseFiles
          canChooseDirectories:(BOOL)canChooseDirectories
        allowsMultipleSelection:(BOOL)allowsMultipleSelection
               allowedFileTypes:(NSArray<NSString *> *)allowedFileTypes
              completionHandler:(void (^)(NSArray<NSURL *> *urls))completionHandler;

+ (void)showSavePanelWithTitle:(NSString *)title
               defaultFileName:(NSString *)defaultFileName
               allowedFileTypes:(NSArray<NSString *> *)allowedFileTypes
              completionHandler:(void (^)(NSURL *url))completionHandler;

@end

@implementation FileDialogHelper

+ (void)showOpenPanelWithTitle:(NSString *)title
               canChooseFiles:(BOOL)canChooseFiles
          canChooseDirectories:(BOOL)canChooseDirectories
        allowsMultipleSelection:(BOOL)allowsMultipleSelection
               allowedFileTypes:(NSArray<NSString *> *)allowedFileTypes
              completionHandler:(void (^)(NSArray<NSURL *> *urls))completionHandler {
    NSOpenPanel *openPanel = [NSOpenPanel openPanel];
    [openPanel setTitle:title];
    [openPanel setCanChooseFiles:canChooseFiles];
    [openPanel setCanChooseDirectories:canChooseDirectories];
    [openPanel setAllowsMultipleSelection:allowsMultipleSelection];
    [openPanel setAllowedFileTypes:allowedFileTypes];
    
    [openPanel beginWithCompletionHandler:^(NSModalResponse result) {
        if (result == NSModalResponseOK) {
            completionHandler([openPanel URLs]);
        } else {
            completionHandler(nil);
        }
    }];
}

+ (void)showSavePanelWithTitle:(NSString *)title
               defaultFileName:(NSString *)defaultFileName
               allowedFileTypes:(NSArray<NSString *> *)allowedFileTypes
              completionHandler:(void (^)(NSURL *url))completionHandler {
    NSSavePanel *savePanel = [NSSavePanel savePanel];
    [savePanel setTitle:title];
    [savePanel setNameFieldStringValue:defaultFileName];
    [savePanel setAllowedFileTypes:allowedFileTypes];
    
    [savePanel beginWithCompletionHandler:^(NSModalResponse result) {
        if (result == NSModalResponseOK) {
            completionHandler([savePanel URL]);
        } else {
            completionHandler(nil);
        }
    }];
}

@end

Swift

import Cocoa

class FileDialogHelper {
    
    // 顯示開啟對話方塊
    static func showOpenPanel(title: String,
                              canChooseFiles: Bool,
                              canChooseDirectories: Bool,
                              allowsMultipleSelection: Bool,
                              allowedFileTypes: [String]?,
                              completionHandler: @escaping ([URL]?) -> Void) {
        let openPanel = NSOpenPanel()
        openPanel.title = title
        openPanel.canChooseFiles = canChooseFiles
        openPanel.canChooseDirectories = canChooseDirectories
        openPanel.allowsMultipleSelection = allowsMultipleSelection
        openPanel.allowedFileTypes = allowedFileTypes
        
        openPanel.begin { result in
            if result == .OK {
                completionHandler(openPanel.urls)
            } else {
                completionHandler(nil)
            }
        }
    }
    
    // 顯示儲存對話方塊
    static func showSavePanel(title: String,
                              defaultFileName: String,
                              allowedFileTypes: [String]?,
                              completionHandler: @escaping (URL?) -> Void) {
        let savePanel = NSSavePanel()
        savePanel.title = title
        savePanel.nameFieldStringValue = defaultFileName
        savePanel.allowedFileTypes = allowedFileTypes
        
        savePanel.begin { result in
            if result == .OK {
                completionHandler(savePanel.url)
            } else {
                completionHandler(nil)
            }
        }
    }
}

使用示例

Objective-C

// 使用示例:顯示開啟對話方塊
[FileDialogHelper showOpenPanelWithTitle:@"選擇檔案或資料夾"
                          canChooseFiles:YES
                     canChooseDirectories:YES
                   allowsMultipleSelection:YES
                          allowedFileTypes:@[@"jpg", @"png"]
                         completionHandler:^(NSArray<NSURL *> *urls) {
    if (urls) {
        for (NSURL *url in urls) {
            NSLog(@"選擇的檔案/資料夾: %@", url.path);
        }
    } else {
        NSLog(@"使用者取消了選擇");
    }
}];

// 使用示例:顯示儲存對話方塊
[FileDialogHelper showSavePanelWithTitle:@"儲存檔案"
                          defaultFileName:@"Untitled.txt"
                          allowedFileTypes:@[@"txt"]
                         completionHandler:^(NSURL *url) {
    if (url) {
        NSLog(@"儲存路徑: %@", url.path);
    } else {
        NSLog(@"使用者取消了儲存");
    }
}];

Swift

// 使用示例:顯示開啟對話方塊
FileDialogHelper.showOpenPanel(title: "選擇檔案或資料夾",
                               canChooseFiles: true,
                               canChooseDirectories: true,
                               allowsMultipleSelection: true,
                               allowedFileTypes: ["jpg", "png"]) { urls in
    if let urls = urls {
        for url in urls {
            print("選擇的檔案/資料夾: \(url.path)")
        }
    } else {
        print("使用者取消了選擇")
    }
}

// 使用示例:顯示儲存對話方塊
FileDialogHelper.showSavePanel(title: "儲存檔案",
                               defaultFileName: "Untitled.txt",
                               allowedFileTypes: ["txt"]) { url in
    if let url = url {
        print("儲存路徑: \(url.path)")
    } else {
        print("使用者取消了儲存")
    }
}

深入探討

自定義皮膚行為

有時候,你可能希望自定義 NSOpenPanelNSSavePanel 的行為,例如在使用者選擇某個檔案或目錄時執行特定的檢查。

Objective-C

// Example of custom delegate for NSOpenPanel
@interface CustomOpenPanelDelegate : NSObject <NSOpenPanelDelegate>
@end

@implementation CustomOpenPanelDelegate

- (BOOL)panel:(id)sender shouldEnableURL:(NSURL *)url {
    // 只允許選擇特定的檔案或目錄
    if ([url.pathExtension isEqualToString:@"txt"]) {
        return YES;
    }
    return NO;
}

@end

// 使用自定義代理
NSOpenPanel *openPanel = [NSOpenPanel openPanel];
CustomOpenPanelDelegate *delegate = [[CustomOpenPanelDelegate alloc] init];
[openPanel setDelegate:delegate];

Swift

// Example of custom delegate for NSOpenPanel
class CustomOpenPanelDelegate: NSObject, NSOpenPanelDelegate {
    func panel(_ sender: Any, shouldEnable url: URL) -> Bool {
        // 只允許選擇特定的檔案或目錄
        if url.pathExtension == "txt" {
            return true
        }
        return false
    }
}

// 使用自定義代理
let openPanel = NSOpenPanel()
let delegate = CustomOpenPanelDelegate()
openPanel.delegate = delegate

皮膚定製介面

你可以在 NSOpenPanelNSSavePanel 中新增額外的使用者介面元素,例如文字欄位或核取方塊。

Objective-C

NSOpenPanel *openPanel = [NSOpenPanel openPanel];

// 建立自定義檢視,例如一個 NSTextField
NSTextField *textField = [[NSTextField alloc] initWithFrame:NSMakeRect(0, 0, 200, 24)];
[textField setStringValue:@"請輸入額外資訊"];

// 新增自定義檢視到皮膚
[openPanel setAccessoryView:textField];

Swift

let openPanel = NSOpenPanel()

// 建立自定義檢視,例如一個 NSTextField
let textField = NSTextField(frame: NSRect(x: 0, y: 0, width: 200, height: 24))
textField.stringValue = "請輸入額外資訊"

// 新增自定義檢視到皮膚
openPanel.accessoryView = textField

總結

透過了解 NSOpenPanelNSSavePanel 的基本使用、配置檔案型別和初始目錄、處理使用者操作、自定義皮膚行為以及新增自定義介面等技巧,並封裝工具類,將能夠更高效地使用這兩個類建立和管理檔案選擇和儲存對話方塊。

相關文章