AppKitでファイル選択ダイアログを表示する方法

AppKitのNSOpenPanelクラスを使うと、macOS標準のファイル選択ダイアログを表示できます。ファイル選択ダイアログは、開くファイルを選択するという機能の他に、Sandbox化されたアプリに対して、ファイルシステム上の指定したディレクトリに対するアプリからのアクセス権を与えるという重要な機能があります。

この記事では、AppKitのNSOpenPanelクラスの使い方を解説します。

目次

ファイル選択ダイアログを実行する

NSOpenPanelクラスはNSWindowクラスを継承しているウインドウの一種です。ファイル選択ダイアログを実行するには、次のような流れになります。

  1. ダイアログの設定を行う
  2. runModalメソッドを使ってモーダルダイアログとして実行する。
  3. 選択された項目を取得する。

選択可能なファイルタイプを設定する

選択可能なファイルタイプを設定します。次のプロパティを使用します。

var allowedContentTypes: [UTType] { get set }

allowedContentsTypesに選択可能なファイルタイプをUTIの配列で指定します。例えば、JPEGファイルとPNGファイルを選択可能なら次のようになります。

let openPanel = NSOpenPanel()
openPanel.allowedContentTypes = [.jpeg, .png]

但し、このプロパティはmacOS 11以降でのみ使用可能です。次のプロパティに拡張子の配列を設定します。

var allowedFilesTypes: [String]? { get set }

このプロパティはmacOS 12.0でDeprecatedに指定されているので、次のようにOSによって使い分ける方が適切です。

let openPanel = NSOpenPanel()

if #available(macOS 11.0, *) {
	openPanel.allowedContentTypes = [.jpeg, .png]
} else {
	openPanel.allowedFileTypes = ["jpg", "png"]
}

モーダルダイアログとして実行する

runModalメソッドを実行すると、ファイル選択ダイアログをモーダルダイアログとして実行できます。

func runModal() -> NSApplication.ModalResponse

「Open」ボタンがクリックされると、.OKが返され、「Cancel」ボタンがクリックされると「.cancel」が返されます。次のように戻り値をアプリ側で判定します。

func selectFile() {
    let openPanel = NSOpenPanel()
    
    if #available(macOS 11.0, *) {
        openPanel.allowedContentTypes = [.jpeg, .png]
    } else {
        openPanel.allowedFileTypes = ["jpg", "png"]
    }
    
    let modalResponse = openPanel.runModal()
    if modalResponse == .OK {
        
    }
}

選択されたファイルを取得する

選択されたファイルは次のプロパティに格納されます。

var urls: [URL] { get }

例えば、次のコードは選択されたファイルをコンソールに出力します。

func selectFile() {
    let openPanel = NSOpenPanel()
    
    if #available(macOS 11.0, *) {
        openPanel.allowedContentTypes = [.jpeg, .png]
    } else {
        openPanel.allowedFileTypes = ["jpg", "png"]
    }
    
    let modalResponse = openPanel.runModal()
    if modalResponse == .OK {
        openFiles(urls: openPanel.urls)
    }
}
    
func openFiles(urls: [URL]) {
    for url in urls {
        print("\(url)")
    }
}

デフォルトディレクトリを設定する

ファイル選択ダイアログでデフォルトで表示されるディレクトリは、次のプロパティに設定します。

var directoryURL: URL? { get set }

例えば、次のコードは「ピクチャ」フォルダをデフォルトで表示します。

openPanel.directoryURL = try! FileManager.default.url(for: .picturesDirectory, in: .userDomainMask, appropriateFor: nil, create: false)

ディレクトリを選択する方法

次のプロパティをtrueに設定すると、ディレクトリを選択できるようになります。

var canChooseDirectories: Bool { get set }

また、次のプロパティをfalseにするとファイルを選択できなくします。

var canChooseFiles: Bool { get set }

この2つのプロパティの値を設定すると、ディレクトリだけを選択可能にしたり、ファイルとディレクトリを両方とも選択可能にしたりできます。

複数ファイルを選択する方法

NSOpenPanelはデフォルトでは選択可能なファイルは1つだけですが、次のプロパティをtrueに設定すると、複数のファイルを選択できるようになります。

var allowsMultipleSelection: Bool { get set }

例えば、次のようになります。

func selectFile() {
    let openPanel = NSOpenPanel()
    
    openPanel.allowsMultipleSelection = true
    
    openPanel.directoryURL = try! FileManager.default.url(for: .picturesDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
    
    if #available(macOS 11.0, *) {
        openPanel.allowedContentTypes = [.jpeg, .png]
    } else {
        openPanel.allowedFileTypes = ["jpg", "png"]
    }
    
    let modalResponse = openPanel.runModal()
    if modalResponse == .OK {
        openFiles(urls: openPanel.urls)
    }
}
    
func openFiles(urls: [URL]) {
    for url in urls {
        print("\(url)")
    }
}

Sandboxのエンタイトルメントの設定

Sandbox化されたアプリの場合は、ファイル選択ダイアログでユーザーが選択したファイルや、選択したディレクトリ及びサブディレクトリのみ読み書きが許可されます。そのためには、ダイアログを表示して選択してもらうという動作をアプリ側で実装する他、エンタイトルメントにもその設定が必要です。

アプリのターゲットの設定を開き、「Signing & Capabilities」タブを開き、「App Sandbox」の「File Access」の設定で、次の項目を設定します。

Type 説明
User Selected File ファイル選択ダイアログで選択された項目のアクセス権
Downloads Folder ダウンロードフォルダのアクセス権
Pictures Folder ピクチャフォルダのアクセス権
Music Folder ミュージックフォルダのアクセス権
Movies Folder ムービーフォルダのアクセス権

アクセス権は次の3種類があります。

Permission & Access 説明
None アクセス権無し
Read Only 読み込み専用
Read/Write 読み書き可能
Xcodeのエンタイトルメントの編集

著書紹介

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

Akira Hayashi (林 晃)のアバター Akira Hayashi (林 晃) Representative(代表), Software Engineer(ソフトウェアエンジニア)

アールケー開発代表。Appleプラットフォーム向けの開発を専門としているソフトウェアエンジニア。ソフトウェアの受託開発、技術書執筆、技術指導・セミナー講師。note, Medium, LinkedIn
-
Representative of RK Kaihatsu. Software Engineer Specializing in Development for the Apple Platform. Specializing in contract software development, technical writing, and serving as a tech workshop lecturer. note, Medium, LinkedIn

目次