該当コントローラが使用しているデリゲートメソッドを特定してオーバーライドする | 成長の果実

成長の果実

不完全でも良いから前に進む。

コントローラーの処理に手を加えたいときがあったりすると思う。

ポップオーバーをある条件のときは閉じさせたくないとか。


でもデリゲートで処理してるからメソッド名が分からない、どうしよう・・・

そんなときはrespondsToSelectorを使えばデリゲートメソッド名が特定できる。



サンプルで説明したほうが分かりやすいので、以下のものを作ってみる。


1.ボタンを押したらポップオーバーが表示される。
2.そのポップオーバーの、あるデリゲートメソッドをオーバーライドしてポップオーバーが閉じないようにする。



以下コード。

主要部分だけを説明していく。赤字の部分がポイント箇所。



まずはポップオーバーを呼び出すボタンを実装していく。


◎ViewController.h

#import <UIKit/UIKit.h>
#import "TableViewController.h"

@interface ViewController : UIViewController <TableViewControllerDelegate, UIPopoverControllerDelegate> {
UIButton *btn;
}

@property (strong, nonatomic) UIPopoverController *popOver;


/* メソッド */

- (void)btnAction:(UIButton*)sender;


/* Delegateメソッド */

- (void)tableViewControllerDelegateDidFinish:(NSString*)getData;


@end




◎ViewController.m

#import "ViewController.h"

@implementation ViewController

@synthesize popOver = _popOver;

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

// ボタン
btn = [UIButton buttonWithType:UIButtonTypeRoundedRect];
btn.frame = CGRectMake(0.0, 0.0, 180.0, 30.0);
btn.center = CGPointMake(self.view.frame.size.width / 2, self.view.frame.size.height / 2);
[btn setTitle:@"Popover" forState:UIControlStateNormal]; // 有効時
[btn addTarget:self action:@selector(btnAction:)forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:btn];
}

/* ============================================================================== */
#pragma mark - Button Action
/* ============================================================================== */
- (void)btnAction:(UIButton*)sender
{
// TableViewController生成
TableViewController *tableViewController;
tableViewController = [[TableViewController alloc]
initWithNibName:@"TableViewController"
bundle:nil];
tableViewController.contentSizeForViewInPopover = CGSizeMake(290, 280);
tableViewController.delegate = self;

// UINavigationを生成
UINavigationController *tableViewNavController = [[UINavigationController alloc] initWithRootViewController:tableViewController];

// タイトル設定
tableViewNavController.navigationBar.topItem.title = @"テーブル";

[tableViewNavController setNavigationBarHidden:NO];

/* UIPopoverController */

if (self.popOver == nil)
{
self.popOver = [[UIPopoverController alloc] initWithContentViewController:tableViewNavController];
self.popOver.delegate = self;
}

// ポップオーバーが現在表示されていなければ表示する
if (!self.popOver.popoverVisible)
{
[self.popOver presentPopoverFromRect:CGRectMake(sender.frame.origin.x - 40, sender.frame.origin.y - 10, 270, 216)
inView:self.view
permittedArrowDirections:UIPopoverArrowDirectionDown // 矢印の向きを下にする
animated:YES];
}
}


/* ============================================================================== */
#pragma mark - Delegete Method
/* ============================================================================== */
- (void)tableViewControllerDelegateDidFinish:(NSString*)getData
{
// タイトルを変更する
[btn setTitle:getData forState:UIControlStateNormal];
}


@end






次は、ポップオーバーで呼び出されるビューコントローラーを作成。



◎TableViewController.h

#import <UIKit/UIKit.h>

@protocol TableViewControllerDelegate;

@interface TableViewController : UIViewController <UITableViewDelegate, UITableViewDataSource> {

}

@property (nonatomic, assign) id <TableViewControllerDelegate> delegate;


/* メソッド */

- (void)rightBarBtnItmAction:(UIButton*)sender;


@end

//--------------------------------------------------------------//

@protocol TableViewControllerDelegate


/* メソッド*/

- (void)tableViewControllerDelegateDidFinish:(NSString*)getData;


@end





◎TableViewController.m

#import "TableViewController.h"

@implementation TableViewController

@synthesize delegate = _delegate;

- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.

//--------------------------------------------------
// ナビゲーションバーにボタンを乗せる
//--------------------------------------------------
UIButton *rightBarBtn = [UIButton buttonWithType:UIButtonTypeRoundedRect];
rightBarBtn.frame = CGRectMake(0, 0, 60, 24);
[rightBarBtn setTitle:@"ボタン" forState:UIControlStateNormal]; // 有効時
[rightBarBtn addTarget:self action:@selector(rightBarBtnItmAction:) forControlEvents:UIControlEventTouchUpInside];
UIBarButtonItem *rightBarBtnItm = [[UIBarButtonItem alloc] initWithCustomView:rightBarBtn];
self.navigationItem.rightBarButtonItem = rightBarBtnItm;


//--------------------------------------------------
// テーブルを生成
//--------------------------------------------------
UITableView *tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, 290, 280) style:UITableViewStylePlain];
tableView.delegate = self;
tableView.dataSource = self;
[self.view addSubview:tableView];
}


/* ============================================================================== */
#pragma mark - Table
/* ============================================================================== */
// セクション数
- (NSInteger)numberOfSectionsInTableView:(UITableView*)tableView
{
// Return the number of sections.
return 1;
}

// 行数
- (NSInteger)tableView:(UITableView*)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return 20;
}

// 内容
- (UITableViewCell *)tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = @"Cell";

UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}

// セル選択時の背景色を設定
UIView *backgroundView = [[UIView alloc] init];
backgroundView.backgroundColor = [UIColor brownColor];
cell.selectedBackgroundView = backgroundView;

// セル選択時の文字色を設定
cell.textLabel.highlightedTextColor = [UIColor blueColor];

// 項目
cell.textLabel.text = [NSString stringWithFormat:@"項目 %d", indexPath.row];

return cell;
}

// セルタップ時に呼び出される
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// 値を反映する
[self.delegate tableViewControllerDelegateDidFinish:[NSString stringWithFormat:@"項目 %d", indexPath.row]];
}


/* ============================================================================== */
#pragma mark - Button Action
/* ============================================================================== */
- (void)rightBarBtnItmAction:(UIButton*)sender
{
//
}


@end






最後に、ポップオーバーのデリゲートメソッドを特定してオーバーライドさせる。



◎CustomUIPopoverController.h

#import <Foundation/Foundation.h>
#import "objc/runtime.h"

@interface UIPopoverController(Add)


/* メソッド */

- (BOOL)respondsToSelector:(SEL)aSelector;


@end




◎CustomUIPopoverController.m

#import "CustomUIPopoverController.h"

@implementation UIPopoverController(Add)

// 該当コントローラが使用しているデリゲートメソッドをNSLogで確認
- (BOOL)respondsToSelector:(SEL)aSelector
{
NSLog(@"%s %@", class_getName([self class]), NSStringFromSelector(aSelector));

return [super respondsToSelector:aSelector];
}

// オーバーライド
- (void)dimmingViewWasTapped:(id)tapped
{
// dismissPopoverAnimatedさせない
}

@end





dimmingViewWasTapped:(id)tappedの中で何やってるかは分からないけど、恐らくdismissPopoverAnimatedして自分自身を閉じる処理くらいだろう。


これだけだとポップオーバーが全く閉じなくなるので他にも処理を書く必要があるが、サンプルなのでひとまずこのくらいということで。



実行して確認してみる。


$成長の果実-SampleRespondsToSelector01


上図ではわからないけど、画面をタップしてもポップオーバーが閉じなくなった!!


----------
サンプルソース:https://github.com/tetsuco/SampleRespondsToSelector