Objective-C/Xcode Tips

何度、検索かけても、忘れているようなこと。メモ。

NSSet -> NSArray 変換

NSSet *nss = [[NSSet alloc] initWithObjects:@"AA", @"BB", @"CC", @"DD", nil];
NSArray *nsa = [nss allObjects];

任意のインスタンス変数のクラス名を調べる

NSString *className = NSStringFromClass([hoge class]);
NSLog(@"ClassName is: %@", className);

#pragma mark の謎

簡単に言えば、ソースコードを Xcode で扱った際に見出しをつけられる。

 

 

CoreData と NSTableView

CoreData に関する記事はいくつか書いてきたんだが、実用的には NSTableView とリンクして使いたい。

なのだが、cocoa & Objective-C 環境で作成されたサンプルがないんだな、これが(笑)。

UITableView になってしまうが iOS & Swift なら結構あるんですけどね。
まあ、ないと iPhone アプリでデータのテーブル表示できないでしょうから、当然でしょうけど。

NSTableView 単独になってしまうけど、かろうじて見つけたのが、これ

おそるおそる現環境(MacOS Ventura, Xcode 14)でビルド。

おお、修正なしで一発動作。よしよし。

解説記事も充実している(態度豹変)。

 

このサンプルをベースに CoreData のデータと NSTableView のセルを結びつける。

結果的には、まあまあできた。

ポイントは、NSTableDataSource プロトコルと NSTableViewDelegate プロトコルに必須の二つのメソッドを実装するところでしょうか。

こういうとき「動く」サンプルは便利だと思うのだが、サンプルを少々訂正するだけでいいし、だから、その意味を掴みやすい。

ワイはこんな風に実装した。


// NSTableViewDataSource Protocol Method

- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView {
    //return self.numbers.count;
    return self.personname.count;
}

// NSTableViewDelegate Protocol Method

-(NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
{
    NSString *identifier = tableColumn.identifier;
    NSTableCellView *cell = [tableView makeViewWithIdentifier:identifier owner:self];
    
    if ([identifier isEqualToString:@"personname"]) {
        //cell.textField.stringValue = [self.numbers objectAtIndex:row];
        cell.textField.stringValue = [self.personname objectAtIndex:row];
    } else {
        cell.textField.stringValue = [self.personage objectAtIndex:row];
    }
    
    return cell;
}

元コードではセル値の供給元になっている numbers は NSArray として定義されていたが、これだと可変値は扱いにくい。データを追加・削除しやすいように NSMutableArray に変更した。

これはなかなか勉強になった。

最終的には、sqlite のデータが書き変わったら、テーブルのセル値も追従して変えてくれるようにしたいんだが、現状だとちょっと難しいかな。(よくわからない人は「NSTableView update」あたりで検索かけましょう。ただ、これ、オブジェクト間通信を使えばいいような? 設計的に美しくないし、可読性落ちるけどさ→やはり通知でできましたとさ)

 

 

Xcode で GUI アプリ 4

このシリーズも今回4回目。

継続していて気が付いたことは、アプリの製作は文法の学習とはまた異なった知識が必要だということ。

前回扱った内容はまさにそれで、メニューイベントを管理するクラスをどこに置くか?という話題で、ある程度凝ったアプリならば、NSWindowController の派生クラスに置くのがいいのでは、みたいな話をした。

答えが複数あり、状況によって最適解を探す、というのは、プログラミングに限らず、習得するのが難しい領域の一つなんでしょうね。

前置きはともかく、今回は、特定のクラスが必要になったとき、それを Objective-C でどう構成するかという話。

イニシャライザでインスタンスを返す

どう構成するかはひとまず置いて、定型的な書き方の復習。


@interface Hoge : NSObject
-(nullable instancetype) initHoge;
@end

@implementation Hoge
-(nullable instancetype) initHoge;
{
    self = [super init];
    if(self)
      {
         //初期化処理
       }
   return self;
}
@end

この書き方、昔は無茶苦茶違和感あったけど、最近慣れたな。

なお、initXxx は init family とかイニシャライザなどと言われ、クラスの初期化を受け持つメソッド。
init 以下に適当な文字(列)をつけるが、最初の文字は大文字にする必要がある。

だから、initialize は init familiy ではない

以前に他人のコードを読んでいたとき、同一クラス内で init と initialize が同居していて「は?」と思ったのだが、そういうことらしい。

(続く)

 

Xocde エラー関係

よくあるエラーに関して。

The document “***.h” could not be saved. You don’t have permission.

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

 

Xcode で GUI アプリ 3

すみません、以前の記事は見当違いなことを書いていたかも。

メニューアイテムから各オブジェクトの操作をするには、この記事読んでください。

 

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 押しながら・・・

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

コードの実装

IBAction の実装は以下のようにシンプルにした。

#import "Menu1.h"

@implementation Menu1

- (IBAction)test:(NSMenuItem *)sender{
    NSLog(@"test");
}
@end

動作確認

これでエラーを吐くことなく正常に動作した。

メニューアイテムが明示的に実体化されているので、大抵の場合は、この方法でいいのかもしれない。