当苹果在 iOS 3.0 中增加了剪切、复制和粘贴功能时,它同时为开发者提供了 UIMenuController 组件用来定制该弹出菜单,但不幸的是,最开始的实现要很麻烦:
-
附加在菜单的视图的 canBecomeFirstResponser 必须返回 YES,这意味着必须子类化。例如最常用的显示元素 UITableViewCell 和 UILabel 默认返回的是 NO
-
UILongPressGestureRecognizer
直到
iOS 3.2 才提供, which means that the long press to initiate the menu display had to be implemented viatouchesBegan:withEvent:
, touchesMoved:withEvent:
,
andtouchesEnded:withEvent:
.
Every custom long press recognizer might use a different delay constant, which could easily confuse users who are used to another app's implementation.
而最新的 iOS 使用两种基本方法解决了这个问题,一个是表格单元格,另外一个是定制菜单选项。
指定情景: UITableViewCell on iOS 5
如果你只是想在 UITableViewCell 中使用系统提供的复制粘贴功能(大部分情况是这样),iOS 5.0 有更简单的方法:
01 |
-
( BOOL )tableView:(UITableView
*)tableView shouldShowMenuForRowAtIndexPath:(NSIndexPath *)indexPath { |
05 |
-
( BOOL )tableView:(UITableView
*)tableView canPerformAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender { |
06 |
if (action
== @selector(copy:)) { |
13 |
-
( void )tableView:(UITableView
*)tableView performAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender { |
14 |
if (action
== @selector(copy:)) { |
15 |
[UIPasteboard
generalPasteboard].string = [data objectAtIndex:indexPath.row]; |
该菜单调用 tableView:canPerformAction:forRowAtIndexPath:withSender
以确认是否该显示系统菜单选项并调用 tableView:performAction:forRowAtIndexPath:withSender:
当用户选择某个选项时.
定制菜单项
如果你想使用定制菜单项,下面代码比较隐晦,但非常灵活。你需要检测是否用户长按并显示菜单,而最简单的方法就是在表格单元格中使用 UILongPressGestureRecognizer
1 |
UILongPressGestureRecognizer
*recognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longPress:)]; |
2 |
[cell
addGestureRecognizer:recognizer]; |
为了让菜单显示,目标视图必须在 responder 链中,很多 UIKit 视图默认并无法成为一个 responder ,因此你需要之类这些视图重载 canBecomeFirstResponder 方法范围 YES
在下面例子中,我们使用定制类 TSTableViewCell 并实现了长按选择器
01 |
-
( void )longPress:(UILongPressGestureRecognizer
*)recognizer { |
02 |
if (recognizer.state
== UIGestureRecognizerStateBegan) { |
03 |
TSTableViewCell
*cell = (TSTableViewCell *)recognizer.view; |
04 |
[cell
becomeFirstResponder]; |
06 |
UIMenuItem
*flag = [[UIMenuItem alloc] initWithTitle:@ "Flag" action:@selector(flag:)]; |
07 |
UIMenuItem
*approve = [[UIMenuItem alloc] initWithTitle:@ "Approve" action:@selector(approve:)]; |
08 |
UIMenuItem
*deny = [[UIMenuItem alloc] initWithTitle:@ "Deny" action:@selector(deny:)]; |
10 |
UIMenuController
*menu = [UIMenuController sharedMenuController]; |
11 |
[menu
setMenuItems:[NSArray arrayWithObjects:flag, approve, deny, nil]]; |
12 |
[menu
setTargetRect:cell.frame inView:cell.superview]; |
13 |
[menu
setMenuVisible:YES animated:YES]; |
17 |
-
( void )flag:(id)sender
{ |
18 |
NSLog(@ "Cell
was flagged" ); |
21 |
-
( void )approve:(id)sender
{ |
22 |
NSLog(@ "Cell
was approved" ); |
25 |
-
( void )deny:(id)sender
{ |
26 |
NSLog(@ "Cell
was denied" ); |
There is only one small gotcha with UIMenuItem
:
if the specified action is not implemented by your view controller, that item will not appear in the menu.