展示并操作编辑菜单
编辑菜单是一个上下文菜单,它展示并提供了一些命令,这些命令能够操作选中内容,例如一个文本页面中的文字或者图片。编辑菜单是复制,剪切,粘贴操作的一个组成部分。它默认展示复制,剪切,粘贴,选中以及全选的命令。然后,你可以添加一个自定义的条目到编辑菜单来对选中内容进行其他操作。
管理选中和编辑菜单
为了在一个页面中复制或剪切一些东西,这些东西必须是被选中的。可以是一串文本,一张图片,一个URL,一个颜色,或者是其他种类的数据,包括自定义对象。你必须自己管理页面中选中的对象。如果用户通过使用某种手势选择了页面中的对象,你必须要处理这个事件,记录选中区域(取消选中之前已选中内容),并在页面上标记出选中的内容。可以允许用户选中多个对象,执行复制,剪切,粘贴操作,你必须自己实现这种多选行为。
注:对于处理触摸事件,包括手势的内容,查看Event Handling Guide for iOS
当你的应用识别出用户已经请求了编辑菜单——也就是产生了一次选中行为——你应该完成以下几步来展示菜单:
- 调用UIMenuController的sharedMenuController类方法获取全局的实例。
- 计算选中区域的边界,并将结果传给setTargetRect:inView:方法。编辑菜单将在这个区域的上面或者下面出现(取决于选中区域距离屏幕顶部及底部的距离)。
- 调用setMenuVisible:animated方法让编辑菜单显示出来。
下面代码展示了你应该如何在touchesEnded:withEvent:方法中展示编辑菜单,用以处理复制,剪切,粘贴操作。(注意,示例代码省略了处理选中的代码。)代码也展示了自定义页面发送becameFirstResponder消息来确保成为接下来复制,剪切,粘贴操作的第一响应者。
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *theTouch = [touches anyObject];
if ([theTouch tapCount] == 2 && [self becomeFirstResponder]) {
// selection management code goes here...
// bring up edit menu.
UIMenuController *theMenu = [UIMenuController sharedMenuController];
CGRect selectionRect = CGRectMake (currentSelection.x, currentSelection.y, SIDE, SIDE);
[theMenu setTargetRect:selectionRect inView:self];
[theMenu setMenuVisible:YES animated:YES];
}
}
菜单初始包括所有第一响应者实现了UIResponderStandardEditActions方法的行为(copy: paste:等)。在菜单出现之前,系统发送canPerformAction:withSender:消息到第一响应者。在该方法的实现中,响应者评估该命令(第一个参数selector中传递)是否适用于当前场合。例如,如果selector是paste:并且剪切板中没有该页面能够处理的数据,响应者应当返回NO,来取消粘贴命令的展示。如果第一响应者没有实现canPerformAction:withSender:方法,或者没有处理指定命令,消息将沿响应链传递下去。
下面代码展示了canPerform:withSender方法的实现,查找并匹配了cut:,copy:以及paste:方法。基于当前的选中文本以及剪切板内容,会开启或关闭复制,剪切,粘贴等菜单命令。
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender {
BOOL retValue = NO;
ColorTile *theTile = [self colorTileForOrigin:currentSelection];
if (action == @selector(paste:) )
retValue = (theTile == nil) &&
[[UIPasteboard generalPasteboard] containsPasteboardTypes:
[NSArray arrayWithObject:ColorTileUTI]];
else if ( action == @selector(cut:) || action == @selector(copy:) )
retValue = (theTile != nil);
else
retValue = [super canPerformAction:action withSender:sender];
return retValue;
}
注意代码里最后的else里调用了父类的实现,父类就有机会处理子类选择忽略的命令。
注意菜单命令在执行后,可能会改变其他菜单命令。例如,当用户执行了全选操作,复制和剪切操作应当出现在菜单中。在这种情况下,响应者可以在菜单始终可见的情况下通过调用menu controller的update方法更新菜单,这就导致了canPerformAcation:withSender:的调用。
添加自定义条目至编辑菜单
你可以添加自定义条目到编辑菜单。当用户点击此项时,将发出一个命令,该命令将以特定应用程序的方式影响当前目标。UIKit通过target-action机制完成该目的。该条目的点击导致了一个行为消息被发送到响应链中第一个能处理该消息的对象。下图展现了一个自定义条目的例子
UIMenuItem的实例代表自定义条目。UIMenuItem对象有两个属性,title和action selector,都可以任意时刻修改。要实现一个自定义菜单条目,你必须初始化这个UIMenuItem实例的这些属性,将该实例添加到menu controller的自定义菜单条目数组中,并在合适的响应者类中实现该命令的操作。
其他方面是各处通用的:使用UIMenuController的单例对象。在自定义或者重写的页面中,你设置了页面为第一响应者,获取menu controller单例,设置一个目标rectangle,通过调用setMenuVisible:animated:方法展示编辑菜单。下面代码是一个添加自定义菜单项的简单例子。
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *theTouch = [touches anyObject];
if ([theTouch tapCount] == 2) {
[self becomeFirstResponder];
UIMenuItem *menuItem = [[UIMenuItem alloc] initWithTitle:@"Change Color" action:@selector(changeColor:)];
UIMenuController *menuCont = [UIMenuController sharedMenuController];
[menuCont setTargetRect:self.frame inView:self.superview];
menuCont.arrowDirection = UIMenuControllerArrowLeft;
menuCont.menuItems = [NSArray arrayWithObject:menuItem];
[menuCont setMenuVisible:YES animated:YES];
}
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {}
- (BOOL)canBecomeFirstResponder { return YES; }
- (void)changeColor:(id)sender {
if ([self.viewColor isEqual:[UIColor blackColor]]) {
self.viewColor = [UIColor redColor];
} else {
self.viewColor = [UIColor blackColor];
}
[self setNeedsDisplay];
}