Objective-Cでよく使うけと忘れてしまいがちなテクニックたち

このメモにはObjective-Cでよく(?)使うけど毎回忘れてしまい、Google検索するところから始めなければならないようなテクニックたちを載せておきます。

 

文字列を置換する

NSStringのstringByReplacingOccurrencesOfStringメソッドを使います。

NSString *result = [targetString stringByReplacingOccurrencesOfString:@"置換対象文字列" withString:"新しく置き換える文字列"];

 

文字列を結合する

いろいろやり方があります。まずは一番単純なものを。

NSString newString = [stringA stringByAppendingString:stringB];

 

double型を文字列に変換する

一度NSNumberにするところがポイントです。

double d = 1.234;
NSString *str = [[NSNumber numberWithDouble:d] stringValue];

 

NSFetchedResultsControllerによってフェッチされたNSManagedObjectを取得する

NSArray *objects = [self.fetchedResultsController fetchedObjects];

 

配列の最後の要素を取得する

NSArray *objects = @[@"A", @"B", @"C"];
NSString *last = [objects lastObject];

 

 

 

iOS – カメラデバイスが存在するか判定する方法

iPod touchは機種によってはカメラがないものもあります。そのため、カメラを使うアプリの場合にはカメラが搭載されているか判定する必要があります。

この判定はとても簡単にできます。以下のような分岐を入れるだけです。

if ([UIImagePickerController isSourceTypeAvailable: UIImagePickerControllerSourceTypeCamera]) {
  // カメラがある場合
}
else {
  // カメラがない場合
}

 

環境

XCode 4.6.2

 

iOS – UITextFieldやUITextViewで入力文字数を制限する方法

入力制限

UITextFieldやUITextViewで入力できる最大文字数を指定する方法を紹介します。

UITextFieldの場合

UITextFieldのshouldChangeCharactersInRangeデリゲートメソッドを利用します。このメソッドはファーストレスポンダになっているUITextFieldに1文字でも入力があれば呼び出されます。このメソッド内において入力された文字が最大文字数を超えていないかチェックします。

尚、お決まりですが、このデリゲートメソッドを使用するためにはUITextFieldDelegateプロトコルを当該ViewControllerで採用し、デリゲート先をそのViewControllerにする必要があります。

実装例は以下の通りです。

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{    
    // 最大入力文字数
    int maxInputLength = 200;

    // 入力済みのテキストを取得
    NSMutableString *str = [textField.text mutableCopy];

    // 入力済みのテキストと入力が行われたテキストを結合
    [str replaceCharactersInRange:range withString:string];

    if ([str length] > maxInputLength) {
        // ※ここに文字数制限を超えたことを通知する処理を追加

        return NO;
    }

    return YES;
}

上記のコードを解説します。まず、7行目でファーストレスポンダになっているUITextFieldから既に入力済みの文字列を取得します。これにはこのデリゲートが呼び出されるきっかけとなった入力は含まれていません。きっかけとなった入力はreplacementStringに格納されています。

10〜12行目の部分では、これら2つの文字列を結合し、その文字列の長さが許容されている範囲を超えていないかチェックして、返す戻り値を変えています。このデリゲートメソッドがYESを返せば直前の入力がUITextFieldに反映されますが、NOを返した場合は反映されません

 

UITextViewの場合

UITextFieldの場合とほとんど同じですが、デリゲートメソッド名や採用するプロトコルが異なります。UITextViewではshouldChangeTextInRangeデリゲートメソッドを利用し、当該ViewControllerではUITextViewDelegateプロトコルを採用します。

以下、実装例です。

- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
{
    int maxInputLength = 2000;

    // 入力済みのテキストを取得
    NSMutableString *str = [textView.text mutableCopy];

    // 入力済みのテキストと入力が行われたテキストを結合
    [str replaceCharactersInRange:range withString:text];

    if ([str length] > maxInputLength) {
        return NO;
    }

    return YES;
}

 

複数のUITextFieldがある場合はどうするか tag_value_on_attribute_inspector

UITextFieldが複数あり、それぞれ最大入力文字数が異なる場合はUITextFieldに異なるtag値を設定し、そのtag値でどのUITextFieldなのかを判定して処理を行うのが簡単だと思います。tag値は右図のようにStoryboard上のAttribute Inspectorで編集できます。

以下に実装例を載せます。

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
    // 最大入力文字数
    int maxInputLength = 50;
    switch (textField.tag) {
        case 1: // 名前
            maxInputLength = 50;
            break;
        case 2: // 住所
            maxInputLength = 200;
            break;
        case 3: // 年齢
            maxInputLength = 3;
            break;

        default:
            break;
    }

    // 入力済みのテキストを取得
    NSMutableString *str = [textField.text mutableCopy];

    // 入力済みのテキストと入力が行われたテキストを結合
    [str replaceCharactersInRange:range withString:string];

    if ([str length] > maxInputLength) {
        return NO;
    }

    return YES;
}

上記のコードは「名前」「住所」「年齢」の3つのUITextFieldにそれぞれtag値として1, 2, 3を割り振った場合の実装例です。

 

環境

XCode 4.6.1

 

iOS – 画面を開いたときの初期フォーカスの設定方法

画面を開いたときにフォーカスを設定したい画面を開いた時点でテキストフィールドにフォーカスを設定するには、そのテキストフィールドをファーストレスポンダに設定すればいいだけです。

テキストフィールドをファーストレスポンダに設定するためにはbecomeFirstResponderメソッドを使用します。

さて、テキストフィールドをファーストレスポンダに指定するタイミングですが、画面描画が完了した後に実行されるviewDidLoadメソッドがその処理を記述するのに適しています。

以下にサンプルコードを載せておきます。

- (void)viewDidLoad
{
    [super viewDidLoad];

    // 最初からテキストフィールドにフォーカスをあわせておく
    [self.mainTextField becomeFirstResponder];
}

 

 

 

iOS – Core Dataで多対多のデータを扱う

データモデルで多対多の関係を扱うことはけっこう多かったりします。代表的なのが「ブログ記事」エンティティと「タグ」エンティティの関係がそれですね。ブログ記事は複数あり、またその記事には複数のタグが付けられ、かつタグは複数のブログ記事の間で重複して指定できるのでまさに多対多の関係にあるわけです。

今回はメモはそんな多対多のデータをCore Dataフレームワークで扱う方法についてのメモです。Core Dataを使うと多対多のデータ構造が非常に簡単に扱えます。やり方さえ一度覚えてしまえば後は楽ちんです。

 

データモデル

many_to_many_relation

左図に示すように、ブログ記事を表す「Blog」エンティティと、そのタグを表す「Tag」エンティティがある場合を例にとって解説します。

 

上記の2つのエンティティはそれぞれリレーションを持っています。BlogエンティティはTagエンティティの要素を複数持つことを表すリレーション「tags」を持ち、 同様にTagエンティティはBlogエンティティの複数要素を持つことを表すリレーション「blogs」を持っています。複数要素(多対)の指定は、下図のように、「To-Many Relationship」にチェックを入れることで指定できます。ポイントはBlogエンティティ、Tagエンティティの双方から互いに多対リレーションを設定するところです。

tags_relation

blogs_relation

 

BlogエンティティのManagedObjectサブクラスの作成

メニューから「File」→「New」→「File...」を選択し、下図のようにCore Dataのの「NSManagedObject subclass」を選択してBlogエンティティを扱うBlogクラスを作成します。この作業は上記のデータモデルのところでリレーションを設定した後にやってください。そうじゃないと自動生成されるソースを後で手直しすることになってしまいます。

ManagedObjectSubclassCreation

Blogクラスが自動生成されると、以下のようになっているはずです。

#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>

@interface Blog : NSManagedObject

@property (nonatomic, retain) NSString * title;
@property (nonatomic, retain) NSSet *tags;
@end

@interface Blog (CoreDataGeneratedAccessors)

- (void)addTagsObject:(NSManagedObject *)value;
- (void)removeTagsObject:(NSManagedObject *)value;
- (void)addTags:(NSSet *)values;
- (void)removeTags:(NSSet *)values;

@end

上記のコードを見てみるとプロパティとしてtagsを持っています。このtagsを用いることでBlogオブジェクトに紐づくTagオブジェクトを参照できます。またaddTagsObjectメソッドなども生成されていることがわかります。

 

データの登録

ここまで来ればなんとなく予想がついているとは思いますが、あるBlogオブジェクトにTagオブジェクトを追加する(紐付ける)場合には以下のようなコードになります。

// blogObjはBlogエンティティに対応するNSManagedObjectのサブクラス
// tagObjはTagエンティティに対応するNSManagedObjectのサブクラス
[blogObj addTagsObject tagObj];

 

データの参照

あるBlogオブジェクトに紐付けられたTagオブジェクを参照したい場合は、Blogオブジェクトのtagsプロパティにアクセスします。例えば、あるBlogオブジェクトにどんなTagオブジェクトが紐付いているかログでみたいときには以下のようなコードになります。

for (Tag *tagObj in blogObj.tags) {
  NSLog(@"%@", tagObj.name);
}

解説は以上となります。

いや〜しかしCore Dataよくできてますね。最初さわったときは「なんだこれ?記述量多すぎ!わけわからん!」てな感じでしたが、リファレンスを読めば読むほど「こんなことまで自動でやってくれるんだ!すごい!」という感想に変わりつつあります。

 

環境
XCode 4.6