iOS7でUIAlertViewにaddSubview出来ない問題を回避する方法

タイトルに偽りあり。

正確にはiOS7からaddSubviewが出来なくなった訳ではなく、しても無視されるようになっているようです。

UIAlertViewにUIActivityIndicatorViewとかUIImageViewを乗せたりするUIってよく見ますよね。これが禁止されるとキツい部分も多いと思います。

正直困るので、なんとか出来ないもんかと調べたりして一応の回避策は見つけました。

 

ただし、UIAlertViewに何かを乗せる行為自体Appleが嫌ってきている様ですし今後の動作は保証できません。また、この方法で申請をしてRejectされる可能性もあるかもしれませんので自己責任で行って下さい。

 

という訳で本題の回避方法です。

UIAlertViewにUIActivityIndicatorViewを乗せてみます

まずiOS7以前はこんな感じで乗せていたと思います

UIAlertView *alert =[[UIAlertView alloc]initWithTitle:@"Alert"
                                               message:@"Indicatorを出したい\n\n\n"
                                              delegate:nil
                                     cancelButtonTitle:nil
                                     otherButtonTitles:@"OK", nil];
   
UIActivityIndicatorView *indicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite];
indicator.center = CGPointMake((self.view.bounds.size.width / 2) - 20, (self.view.bounds.size.height / 2) - 130);
[indicator startAnimating];
   
[alert addSubview:indicator];
[alert show];

画像でみるとこの様になります

f:id:ojukog:20131004212032j:plain

 

そして同様のコードをiOS7で動かすと

f:id:ojukog:20131004212049j:plain

この様にUIActivityIndicatorViewが乗っていません

 

なのでiOS7ではこんな感じで書いてみます

UIAlertView *alert =[[UIAlertView alloc]initWithTitle:@"Alert"
                                               message:@"Indicatorを出したい"
                                              delegate:nil
                                     cancelButtonTitle:nil
                                     otherButtonTitles:@"OK", nil];
   
UIActivityIndicatorView *indicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
indicator.center = CGPointMake((self.view.bounds.size.width / 2) - 20, (self.view.bounds.size.height / 2) - 130);
[indicator startAnimating];
   
[alert setValue:indicator forKey:@"accessoryView"];
[alert show];

これで動かすと

f:id:ojukog:20131004212131j:plain

この様にUIActivityIndicatorViewが乗りました。

  • iOS7以前でUIActivityIndicatorViewの領域分を確保する為にしていたmessageの改行を無くす
  • UIActivityIndicatorViewStyleWhiteでは見難いのでUIActivityIndicatorViewStyleGrayへ変更する

等の細かい調整は必要になりますが、この回避方法でUIAlertViewに何かを乗せるUIは実現出来ます。

肝である[alert setValue:indicator forKey:@"accessoryView"];を本当に使っていいのかというのはあると思いますが、冒頭の通り自己責任で使って下さい。

 

参考(要ログイン):Apple DeveloperForums

CoreImageで画像の加工をする その3「GPUにするべきかCPUにするべきか」

CoreImageで画像の加工をする その1「準備から実行」

CoreImageで画像の加工をする その2「フィルタの重ねがけ」

 

その1、その2からの続きというか補足です。

今までCIContextをこんな感じで作っていました。

NSDictionary *contextOptions = [NSDictionary dictionaryWithObjectsAndKeys:
                                     [NSNumber numberWithBool:NO],kCIContextUseSoftwareRenderer,nil];
CIContext *ciContext = [CIContext contextWithOptions:contextOptions];

 

CoreImageで処理を行う場合、その処理をGPUにさせるかCPUにさせるか選べます。

上記例だとGPUで処理を行うようになっていて、非常に高速に動作します。

ただしGPUで処理を行う場合は必ずアプリがアクティブな状態である必要があります。

またCIContextが扱える画像のサイズにも上限があり、シミュレータやiPhone4Sでは4096*4096までが扱えるサイズで、それ以上のサイズの場合はCPUで処理させなければいけません。

※CIContextが扱えるサイズについては環境で違いが出るのでinputImageMaximumSizeやoutputImageMaximumSizeで確認して適切に処理するようにします

 

という訳でバックグラウンドで処理させる場合や、画像サイズが非常に大きかった場合等はCPUで処理させる必要が出てくるのですが、その場合は下記の様にCIContextを作ります

NSDictionary *contextOptions = [NSDictionary dictionaryWithObjectsAndKeys:
                                     [NSNumber numberWithBool:YES],kCIContextUseSoftwareRenderer,nil];
CIContext *ciContext = [CIContext contextWithOptions:contextOptions];

これでCPUで処理をさせられます。

CIContextを作る際は用途や画像サイズによってGPU/CPUを選択して適切に処理をさせる事でパフォーマンスを高めることができます。

ちなみにシミュレータではCPUを選択することができません。この処理については実機で確認する必要があるので注意して下さい

CoreImageで画像の加工をする その2「フィルタの重ねがけ」

CoreImageで画像の加工をする その1「準備から実行」の続きです。

前回モノクロ加工を行いましたが複数のエフェクトを適用したい場合があると思います。

例えばコントラストを調整する下記の様なメソッドを作ります

-(UIImage*)effectColorControls:(UIImage *)targetImage{
    
    CIImage *ciImage = [[CIImage alloc] initWithImage:targetImage];
    
    CIFilter *ciFilter = [CIFilter filterWithName:@"CIColorControls"
                                    keysAndValues:kCIInputImageKey, ciImage,
                          @"inputContrast", [NSNumber numberWithFloat:1.75],
                          nil
                          ];
    
    NSDictionary *contextOptions = [NSDictionary dictionaryWithObjectsAndKeys:
                                     [NSNumber numberWithBool:NO],kCIContextUseSoftwareRenderer,nil];
    
    CIContext *ciContext = [CIContext contextWithOptions:contextOptions];
    CGImageRef cgImageRef = [ciContext createCGImage:[ciFilter outputImage] fromRect:[[ciFilter outputImage] extent]];
    UIImage* resultImage = [UIImage imageWithCGImage:cgImageRef];
    CGImageRelease(cgImageRef);
    
    return resultImage;
}

これを呼び出すと下記の様な結果になります

before:

f:id:ojukog:20130413150546p:plain

after:

f:id:ojukog:20130413155821j:plain

 

ここまでは前回の使い方と同じです。

今回は前回作ったモノクロ+コントラストの2つを適用してみます。

凄く単純だと作った2つのメソッドを呼び出す様な

-(void)effectSample{
    UIImage *targetImage = [UIImage imageNamed:@"sample.png"];
    
    //モノクロ
    targetImage = [self effectMono:targetImage];
    //コントラスト
    targetImage = [self effectColorControls:targetImage];
    
    UIImageView *imageView = [[UIImageView alloc] initWithImage:targetImage];
    [self.view addSubview:imageView];
}

こんな感じで良い気がします。

実際これでも両方のエフェクトがかかった形になるのですが、毎回CIImageやCIContextを作ったりと非常に無駄が多いです。

特にCIContextは毎回作るのではなく使いまわすのが推奨されているので出来れば1度で済ませたいです。

またメモリ的にも重ねがけの方が使用量が少ないです。大きな画像を扱うとCGImageRefを作った時点でかなりのメモリが使用されてしまうので何度も行うのは得策ではないと思います。

 

じゃあ重ねがけってどうやんのよというとこんな感じでやります

-(UIImage*)effectMonoAndColorControls:(UIImage *)targetImage{
    CIImage *ciImage = [[CIImage alloc] initWithImage:targetImage];
    
    //モノクロ
   *ciFilter = [CIFilter filterWithName:@"CIColorMonochrome"
                                    keysAndValues:kCIInputImageKey, ciImage,
                          @"inputColor", [CIColor colorWithRed:0.75 green:0.75 blue:0.75],
                          @"inputIntensity", [NSNumber numberWithFloat:1.0],
                          nil
                          ];
    //コントラスト
    ciFilter = [CIFilter filterWithName:@"CIColorControls"
                          keysAndValues:kCIInputImageKey, [ciFilter outputImage],
                @"inputContrast", [NSNumber numberWithFloat:1.75],
                nil
                ];
    
    NSDictionary *contextOptions = [NSDictionary dictionaryWithObjectsAndKeys:
                                     [NSNumber numberWithBool:NO],kCIContextUseSoftwareRenderer,nil];
    
    CIContext *ciContext = [CIContext contextWithOptions:contextOptions];
    CGImageRef cgImageRef = [ciContext createCGImage:[ciFilter outputImage] fromRect:[[ciFilter outputImage] extent]];
    UIImage* resultImage = [UIImage imageWithCGImage:cgImageRef];
    CGImageRelease(cgImageRef);
    
    return resultImage;
}

要はCIFilterを作る際に使っているInputImageに、エフェクトを適用したCIImageのOutputImageを入れれば良いだけです。

これを実行するとこんな結果になります

f:id:ojukog:20130413163237j:plain

 

※ちょっとわかりにくいですがモノクロ単体だとこうなってました

f:id:ojukog:20130413152831j:plain

 

これでフィルタの重ねがけが出来るようになり、色々なフィルタの組み合わせで遊べます。

例えばPhotompyで使っている昔風にする加工や、一時期賑わった漫画風の様な加工も簡単に行えます

 

CoreImageで画像の加工をする その1「準備から実行」

iOSのアプリで画像の加工をする場合、いくつか選択肢がありますがPhotompyで使っているCoreImageの簡単な使い方を紹介してみます。

CoreImageはAppleが提供しているAPIで、OSXやiOSで使う事ができます。ですがiOSでは使えるフィルタが限られているので注意が必要です。

使えるフィルタについてはリファレンスを参照するのが手っ取り早いです。

 

CoreImageを使う為にライブラリを追加します。手順は下記の通り

  1. projectのtargetからBuild Phasesを選択しLink Binary With Librariesの+ボタンを押してCoreImage.frameworkを追加します

    f:id:ojukog:20130413150112j:plain

  2. ヘッダファイルに下記のコードを追加
    #include <CoreImage/CoreImage.h>

これで準備は整ったので実際に加工します。

画像はこれを使ってみます

f:id:ojukog:20130413150546p:plain

 

この画像をモノクロにしてみます

-(void)effectSample{
    UIImage *targetImage = [UIImage imageNamed:@"sample.png"];
    targetImage = [self effectMono:targetImage];

    UIImageView *imageView = [[UIImageView alloc] initWithImage:targetImage];
    
    [self.view addSubview:imageView];
}

-(UIImage *)effectMono:(UIImage *)targetImage{
    
    CIImage *ciImage = [[CIImage alloc] initWithImage:targetImage];
    
    CIFilter *ciFilter = [CIFilter filterWithName:@"CIColorMonochrome"
                                    keysAndValues:kCIInputImageKey, ciImage,
                          @"inputColor", [CIColor colorWithRed:0.75 green:0.75 blue:0.75],
                          @"inputIntensity", [NSNumber numberWithFloat:1.0],
                          nil
                          ];
    
    NSDictionary *contextOptions = [NSDictionary dictionaryWithObjectsAndKeys:
                                     [NSNumber numberWithBool:NO],kCIContextUseSoftwareRenderer,nil];
    
    CIContext *ciContext = [CIContext contextWithOptions:contextOptions];
    CGImageRef cgImageRef = [ciContext createCGImage:[ciFilter outputImage] fromRect:[[ciFilter outputImage] extent]];
    UIImage* resultImage = [UIImage imageWithCGImage:cgImageRef];
    CGImageRelease(cgImageRef);
    
    return resultImage;
}

エフェクトをかける際に肝になるのはCIFilterの設定です。その他の設定はひとまず置いておきます。

使いたいフィルタや設定できる値などは全てリファレンスに書かれていますので、そちらを参照して任意の値を設定するようにして下さい。

例えば上記例ではinputColorやinputIntensityの値を弄る事でかかるエフェクトの結果が変わってきます。

とりあえず上記例をそのまま実行すると

 

f:id:ojukog:20130413152831j:plain

こんな感じに加工できました。

他にもフィルタは用意されているので色々試してみるのも面白いと思います

Photompy 1.0.2をリリースしました

iPhone用画像編集アプリ Photompyの1.0.2をリリースしました。

Photompy 1.0リリース

iPhone用画像編集アプリのPhotompyをリリースしました。

AppStoreの説明と同じになってしまいますが、アプリの説明は下記の様になっています

 

■主な機能■ 

・画像、文字の合成 

・画像の切り抜き 

・画像のエフェクト加工 

・画像の回転 

 

■対応端末■ 

iPhone4S/iPhone5 

※対応端末以外での動作は保障できません 

 

■対応OS■ 

iOS6以降が必要です 

 

その他、アプリの使い方についてはアプリ内のヘルプを御覧下さい

AppStoreの説明に書くとrejectされてしまったので、補足はこちらに書きます

 

■補足■

Photompyは複数の画像を扱えます。

扱える画像数に特に制限を設けていない為、扱う画像のサイズが大きかったり、エフェクトをかけていると端末の状況によってはメモリが不足する事があります。

その際は他のアプリを落とすか、Photompy上の画像を一度保存し、再起動する等してメモリを確保して下さい。

 

また実機での動作確認を行なっているのは現状iPhone4S(iOS6.1)になります。

iPhone5での動作確認はシミュレータでしか行なっていません。その為表示等が崩れる可能性がありますが、基本機能は動作すると思います。

それ以外の端末では動作確認を行なっていないので動作の保障は出来ません。予めご了承下さい

余裕のRejected

前回の記事から11日経過。

今だにReleaseされていないのは一度rejectを食らったから。

 

申請から5日後辺りにWaiting for ReviewからIn Reviewに変わったのだが、すぐにrejectメールが届いた。

メールにはiTunes ConnectのResolution Centerで詳細を確認しなってことだけ書いてあり、リンクがあるのでそこから飛ぶ。

で、Resolution Centerには原因が書かれているんだけど、基本的に気にするのは

3.11: Apps which recommend that users restart their iOS device prior to installation or launch may be rejected

の部分でいい。

他にメッセージが書いてあるが、そこは変更のやり方等が記載されてる。

ただ「この部分が駄目」と特定はしてくれてないっぽいので結構戸惑うかも。

 

今回のreject理由は

「アプリのインストールや起動前にiOSの再起動を求めるアプリはリジェクトされる可能性があります。」

という意味なのだけど、申請したアプリは特に再起動は求めない。

が、アプリの説明文の所に「メモリ不足の場合、一度画像を保存してアプリを再起動して下さい」との一文をいれていた。

もしかしてこれが理由なのか?と思いそこを削って再申請して無事審査をパスした。

 

にしても「○○の場合は一度アプリを終了させ、しばらくたってから再開して下さい」という文言のアプリはOKで「アプリを再起動して下さい」が駄目っていうのは笑える。

言葉の表現って奥深い。そう思いました。アプリ申請する方は気をつけて下さい

 

今はProcessing for App Store。すぐにReleaseされるか、それともまた時間がかかるのか。