macOSの標準のアラートダイアログを使用しないで,しかし同じような目的のダイアログボックスなどを作ると,親ウィンドウの場所にかかわらず,その中央に出したくなります.
しかし,比較的古いOSでも大丈夫なようにしようとして,とても苦労しました.
座標系のこと
macOSでは,画面表示関係で3種類の座標系もあったりして,NSScreenにまつわる画面左下原点の座標系でコードを書いていたら,どうも出現位置がおかしい.今回書いた関数では,CGWindowの座標系で動くメソッドを使用していて,それらが混ざり合ってめんどくさいことになっていました.
(そもそもNSScreenの左下原点座標系の時点で,各ウィンドウの背景に独自のビットマップを配置したりすると,その画像は左上原点だったりしてとてもめんどくさかった.)
親ウィンドウの位置とサイズがわからないとき
また,親ウィンドウそのもののサイズが取得しづらい場所で,この独自アラートを表示しようとすると,どの位置に表示すればいいかわかりません・・・
というわけで今回作成したメソッドを共有します
入力は,動かしたいウィンドウ(NSWindow *)と,親ウィンドウのタイトル(NSString *)としました.
ウィンドウのタイトルが色々変わるような場合は・・・適当にあしらってください.
/// Move specified window to the center of parent window by the parent's window title text.
/// Note Y coordinate because the NSScreen and CGWindow coordinate systems are opposite.
/// @param targetWindow (NSWindow *) the window to move
/// @param parentWindowName (NSString *) the parent window title to be identified
- (void)moveWindow:(NSWindow *)targetWindow ToCenterOfWindowNamed:(NSString *)parentWindowName{
// Get all the windows
CFArrayRef windowList = CGWindowListCopyWindowInfo(kCGWindowListOptionOnScreenOnly, kCGNullWindowID);
NSArray* arr = CFBridgingRelease(windowList);
// Loop through the windows
for (NSMutableDictionary* entry in arr)
{
if (nil == [entry objectForKey:(id)kCGWindowName])
continue;
NSString *windowName = [[NSString alloc] initWithString:[entry objectForKey:(id)kCGWindowName]];
NSLog(@"Window title = %@", windowName);
if (![parentWindowName isEqualToString:windowName])
continue;
// Screen Height
float sh = [[NSScreen mainScreen] frame].size.height;
// Parent window frame
CGRect pf;
CGRectMakeWithDictionaryRepresentation((CFDictionaryRef)[entry objectForKey:(id)kCGWindowBounds], &pf);
// Child window frame to be moved
CGRect cf = [targetWindow frame];
// Compute destination
float x = pf.origin.x + (pf.size.width - cf.size.width) / 2.0;
float y = sh - (pf.origin.y + pf.size.height) + (pf.size.height - cf.size.height) / 2.0;
// Then move the target window
NSRect frame = [targetWindow frame];
frame.origin = CGPointMake(x, y);
[targetWindow setFrame:frame display:YES];
}
}