メインウィンドウからサブウィンドウを開く
macOS 10.15.7 / Xcode 11.3.1 / Swift 5.0
メインウィンドウからサブウィンドウを開く一般的な方法について説明する
ウィンドウアプリケーションを作成すると、自動的に AppDelegateクラスが作成され、メインウィンドウを保持する。
まず行うことは、サブウィンドウオブジェクトを xibファイルに作成し、それを制御するウィンドウコントローラを作成することである。
次に AppDelegateクラスに、ウィンドウコントローラを保持し、サブウィンドウを開くときは、ウィンドウコントローラに依頼する。ウィンドウコントローラは、xibファイルからウィンドウオブジェクトをロードし、サブウィンドを開く。サブウィンドウはイベントを受け付け、それに応じた処理を行い、最後にウィンドウを閉じる。
オブジェクトの関係
![[class]](/swift/B12/class.png)
本章では、3種類のサブウィンドウの開き方を紹介する。基本的な処理方式はどれも同じで、使用するメソッドが異なるだけのバリエーションとも言える。
1. モーダルなシートを開く
シートはウィンドウの上辺からにゅっと出てくるサブウィンドウである。シートが表示されている間は、後ろのメインウィンドウをクリックしても反応しない。シートはイベントの受け付けを独占するが、このようなウィンドウをモーダルなウィンドウという。
![[win1_image1]](/swift/B12/win1_image1.png)
画像をクリックすると動画が表示されます。
ウィンドウコントローラクラスの作成
NSWindowControllerクラスを継承するウィンドウコントローラクラス(SubWinController)を作成する。
ウィンドウ(シート)を開くには、下記コードにあるように、windowNibNameプロパティに xibファイル名を設定するだけで良い。ウィンドウコントローラが起動すると、フレームワークは指定した xibファイルからウィンドウオブジェクトを読み込み、メモリにロードする。ウィンドウのオープン/クローズはウィンドウの表示/非表示を切り替えるだけである。
シートを閉じるには、NSWindow.sheetParent.endSheetメソッドを実行する。
ウィンドウオブジェクトの作成
xibファイルにウィンドウオブジェクトを作成する。
![[win1_image2]](/swift/B12/win1_image2.png)
File's Ownerをウィンドウコントローラクラスとする。これは、ウィンドウコントローラがこのウィンドウを所有することを意味する。
ウィンドウコントローラの Windowプロパティとウィンドウオブジェクトを Outlet接続する。イベントプロシージャとなるメソッドとウィンドウオブジェクトのボタンを Action接続する。
ウィンドウの属性の Visible At launch」を falseにする。true(デフォルト)にしておくと、アプリケーション起動時にウィンドウが開いてしまう。
AppDelegateクラスの実装
ウィンドウコントローラ(SubWinControllerクラス)のインスタンスを作成し保持する。
開くボタンのクリックにより、NSWindowクラスの beginSheetメソッドを実行しシートを開く。引数にウィンドウコントローラオブジェクトを指定し、コールバックのクロージャにはシートからの戻り値に応じた処理を記述する。
2. モーダルなサブウィンドウを開く
モーダルなサブウィンドウを開く。表示中はメインウィンドウはイベントを受け付けることができない。
イベントを受け付けるウィンドウは、イベントループ制御を保持している。ウィンドウをモーダル化するとは、メインウィンドウとサブウィンドウの間でイベントループ制御権の受け渡し(一方が放棄し一方が獲得するといったやりとり)を行うことである。
これを実現するには、NSApplicationクラスの runModalメソッドを利用する。フレームワーク寄りの機能を利用するので注意が必要だが、用法を守れば大丈夫でしょう。
![[win2_image1]](/swift/B12/win2_image1.png)
画像をクリックすると動画が表示されます。
ウィンドウコントローラクラスの作成
NSWindowControllerクラスを継承するウィンドウコントローラクラス(SubWinController)を作成する。
closeメソッドによりウィンドウを閉じる。このとき NSApplicationオブジェクトの stopModalメソッドによりイベントループ制御をメインウィンドウに戻さなくてはいけない。ウィンドウコントロールの閉じるボタン(赤バツ)でウィンドウを閉じた場合も同様である。
ウィンドウオブジェクトの作成
xibファイルにウィンドウオブジェクトを作成し、必要な設定を行う。
1. モーダルなシートを開く・ウィンドウオブジェクトの作成と同じなので省略
AppDelegateクラスの実装
ウィンドウコントローラ(SubWinControllerクラス)のインスタンスを作成し保持する。
開くボタンのクリックで NSWindowControllerクラスの showWindowメソッドを実行し、ウィンドウを開く。NSWindowクラスの setFrameOriginメソッドでサブウィンドウの表示位置を指定する。
同時に、NSApplicationクラスの ModalResponseメソッドでイベントループ制御をサブウィンドウに渡す。イベントループ制御がサブウィンドウから戻ると終了のステータスコードが戻るので、それに応じた処理を記述する。
サブウィンドウを特定の位置に配置する
次のコードは、サブウィンドウをメインウィンドウの右横に、上辺を合わせて配置するものである。showWindow(メソッドの直前または直後に実行する。スクリーンの原点は左下になるので、Y座標の決め方がやや面倒。
サブウィンドウをオープンしたときに特定の処理を行う
親ウィンドウ側で行う場合は、showWindowメソッドでサブウィンドウを表示する前後で行う。
サブウィンドウ側で行う場合は、NSWindowDelegateの windowDidBecomeMainメソッドを実装すればよい。
windowDidLoadメソッドは、ウィンドウオブジェクトがロードされたときに起動するメソッドで、最初のウィンドウのオープン時に起動するが、2回目以降のオープン時には起動しない。
3. モードレスなサブウィンドウを開く
モードレスなサブウィンドウを開く。ウィンドウのクリックによりメインウィンドウとサブィンドウを交互にアクティブにすることができる。つまり、サブウィンドウ表示中でもメインウィンドウはイベントを受け付けることができ、ここがモーダルなサブウィンドウの表示と異なるところである。
![[win3_image1]](/swift/B12/win3_image1.png)
画像をクリックすると動画が表示されます。
ウィンドウコントローラクラスの作成
NSWindowControllerクラスを継承するウィンドウコントローラクラス(SubWinController)を作成する。
ウィンドウオブジェクトの作成
xibファイルにウィンドウオブジェクトを作成し、必要な設定を行う。
1. モーダルなシートを開く・ウィンドウオブジェクトの作成と同じなので省略
AppDelegateクラスの実装
ウィンドウコントローラ(SubWinControllerクラス)のインスタンスを作成し保持する。
開くボタンのクリックで、NSWindowクラスの setFrameOriginメソッドでサブウィンドウの表示位置を指定し、NSWindowControllerクラスの showWindowメソッドを実行する。
モードレスなサブウィンドウの場合、戻り値を取得する手段はない。