iPhone用メモ帳アプリをつくる

 iPhone用のメモ帳アプリのつくり方を解説します。メモ帳を使用することで、iPhoneアプリプログラムの基本を学ぶことができます。実際に手を動かして作っていきましょう。

プロジェクトとGUIの作成

まず、「Master-Detail Application」としてプロジェクトをつくります。

プロジェクトの作成

そして、名前を適当に決めてプロジェクトを完成させます。完成したプロジェクトを実行すると「Edit」「+」ボタンがあります。「+」を押すと現在の時刻が追加されます。

現時刻の追加

そして、詳細ボタン「>」を押すと、タイトルが画面中央に表示されます。

時刻の表示

Editを押すことにより、項目を削除することが可能です。あとはこれを修正してメモ帳アプリをつくっていきます。

「+」を押して時刻が表示されるまで

では、「+」をおして時刻が表示されるまでの様子を見ていきます。

オブジェクトを作成してテーブルビューに追加する

MasterViewController.mファイルを見てください。以下の様な項目があるはずです。

- (void)insertNewObject:(id)sender
{
    if (!_objects) {
        _objects = [[NSMutableArray alloc] init];//オブジェクトの作成
    }
    [_objects insertObject:[NSDate date] atIndex:0];//日付を先頭に記入する
    NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0];
    [self.tableView insertRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
}

これは、「+」が押されると実行されます。

[_objects=[[NSMutableArray alloc] init];

でもしオブジェクトの生成ができていないなら、NSMutableArray型のオブジェクトをつくり、

[_objects insertObject:[NSData data] atIndex:0];

で日付[NSData data]を先頭atIndex:0に追加します。そして、

*indexPath=[NSIndexPath indexPathForRow:0 inSection:0];

でセクション0、行0を指定し、

[self.tableView insertRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];

テーブルtableViewに追加します。

セクションが行の数を返す

tableViewに追加されると、以下の項目が実行されます。

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return _objects.count;
}

それぞれ、セクションと行の数を返します。ここではセクションの数が1つなので、1が返されます。行は_objects.countだけ返します。

テーブルビューに表示する

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath];

    NSDate *object = _objects[indexPath.row];
    cell.textLabel.text = [object description];
    return cell;
}

「+」を押すと題名が表示されるように改良する

では、メモ帳らしくするために「+」を押したら「題名」が表示されるように改良します。はじめに、insertObjectメソッドを以下のように変更します。

- (void)insertNewObject:(id)sender
{
    if(!_objects){
        _objects=[NSMutableArray array];//オブジェクトがないなら作成する
    }
    NSDictionary *dic=@{ @"title": @"題名",@"body":@"内容"};
    NSFileManager *FManager=[NSFileManager defaultManager];
    NSURL *docDURL=[FManager URLForDirectory:NSDocumentDirectory
                                    inDomain:NSUserDomainMask
                           appropriateForURL:nil
                                      create:NO
                                       error:nil];
    NSString *fileName=[[[NSUUID UUID] UUIDString] stringByAppendingPathExtension:@"txt"];
    NSURL *FURL=[docDURL URLByAppendingPathComponent:fileName];
    if([dic writeToURL:FURL atomically:YES]){//dicをFURLへ書き込み
        //書き込みに成功したならテーブルに追加する
        [_objects insertObject:FURL atIndex:0];
        NSIndexPath *indexPath=[NSIndexPath indexPathForRow:0 inSection:0];
        [self.tableView insertRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
    }
}

ここでは、NSDictionary型の変数dicをつくり、

if([dic writeToURL:FURL atomically:YES]){

で書き込み、書き込みに成功した場合にテーブルに追加しています。ただ、これでは「題名」と表示されないので、表示用メソッドcellForRowAtIndexPathを以下のように変更します。

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath];

    NSURL *FURL=_objects[indexPath.row];
    NSDictionary *dic=[NSDictionary dictionaryWithContentsOfURL:FURL];  //ファイルURLから読み込み
    cell.textLabel.text=dic[@"title"];  //「題名」を表示
    return cell;
}

これで「+」を押すと「題名」が表示されます。

題名の表示

メモファイルの読み込みを実装する

 「+」を押して、ファイルを作成できたので、今度は起動時にメモファイルを読み込めるようにします。起動時にはviewDidLoadメソッドが実行されます。ここで、オブジェクトを準備します。以下のように一行追加してください。

- (void)viewDidLoad
{
    [super viewDidLoad];
	// Do any additional setup after loading the view, typically from a nib.
    self.navigationItem.leftBarButtonItem = self.editButtonItem;

    UIBarButtonItem *addButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(insertNewObject:)];
    self.navigationItem.rightBarButtonItem = addButton;
    
    _objects=[NSMutableArray array];//追加**********
}

あとは、このメソッドの下にviewWillAppearメソッドを追加します。このメソッドはテーブルが表示される際に実行されます。

-(void)viewWillAppear:(BOOL)animated
{
    [_objects removeAllObjects];
    NSFileManager *FManager=[NSFileManager defaultManager];
    NSURL *docDURL=[FManager URLForDirectory:NSDocumentDirectory
                                    inDomain:NSUserDomainMask
                           appropriateForURL:nil
                                      create:NO
                                       error:nil];
    NSArray *file=[FManager contentsOfDirectoryAtURL:docDURL
                   includingPropertiesForKeys:@[NSURLContentAccessDateKey]
                                             options:0
                                               error:nil];
    [_objects addObjectsFromArray:file];
    [_objects sortUsingComparator:^NSComparisonResult(id obj1,id obj2){
        NSDate *date1,*date2;
        [obj1 getResourceValue:&date1 forKey:NSURLContentModificationDateKey error:nil];
        [obj2 getResourceValue:&date2 forKey:NSURLContentModificationDateKey error:nil];
        return [date2 compare:date1];
    }];
    [self.tableView reloadData];
}

ここでははじめに

[_objects removeAllObjects];

で_objects内のファイルをすべて削除して初期化しています。そして、contentsOfDirectoryAtURLを使って、fileにファイルのディレクトリをすべて記憶させています。あとは

[_objects addObjectsFromArray:file];

でオブジェクトにファイルデータを与えて、sortUsingComparatorで更新順に並び替えています。最後に

[self.tableView reloadData];

でテーブルを更新して表示させます。iOSを実行させるとこれまで保存した内容がはじめに表示されます。

追加項目を削除できるようにする

追加項目を削除しても、再起動すると再び追加されています。テーブルだけでなくファイルも削除されるようにしていきます。commitEditingStyleメソッドを以下のように変更します。

- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (editingStyle == UITableViewCellEditingStyleDelete) {
        NSURL *FURL=_objects[indexPath.row];//ファイルURLを取得
        NSFileManager *FManager=[NSFileManager defaultManager];
        if([FManager removeItemAtURL:FURL error:nil]){  //ファイルを削除
            //成功したらテーブルも削除
            [_objects removeObjectAtIndex:indexPath.row];
            [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
        }
    }
}

内容は簡単で、ファイルURLを取得してファイルの削除とテーブルの変更を行っているだけです。これで、項目を削除できるようになりました。

メモの内容を表示する

メモの追加・削除ができるようになりました。あとは内容を編集できるようになればメモ帳の完成ですね。がんばりましょう。

セグウェイで値を渡す

Main.storyboardファイルを見ると、以下のようにMasterとDetail Viewがつながっています。

セグウェイ

このつなぎ役をしているのが、セグウェイ(Segue)です。セグウェイを使って値をMasterからDetailへ伝えます。その処理を行っているのが、prepareForSegueです。ここを以下のように変更します。

編集用GUIの準備

ではDetailViewを以下のようにText FieldとText View

Text Field

Text View

を追加します。

メモ帳のGUI

あとは、画面に合わせてエリアが変わるようにします。以下のように設定しましょう。

画面の固定画面の固定

GUIとソースをつなげる

ではGUIとソースをつなげます。右上の

2画面

をおして2画面に設定して、コントロールを押しながらドラッグアンドドロップでつなげます。

GUIとソースをつなげる

GUIとソースをつなげる

あとは、題名と内容を表示できるようにします。DetailViewController.mのconfigureViewメソッドを以下のように変更します。

- (void)configureView
{
    // Update the user interface for the detail item.

    if (self.detailItem) {
        NSURL *FURL=(NSURL *)self.detailItem;
        NSDictionary *dic=[NSDictionary dictionaryWithContentsOfURL:FURL];
        self.titleField.text=dic[@"title"];
        self.contentField.text=dic[@"body"];
    }
}

ファイルURLから題名と内容を読み取って表示させています。

メモ内容を編集する

メモ内容の表示ができたので、編集できるようにします。テキストフィールドやテキストビューの編集が完了したことを知るために、Delegateを導入します。アウトラインビューを

Delegation

より表示し、以下のようにテキストフィールドとテキストビューにDelegateを追加します。

delegateの追加

そして、DetailViewController.hを以下のように変更します。

@interface DetailViewController : UIViewController<UITextViewDelegate,UITextViewDelegate>

さらにDetailViewController.mに以下のように追加して、編集が終わったら保存するようにします。

-(void)saveData
{
    NSURL *FURL=(NSURL *)self.detailItem;
    NSDictionary *dic=@{@"title":self.titleField.text,@"body":self.contentField.text};
    [dic writeToURL:FURL atomically:YES];
}
-(void)textFieldDidEndEditing:(UITextField *)textFiedl
{
    [self saveData];
}
-(void)textViewDidEndEditing:(UITextView *)textView
{
    [self saveData];
}

以上で、アプリの完成です。これでメモをチェックしたり編集したりできます。

メモ帳

著者:安井 真人(やすい まさと)