Blocksで記述出来るiPad用のPopOverMenuと通知のクラスを公開しました

追記4/21/2012:メモリ管理関係のバグを修正しました。Githubの最新をご覧下さい。

ただいま、作成中のアプリ、TweetOverviewのサブミットも終了し、現在審査の終了を待っているところです。開発中はこんな感じです。

このアプリのために作成した、共通で使えそうなクラスを共有します。最初に動作のビデオ

TOVLinkPopoverViewController

こちらはUIPopOverControllerを使って簡単にメニューを出すために作りました。

PopOver動作サンプル
PopOver動作サンプル

このように動作します。このためのコードは簡単にはこのように動作しています。

  • PopOver表示用のNSMutableArrayを作成
  • Arrayに、表示項目と、実行用のBlockを登録
  • PopOverを表示する

このような感じです。これと直接は関係ありませんが、UIToolbarのイベントから、位置を取得する用にも作っています。

-(IBAction)clickedButtonA:(UIBarButtonItem*)sender event:(UIEvent*)event {
....
controller.parentPop = _myPop;
_myPop.popoverContentSize = CGSizeMake(400, 44*arrRows.count+16);
// pop.delegate = self;
UIView *button;
for( UITouch* touch in [event allTouches] ) {
if( [touch phase] == UITouchPhaseEnded ) {
button = [touch view];
}
}
CGRect rect = CGRectMake(button.frame.origin.x + button.superview.frame.origin.x, button.frame.origin.y + button.superview.frame.origin.y, button.frame.size.width, button.frame.size.height);

[_myPop presentPopoverFromRect:rect inView:self.view permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];

このように、eventをとって、タッチをとることによって、ボタンの押された位置から、ポップオーバーを出すことが出来ます。
そして、あのblocksは大丈夫?iOSで、blocksを使った記述で、リリースビルドのみにクラッシュする事例 | Zero4Racer PRO Developer’s Blog で参照した様に、このコードは、リリースのみでクラッシュする状況があります。その検証用に4パターンコードで書いていますが、おそらくの最適解は、blockを、NSMutableDictionaryに登録する際に、そのまま登録すると、retainされてしまうため、[block copy]をして、明示的にコピーしてあげることによって,内部で使用している変数がキャプチャされて、リテインされるので、正しく動作するのかなと考えているのですが、未だ曖昧です。

呼び出しのサンプルコードはこの様になっています。

-(IBAction)clickedButtonA:(UIBarButtonItem*)sender event:(UIEvent*)event {

NSMutableArray *arrRows = [NSMutableArray arrayWithCapacity:0];
{
#warning this code only crash on Release Build.... Don't use this
NSMutableDictionary * dicRow = [NSMutableDictionary dictionaryWithCapacity:0];
[dicRow setValue:NSLocalizedString(@"Pattern 1 Crash",nil) forKey:kDicKeyLinkPopCellText];
[arrRows addObject:dicRow];
dispatch_block_t block = ^{

NSString *str = [NSString stringWithFormat:@"%@",[_tweet valueForKey:@"text"]];

[[UIPasteboard generalPasteboard] setString:str];
[[JTCAppNotificationManager sharedManger] startTimerNotificationWithMessage:NSLocalizedString(@"Copy succeeded", @"Copy succeeded") dulation:2.5 iconName:@"w17-check.png"];

};
[dicRow setValue:block forKey:kDicKeyLinkPopBlock];
}
{
NSMutableDictionary * dicRow = [NSMutableDictionary dictionaryWithCapacity:0];
[dicRow setValue:NSLocalizedString(@"Pattern 2 __block",nil) forKey:kDicKeyLinkPopCellText];
[arrRows addObject:dicRow];
#warning this code is not the way recommended how to use __block ... not recommended
__block id bt = _tweet;
dispatch_block_t block = ^{

NSString *str = [NSString stringWithFormat:@"%@",[bt valueForKey:@"text"]];

[[UIPasteboard generalPasteboard] setString:str];

[[JTCAppNotificationManager sharedManger] startTimerNotificationWithMessage:NSLocalizedString(@"Copy succeeded", @"Copy succeeded") dulation:2.5 iconName:@"w17-check.png"];

};
[dicRow setValue:block forKey:kDicKeyLinkPopBlock];
}
{
NSMutableDictionary * dicRow = [NSMutableDictionary dictionaryWithCapacity:0];
[dicRow setValue:NSLocalizedString(@"Pattern 3 only declare bt crash",nil) forKey:kDicKeyLinkPopCellText];
[arrRows addObject:dicRow];
#warning this code only crash on Release Build.... Don't use this
id bt = _tweet;
dispatch_block_t block = ^{

NSString *str = [NSString stringWithFormat:@"%@",[bt valueForKey:@"text"]];

[[UIPasteboard generalPasteboard] setString:str];

[[JTCAppNotificationManager sharedManger] startTimerNotificationWithMessage:NSLocalizedString(@"Copy succeeded", @"Copy succeeded") dulation:2.5 iconName:@"w17-check.png"];
};
[dicRow setValue:block forKey:kDicKeyLinkPopBlock];
}
{
NSMutableDictionary * dicRow = [NSMutableDictionary dictionaryWithCapacity:0];
[dicRow setValue:NSLocalizedString(@"USE THIS:Pattern 4 declare bt and copy block",nil) forKey:kDicKeyLinkPopCellText];
[arrRows addObject:dicRow];
id bt = _tweet;
dispatch_block_t block = ^{

NSString *str = [NSString stringWithFormat:@"%@",[bt valueForKey:@"text"]];

[[UIPasteboard generalPasteboard] setString:str];

[[JTCAppNotificationManager sharedManger] startTimerNotificationWithMessage:NSLocalizedString(@"Copy succeeded", @"Copy succeeded") dulation:2.5 iconName:@"w17-check.png"];
};
[dicRow setValue:[block copy] forKey:kDicKeyLinkPopBlock];
}

NSMutableArray *sections = [NSMutableArray arrayWithObject:arrRows];
TOVLinkPopoverViewController *controller= [[TOVLinkPopoverViewController alloc] init];
controller.arrayLink = sections;
_myPop = [[UIPopoverController alloc] initWithContentViewController:controller];

controller.parentPop = _myPop;
_myPop.popoverContentSize = CGSizeMake(400, 44*arrRows.count+16);
// pop.delegate = self;
UIView *button;
for( UITouch* touch in [event allTouches] ) {
if( [touch phase] == UITouchPhaseEnded ) {
button = [touch view];
}
}
CGRect rect = CGRectMake(button.frame.origin.x + button.superview.frame.origin.x, button.frame.origin.y + button.superview.frame.origin.y, button.frame.size.width, button.frame.size.height);

[_myPop presentPopoverFromRect:rect inView:self.view permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];

}

TimerNotification

続いてのサンプルは,タイマーで表示するNotificationです。こちら非常に簡単に使えて、
[[JTCAppNotificationManager sharedManger] startTimerNotificationWithMessage:NSLocalizedString(@"Timer Notification Test", nil) dulation:2.5 iconName:@"w17-check.png"];

この様に書くと、

タイマー付きNotification
タイマー付きNotification

このような通知が、指定時間表示されます。このタイプの通知は、一度に一つだけの表示を想定しています。

IndicatorNotification.

くるくる回る、Indicator付きの通知を出すためのものです。
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

__block NSNumber* num;
dispatch_async(dispatch_get_main_queue(), ^{
num = [[JTCAppNotificationManager sharedManger] startIndicationNotificationWithMessage:NSLocalizedString(@"Indicator Timer Test", nil)];
});

double delayInSeconds = 5.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
[[JTCAppNotificationManager sharedManger] stopNotification:num];
});
});

このように書くと、5秒間通知が出て、ユーザは画面を触れなくなります。このクラスの特徴は複数の通知を出すことが出来、通知毎に、NSNumberで、通知IDを返すので、指定部分だけ終了して、複数のNotificationを出すことが出来るという事です。

複数の通知を出した状態
複数の通知を出した状態

このようにして、通知の出し入れが可能になります。詳しくは最初のビデオをご覧下さい。

これらは、GitHubにMITライセンスで提供していますので、関心のある方はご覧下さい。
tomohisa/iOS_PopoverMenu_Notification

今回の件でいろいろ教えて頂いたり、調べたりして、ARC+Blocksが思った以上に正しく使うのが難しいということが分かりました。どちらにしろ、よいテストが非常に重要である事がよく分かりました。

「Blocksで記述出来るiPad用のPopOverMenuと通知のクラスを公開しました」への1件のフィードバック

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください