iOS コピー&ペースト UIPasteboard と UIMenuControllerの拡張


切り取り(cat)やコピー(copy)した内容を置き換える


 クリップボード(iOSではペーストボードというらしい)に追加や削除したタイミングでnotificationが発行される
  - 追加 : UIPasteboardChangedNotification
  - 削除 : UIPasteboardChangedTypesRemovedKey

以下サンプル
-(void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];
 
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(pasteboardChanged:) name:UIPasteboardChangedNotification object:nil];
}
- (void)pasteboardChanged:(NSNotification*)notification
{
    NSLog(@"pasteboardChanged");
    NSDictionary* userInfo = [notification userInfo];
    NSArray* keys = [userInfo objectForKey:UIPasteboardChangedTypesAddedKey];
    if ([keys count] != 0) {
        UIPasteboard *board = [UIPasteboard generalPasteboard];
        [board setValue:@"上書きする文字" forPasteboardType:[keys objectAtIndex:0]];
    }
}

ポップアップをカスタマイズする UIMenuController


 ポップアップの表示は、UIMenuControllerを使う

  1. UIMenuControllerの設定
    UIMenuController* menuController = [UIMenuController sharedMenuController];
  2. 表示するアイテムを追加
    menu.menuItems = [NSArray ...];
  3. 表示メッセージを送る
    [menu setMenuVisible:YES animated:YES];
   
そして、注意しないと行けないところは、
 - 表示するviewがfirstResponderになっていること
 - ポップアップが表示できる領域が確保されていること(こだわりない場合はDefaultで)
   menuController.arrowDirection = UIMenuControllerArrowUP; // 注意

特に2番目の所ははまりました。。
 viewをCGRectMake(10,10, 50, 44)などの位置につくってしまうと、ポップアップが表示される領域がないので。

以下サンプル
@interface ViewEx : UIView
@end
@implementation ViewEx
- (BOOL)canBecomeFirstResponder{
    return YES;
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    UIMenuController* menuController = [UIMenuController sharedMenuController];
    [menuController setTargetRect:CGRectZero inView:self];
 
    NSMutableArray* menuItems = [NSMutableArray array];
 
    [menuItems addObject:
     [[[UIMenuItem alloc] initWithTitle:@"メニュー"
                                 action:@selector(menu:)] autorelease]];
    menuController.menuItems = menuItems;
    [menuController setMenuVisible:YES animated:YES];
}
- (void)menu:(id)sender
{
    NSLog(@"menu1: %@", sender);
}
@end

呼び出し側
    ViewEx* viewEx = [[ViewEx alloc] initWithFrame:CGRectMake(110, 100, 50, 44)];
    [viewEx setBackgroundColor:[UIColor greenColor]];
    [self.view addSubview:viewEx];
    [viewEx becomeFirstResponder];
ちなみに標準のコピー等を表示したい場合、下記のように対応するメソッドのみYESを返して上げる必要があります

- (BOOL)canPerformAction:(SEL)action withSender:(id)sender {
if (action == @selector(cat:)) {
  return YES;
}
  return NO;
}

UITableView(UITableViewCell)内でのメニュー表示

やる事は同じです
まずはUITableViewCellの継承クラスを作成

@interface CellEx : UITableViewCell
@end
@implementation CellEx
- (BOOL)canBecomeFirstResponder{
    return YES;}
@end
使う側
- (void)viewDidLoad{    [super viewDidLoad];// Do any additional setup after loading the view.
    UITableView* tableView = [[[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStylePlain] autorelease];    tableView.delegate = self;    tableView.dataSource = self;    [self.view addSubview:tableView];}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{    return 10;}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{    CellEx *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell"];    if (cell == nil) {        cell = [[[CellEx alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"Cell"] autorelease];    }    cell.textLabel.text = [NSString stringWithFormat:@"row:%d", indexPath.row];
    return cell;}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{    CellEx* cell = (CellEx*)[tableView cellForRowAtIndexPath:indexPath];    [cell becomeFirstResponder];
    UIMenuController* menuController = [UIMenuController sharedMenuController];    [menuController setTargetRect:CGRectZero inView:cell];    menuController.arrowDirection = UIMenuControllerArrowDefault;        NSMutableArray* menuItems = [NSMutableArray array];        [menuItems addObject:     [[[UIMenuItem alloc] initWithTitle:@"メニュー"                                 action:@selector(menu:)] autorelease]];    menuController.menuItems = menuItems;    [menuController setMenuVisible:YES animated:YES];    }- (BOOL)canPerformAction:(SEL)action withSender:(id)sender {    if (action == @selector(menu:)) {  return YES;    }  return NO;}
- (void)menu:(id)sender{    NSLog(@"menu: %@", sender);}

実行するアクションの実装場所

上記のテーブルの例だと、CellExにcanPerformActionを実装しても動きます。
もちろん、menu:(id)senderも一緒に持って行きます。
どのような仕組みかは理解してませんがなんか気持ち悪いですね。
setTargetRnage:inViewで指定したViewのメソッドを呼んでると思ったんですが、違うみたいですね。

0 件のコメント:

コメントを投稿

ReactNativeでAndroid対応する話

前提 ReactNativeでiOS版のアプリをリリースしていて、Android版をリリースする話 トラブルシューティング Build.VERSION_CODES.Q が存在しないエラー compileSdkVersionを29以上にすると解決 メモリー足りないエラー Execu...