見出し画像

【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 *);

こんな感じです。




この記事が気に入ったらサポートをしてみませんか?