ObjecTips

Swift & Objective-C で iOS とか macOS とか

UITableViewCell で Copy Paste メニューを表示する

UITableView にはセルの Copy Paste メニューを簡単に実装できる delegate メソッドが用意されている。

// Copy/Paste.  All three methods must be implemented by the delegate.

- (BOOL)tableView:(UITableView *)tableView shouldShowMenuForRowAtIndexPath:(NSIndexPath *)indexPath NS_AVAILABLE_IOS(5_0);
- (BOOL)tableView:(UITableView *)tableView canPerformAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender NS_AVAILABLE_IOS(5_0);
- (void)tableView:(UITableView *)tableView performAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender NS_AVAILABLE_IOS(5_0);

ヘッダによればこの3つのメソッドを実装する必要があるとの事。
上の2つのメソッドで YES を返して最後のメソッドは空にすればとりあえずセルの長押し時に以下のようにメニューを表示させる事ができる。

- (BOOL)tableView:(UITableView *)tableView shouldShowMenuForRowAtIndexPath:(NSIndexPath *)indexPath
{
    return YES;
}

- (BOOL)tableView:(UITableView *)tableView canPerformAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender
{
    return YES;
}

- (void)tableView:(UITableView *)tableView performAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender
{
}

f:id:Koze:20150628001225p:plain

performAction: での条件分岐のためのセレクタの判定は NSStringFromSelector で文字列にしてから isEqualToString: メソッドで比較する以外に == でSEL型を直接比較する事もできる。

  • copy: の時は表示コンテンツをペーストボードに保存
  • paste: の時はペーストボードの中身を表示コンテンツに反映
  • cut: の時は表示コンテンツをペーストボードに保存して表示コンテンツを削除

を実装するとひとまず動きが見られる状態になる。


上記のコードでは文字列 @"ABCDE" から配列 @[@"A", @"B", @"C", @"D", @"E"] を作成してテーブルに表示するコンテンツのリストとして使用している。
実際に動作させてみると以下の様に動く。

まずCのセルを長押ししてメニューを表示

f:id:Koze:20150628002621p:plain

Copy を選択

f:id:Koze:20150628002650p:plain

次のDのセルを長押し

f:id:Koze:20150628002720p:plain

Paste を選択

f:id:Koze:20150628002744p:plain

CのコンテンツがDに上書きされる

f:id:Koze:20150628002803p:plain

次にBのセルを長押し

f:id:Koze:20150628002822p:plain

今度は Cut を選択

f:id:Koze:20150628002845p:plain

Bが削除される

f:id:Koze:20150628003220p:plain

そしてAのセルを長押し

f:id:Koze:20150628003227p:plain

Paste を選択

f:id:Koze:20150628003235p:plain

BのコンテンツがAに上書きされる

実際には表示するコンテンツのモデルクラスによってアクションを動作させたりさせなかったり、オブジェクトの encode によるコピーを実装したり、アプリ用の UIPasteboard を使用したりといろいろ考慮すべき事はあるけどひとまず使用方法のサンプルとしてはこんなところ。


以下の様に引数に入って来る action を全てログ出力すると以下のように様々なアクションが入って来るのが分かる。

- (BOOL)tableView:(UITableView *)tableView canPerformAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender
{
    NSLog(@"%@", NSStringFromSelector(action));
    return YES;
}

ログ

cut:
copy:
select:
selectAll:
paste:
delete:
_promptForReplace:
_transliterateChinese:
_showTextStyleOptions:
_define:
_addShortcut:
_accessibilitySpeak:
_accessibilitySpeakLanguageSelection:
_accessibilityPauseSpeaking:
makeTextWritingDirectionRightToLeft:
makeTextWritingDirectionLeftToRight:

でもこの delegate メソッドを使う方法だとメニューの詳細なカスタマイズができず、実際にメニューとして表示される内容は framework 任せで Cut, Copy, Paste になってしまう模様。