![見出し画像](https://assets.st-note.com/production/uploads/images/10256642/rectangle_large_type_2_67e8691208631441e2577fba21a205ab.jpeg?width=1200)
【Objective-C】Block構文でポップオーバー(UIPopoverPresentationController)での押下したセルをコールバックで返す方法【Xcode10.1】
こういう人向けに発信しています。
・ブロック構文について理解が浅い人
・ブロック構文でコールバックするサンプルコードが見たい人
・Objective−c 初級者〜中級者
ブロック構文とは
一言では説明できないので別noteで展開します。
以前どこかで読んだ説明になりますが、構造体と思っておいて下さい。
このnoteで出来るようになる事
(1)ポップオーバーでtableViewを表示させる
(2)tableViewにn個の選択肢を表示させる。
(3)n番目押下した時にポップオーバー表示させたクラス側でオブジェクトへの変更ができる(1番目のセルならこういう文言を表示させるなど)
アプリへの実装イメージ
【 】年というボタンを押下したら、
1990年-2019年までポップオーバーで出てきて、選択したら、
元の空白が数字入ってるみたいなのがイメージに近いですね。
アプリの実装イメージ(スクリーンショット)
0番目のセルを押下する事で、
ViewController(最初のクラス)のUIButtonのテキストを
変更しています。
クラス構成
・ViewController.h(UIViewController継承)
・ViewController.m
・popOverTableTableViewController.h(UITableView継承)
・popOverTableTableViewController.m
popOverTableTableViewControllerに関しては、
UITableViewを継承して作成しました。
もちろんUIViewController
UITableViewDelegate,UITableDataSourceDelegateを採用してもOKです(手間だから継承しています。)
ViewController.h
#import <UIKit/UIKit.h>
@interface ViewController : UIViewController
@end
ViewController.m
#import "ViewController.h"
#import "popOverTableTableViewController.h"
//デリゲートの採用はしておく。
@interface ViewController ()<UIPopoverPresentationControllerDelegate>
@end
@implementation ViewController{
UIButton *nextVCBtn;
}
- (void)viewDidLoad {
[super viewDidLoad];
[self initButton];
NSLog(@"ボタンを描画した");
}
-(void)initButton{
nextVCBtn = [[UIButton alloc]initWithFrame:CGRectMake(0, self.view.bounds.size.height/2 + 50.0f, self.view.bounds.size.width, 10.0f)];
[nextVCBtn setTitle:@"次のVCに遷移。(UIButton)" forState:UIControlStateNormal];
[nextVCBtn setTitleColor:[UIColor blueColor] forState:UIControlStateNormal]; //有効時
[nextVCBtn addTarget:self action:@selector(tappedButton:)
forControlEvents:UIControlEventTouchDown];
[self.view addSubview:nextVCBtn];
}
- (IBAction)tappedButton:(id)sender {
//表示したいViewControllerを初期化する。
popOverTableTableViewController *popOverVC = [[popOverTableTableViewController alloc] init];
popOverVC.customBlock = ^(NSIndexPath *indexPath){
switch (indexPath.row) {
case 0:
[nextVCBtn setTitle:@"0番目のセルを押下して変更した" forState:UIControlStateNormal];
break;
case 1:
[nextVCBtn setTitle:@"1番目のセルを押下して変更した" forState:UIControlStateNormal];
break;
default:
break;
}
};
[self presentPopOverWithViewController:popOverVC sourceView:nextVCBtn];
}
- (void)presentPopOverWithViewController:(UIViewController *)viewController sourceView:(UIView *)sourceView
{
viewController.modalPresentationStyle = UIModalPresentationPopover;
viewController.preferredContentSize = CGSizeMake(600.0, 44*2);
UIPopoverPresentationController *presentationController = viewController.popoverPresentationController;
presentationController.delegate = self;
presentationController.permittedArrowDirections = UIPopoverArrowDirectionUp;
presentationController.sourceView = sourceView;
presentationController.sourceRect = sourceView.bounds;
presentationController.backgroundColor = [UIColor whiteColor];
[self presentViewController:viewController animated:YES completion:NULL];
}
//iPhoneでの描画に大きく関係するのでしっかり追加しておく
- (UIModalPresentationStyle)adaptivePresentationStyleForPresentationController:(UIPresentationController *)controller
{
return UIModalPresentationNone;
}
@end
popOverTableTableViewController.h
#import <UIKit/UIKit.h>
typedef void(^MyCustomBlock)(NSIndexPath *);
@interface popOverTableTableViewController : UITableViewController<UITableViewDelegate,UITableViewDataSource>
@property (nonatomic, copy) MyCustomBlock customBlock;
@end
popOverTableTableViewController.m
#import "popOverTableTableViewController.h"
@interface popOverTableTableViewController ()
@end
@implementation popOverTableTableViewController
- (void)viewDidLoad {
[super viewDidLoad];
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 2;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 44;
}
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
//標準で用意されているTableViewを利用する場合。
NSString *cellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (!cell) {
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
}
cell.textLabel.text = [NSString stringWithFormat:@"このセルは%ld番目のセルになります!", (long)indexPath.row];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
_customBlock(indexPath);
}
重要な所だけ解説します。
popOverTableTableViewController *popOverVC = [[popOverTableTableViewController alloc] init];
popOverVC.customBlock = ^(NSIndexPath *indexPath){
switch (indexPath.row) {
case 0:
[nextVCBtn setTitle:@"0番目のセルを押下して変更した" forState:UIControlStateNormal];
break;
case 1:
[nextVCBtn setTitle:@"1番目のセルを押下して変更した" forState:UIControlStateNormal];
break;
default:
break;
}
};
popOverTableTableViewControllerのクラスを初期化して、
インスタンスを生成し、popOverVC(popOverTableTableViewController)
のプロパティであるcustomBlockというブロックに
^{から始まるブロックを代入しております。
ブロックを受け渡されたクラスにて
#import <UIKit/UIKit.h>
typedef void(^MyCustomBlock)(NSIndexPath *);
@interface popOverTableTableViewController : UITableViewController<UITableViewDelegate,UITableViewDataSource>
@property (nonatomic, copy) MyCustomBlock customBlock;
@end
typedefはブロックの型の宣言なので、
最悪なくてもOKですが可読性を高める為には必須と言えます。
typedef void(^MyCustomBlock)(NSIndexPath *);
今後、MyCustomBlockという名前の型は、
値を返さない(void)けど引数1つ(NSIndexPath型)持っているBlockだから
というブロックの型の宣言になります。
@property (nonatomic, copy) MyCustomBlock customBlock;
なので、上記のプロパティ宣言は
(1)値を返さない(void)けど引数1つ(NSIndexPath型)持っているBlock
(2)ブロック名はcustomBlock
(3)copy属性
という事になります。(3)も重要。
実際にブロックを呼び出している所
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
_customBlock(indexPath);
}
めっちゃシンプルですけど、これだけでブロック(customBlock)に
引数 NSIndexPath型のindexPathが渡せます。
たとえば0番目を押下すると
indexPath.row=0,indexPath.section = 0という情報が渡されてます。
コールバック処理
popOverVC.customBlock = ^(NSIndexPath *indexPath){
switch (indexPath.row) {
case 0:
[nextVCBtn setTitle:@"0番目のセルを押下して変更した" forState:UIControlStateNormal];
break;
case 1:
[nextVCBtn setTitle:@"1番目のセルを押下して変更した" forState:UIControlStateNormal];
break;
default:
break;
}
};
ブロックを呼び出されたら、上記でブロックが実行されます。
indexPath.rowを参照する事で何番目のセルが押下されたのかわかります。
また、押下されたセルのtextを渡してあげる事も可能です。
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell *selectedCell = [self.tableView cellForRowAtIndexPath:indexPath];
_customBlock(selectedCell.textlabel.text);
}
察しの良い方はお気づきかもしれませんが、
上記ではブロックにNSString型を渡してあげているので、
もし上記のコードをやりたい場合は
ブロック自体の引数の型を変える必要あり。
NSString型をコールバックする場合の書き方
(変更箇所のみ記載)
popOverTableTableViewController *popOverVC = [[popOverTableTableViewController alloc] init];
popOverVC.customBlock = ^(NSString *cellString){
switch (cellString) {
case @"0番目のセルです":
[nextVCBtn setTitle:@"0番目のセルを押下して変更した" forState:UIControlStateNormal];
break;
case 1番目のセルです:
[nextVCBtn setTitle:@"1番目のセルを押下して変更した" forState:UIControlStateNormal];
break;
default:
break;
}
};
typedef void(^MyCustomBlock)(NSString *);
こんな感じです。