【Objective-C】アプリ内保存した画像のパスがpathForResourceで取れない(解決編)

【追記】2014.9.13 3:00
この方法はあくまで「アプリ内保存した画像のパス」の話で、Xcode上で最初から登録している画像には当てはまりません。パスに「Documents」ディレクトリが含まれないためです。

【Xcode5.1.1 + iOS 7.1 + MacOX10.9.4】

前回の解決編です。

JBOYSOFT JunjiSuzukiさんに「NSSearchPathForDirectoriesInDomains を使うのもいいかも」との助言を受けて、描いてみたコードがこれ。NSSearchPathForDirectoriesInDomainsでディレクトリのパスを取ってきて、保存しているファイル名と組み合わせて、自前でパスを作っている。

    NSArray *paths = NSSearchPathForDirectoriesInDomains(
                                                         NSDocumentDirectory,
                                                         NSUserDomainMask, YES);
    
    NSArray *ph = [select_name componentsSeparatedByString:@"/"];
    NSString *fn = [ph objectAtIndex:ph.count-1];
    
    NSLog(@"paths >> %@",paths);
    NSLog(@"fn >> %@",fn);
    
    path = [NSString stringWithFormat:@"%@/%@",[paths objectAtIndex:0],fn];
    
    NSLog(@"path >> %@",path);

    iconImage = [[UIImage alloc] initWithContentsOfFile:path];

これだと既存のものも、起動後に保存したものも、どちらも問題なく参照出来る!NSLogによるトレースはこちら。

paths >> (
    "/var/mobile/Applications/BB4E2366-816D-427F-8F8B-FF836EB58AB8/Documents"
)
fn >> icon20140913003532.png
path >> /var/mobile/Applications/BB4E2366-816D-427F-8F8B-FF836EB58AB8/Documents/icon20140913003532.png

気になったので、前回描いたコードで取得されるパスを確認してみる。前回のコードはコレ。

    NSArray *phrases = [imageName componentsSeparatedByString:@".png"];
    NSString *filename = [phrases objectAtIndex:0];
    NSString *path= [[NSBundle mainBundle] pathForResource:filename ofType:@"png"];
    
    //アプリ内保存した直後の画像は、path が nullになる
    NSLog(@"path >> %@",path);
    
    //保存直後の画像はNSBundleでパスが取れないための対処(アプリが再起動しないとダメみたい)
    UIImage *iconImage;
    
    if (path == NULL) {
        iconImage = [UIImage imageNamed:imageName];
        
    } else {
        iconImage = [[UIImage alloc] initWithContentsOfFile:path];
    }

※ imageNameには、”../Documents/icon20140912171442.png”のようなパスが入っている。

アプリ起動前に保存されている画像について、NSLogで表示されるパスを見ると・・・

path >> /var/mobile/Applications/BB4E2366-816D-427F-8F8B-FF836EB58AB8/xxxxxx.app/../Documents/icon20140913003017.png

と、アプリ名(xxxxxx.app/../)がはさまってる。この違いは何?NSBundleだとこうなるの?

更に、冒頭の修正コードをアプリ名(xxxxxx.app/../)がはさまってるパスに変更して参照してみる。

    NSArray *paths = NSSearchPathForDirectoriesInDomains(
                                                         NSDocumentDirectory,
                                                         NSUserDomainMask, YES);
    
    NSArray *ph = [select_name componentsSeparatedByString:@"/"];
    NSString *fn = [ph objectAtIndex:ph.count-1];
    
    NSLog(@"paths >> %@",paths);
    NSLog(@"fn >> %@",fn);
    
    NSArray *paths2 = [[paths objectAtIndex:0] componentsSeparatedByString:@"Documents"];
    path = [NSString stringWithFormat:@"%@iconFaceCam.app/%@",[paths2 objectAtIndex:0],select_name];
    
    NSLog(@"path >> %@",path);

    iconImage = [[UIImage alloc] initWithContentsOfFile:path];

NSLogの出力結果がこれ。

paths >> (
    "/var/mobile/Applications/BB4E2366-816D-427F-8F8B-FF836EB58AB8/Documents"
)
fn >> icon20140913012801.png
path >> /var/mobile/Applications/BB4E2366-816D-427F-8F8B-FF836EB58AB8/xxxxxx.app/../Documents/icon20140913012801.png

動作としては、こちらでも問題なく動きました。

で、結論としては、アプリ内保存のファイル参照では・・・

・NSSearchPathForDirectoriesInDomainsでドキュメントディレクトリを取得する。
・ドキュメントディレクトリ+ファイル名でパスを生成する。
・生成したパスを使って、initWithContentsOfFileで参照する。

ということになるんだけれど、そもそも何で・・・

[[NSBundle mainBundle] pathForResource:filename ofType:@”png”]

でパスが取得できないのか、がわからずモヤモヤします…。

とりあえずは余計なifとか要らなくなったので、よしとして先に進みます。
JBOYSOFT JunjiSuzukiさんに感謝。

【参考】
iOS でデータを永続化する方法 – A Day In The Life