よくあるエラーに関して。
The document “***.h” could not be saved. You don’t have permission.
おそらく、システムのヘッダを書き換えるように fix しちゃったんだと思う。
キャッシュを消したいので、Xcode 自体を強制終了しましょう。再インストールまでは必要ないです。


ウマ娘関連のネタが多いでしょうか
よくあるエラーに関して。
おそらく、システムのヘッダを書き換えるように fix しちゃったんだと思う。
キャッシュを消したいので、Xcode 自体を強制終了しましょう。再インストールまでは必要ないです。

あまり勉強したことがない Metal 関係のリソースを物色中。
おお!と思ったのは公式のサンプル紹介記事にあったこのプロジェクト(群)。

C++ と書いてあるが、「どーせ、これ Objective-C++ でしょ?」と思っていたが、れっきとした C++ だった。
例えば、Xcode で GUI アプリを作成した際、自動で生成される main.m に相当する部分は
int main( int argc, char* argv[] )
{
NS::AutoreleasePool* pAutoreleasePool = NS::AutoreleasePool::alloc()->init();
MyAppDelegate del;
NS::Application* pSharedApplication = NS::Application::sharedApplication();
pSharedApplication->setDelegate( &del );
pSharedApplication->run();
pAutoreleasePool->release();
return 0;
}
となっている。
言われてみれば Objective-C++ と C++ との対応はこうですね、って感じ。
そんな方はこちらなんかも。
ただし、これは Swift の iOS プロジェクトなので、MacOS に移植する際にはいくらか(↓)修正が必要。
手動で追加する。

NSObect のサブクラスとして適当な名前のクラスを作成した後に、MTKViewDelegate を書き加える。
また、以下の二つのメソッドは MTKViewDelegate を継承するには必須のようなので、付け加える。

『MacでObjective-cを使ってMetalを初期化』
のシリーズを使って勉強しよう・・・と思っていたのだが、これは Objective-C で Metal を扱う際にはイマイチのやり方のようだ。
もともとアップル公式のサンプルは Objective-C で書かれており、これを Swift に移植したものが、上記シリーズ(というかネットに落ちている情報全般)っぽい。
この記事にならって丹念にアップル公式のサンプル眺めていくしかないかな。
MacOS のデスクトップアプリを Xcode で作成する際、UI として storyboard を選べば、自動で appdelegate は生成されるので、簡単なアプリならば、ここで初期化処理すればいい。
が、レガシーなプロジェクトでは xib が使われている場合も多い。
ちょっと調べますかね。
Xcode を起動し、UI を xib でプロジェクトを作成。

MainMenu.xib が自動で生成されるが、この Objects を管理しているのが AppDelegate という構成になっているようだ。

AppDelegate も自動生成されるが、このクラスは以下のように NSApplicationDelegate を継承している。

なるほど。
ただ、レガシーなプロジェクトではこの継承が明示されていないこともあるようだ。
AppController のような名前になっていて初見でなんのことかわからなかった。
もちろん、
– (void)applicationDidFinishLaunching:
– (void)applicationWillTerminate:
は、実装されていたが。
さらに言っておくと WindowController から AppController のイニシャライズをする、というなんとも風変わりな構成になっていた。
こういうのもあるんですね。
すみません、以前の記事は見当違いなことを書いていたかも。
メニューアイテムから各オブジェクトの操作をするには、この記事読んでください。
1 まず、アイテムと AppDelegate が結びついていることが前提。
2 ここがポイントなんでしょうが、以下のようにアイテムをファーストレスポンダーに結びつけ、その中から AppDelegate で定義したメソッドを選ぶ。

3 これは言われなければ絶対に気がつかない。アクションのコードを AppDelegate から動作せたいコントローラークラスにコピペ。

いや、これでうまくいくんだな。
Objective-C の GUI アプリシリーズの3回目。
前回まででプロジェクトを作成、window を表示させ、その window に若干の制御(表示位置をセンターに持ってくるだけだが)を加えた。
よりデスクトップアプリっぽさを出すために、メニューを操作してみたい。
一番簡単にはこの記事で示されているように
クラスの追加→メニュアイテムと関連付ける
だが、Objective-C でうまくいくか?
上の方法が汎用性があるかどうかはわからないが、以下のような手続きで Objective-C でもうまくいった。
すみません、元の操作は、単に App Delegate の名前を変えていただけかも。
結局、以下の方針で NSWindowController のサブクラスを作りました。
ちょっと自信ないですが、一旦、メニューバーを引き受ける object を追加して(↓下図参照。これをしないとイベントを引き受けるクラスを新規に追加できない)そのカスタムクラスを実装する(ワイの場合は NSWindowController のサブクラスとした)、という方針が素直でいいように思います。

一応、これでうまくいきました。悪名高き Could not insert new outlet connection エラーが頻発しましたが。。。
これも NSMenuDelegate 削ったら、エラーがピタリと止んだりと、ここら辺は、なんか感覚です。
cocoa のしきたり、奥が深い。
まず、メニューを管理するクラス(Menu1)を作成し、メニューバーの UI の所属先をこのクラスに変更。

こうした後、メニューアイテムをあの「ctrl 押しながら・・・」ってやつでソースコードに結びつけた。

IBAction の実装は以下のようにシンプルにした。
#import "Menu1.h"
@implementation Menu1
- (IBAction)test:(NSMenuItem *)sender{
NSLog(@"test");
}
@end
これでエラーを吐くことなく正常に動作した。

メニューアイテムが明示的に実体化されているので、大抵の場合は、この方法でいいのかもしれない。
前回まででまずはプロジェクトはできた。
しかし、ViewerController に関しては、Xcode 任せの状態であるため、次に、プロジェクトにこの派生クラスの追加をしよう。
まず、プロジェクト(本当は「ターゲット」だが)に NSWindowController 派生クラスを追加する。

名前はなんでもいいのだが、諸々の事情で PHWindowController とした。
ファイルが追加できたら、画面左にある(↓)の項目を切り替えて、現在の WindowController を PHWindowController に変更する。

この時点で run させても、変更前と動作は変わらない。
独自機能を追加するため windowDidLoad をオーバーライドする。
なお、Objective-C における override はかなり簡単で、親クラスの同名メソッドを書き換えるだけでいい。
今回は
[self.window makeKeyAndOrderFront:self];
[self.window center];
の2行を追加した。
ヘッダファイル PHWindowController.h は以下の通り。
#import <Cocoa/Cocoa.h>
NS_ASSUME_NONNULL_BEGIN
@interface PHWindowController : NSWindowController
//- (instancetype)init;
//@property(strong) NSWindow *window;
@end
NS_ASSUME_NONNULL_END
PHWindowController.m は以下の通り。
#import "PHWindowController.h"
@interface PHWindowController ()
@end
@implementation PHWindowController
/*
- (instancetype)init
{
self = [super init];
return self;
}
*/
- (void)windowDidLoad {
[super windowDidLoad];
[self.window makeKeyAndOrderFront:self];
[self.window center];
}
@end
これでランさせると以下のようになる。

変更前には画面やや左下に位置していたウィンドウが中央に配置されるようになった。
ところで、init メソッドは必ずしもいらないようだ。
また、NSWindowController を宣言した時点で window は持っているようで、自分自身のオブジェクトのウィンドウにアクセスする場合は self.window でいいようだ。
こういうクセみたいなものは、やってみないとわからない。
(続く)