复制,剪切,粘贴操作
用户可以复制某个应用中的文本,图片,或者其他数据到另外一个地方,到该应用内或者另一个应用都可以。UIKit Framework在UITextView中实现了复制-剪切-粘贴操作,如果你想要拥有这些行为,你可以使用这些类或者自己实现复制-剪切-粘贴。
下面的内容描述了UIKit的接口,如何使用复制,剪切,粘贴操作。
注:和复制粘贴操作相关的指导,看iOS Human Interface Guidelines.中的“Supporting Copy and Paste”一节
UIKit的复制粘贴操作
UIKit中提供了几个类和一个非正式协议(译者注:非正式协议在较早的版本用分类实现,目前用协议实现,协议中的方法为可选类型)以及一种机制,让你能够实现复制,剪切,粘贴操作。
- UIPasteboard类提供了剪切板:共享数据的保存的地方。这个类提供了从剪切板按条读写数据的方法。
- UIMenuController类展示了编辑菜单,出现在选中内容的上方或者下方。编辑菜单默认的命令是复制,剪切,粘贴,选中和全选。你可以在编辑菜单中添加自定义条目。
- UIResponder类声明了canPerformAction:withSender:方法。响应类可以实现该方法,来让某些编辑条目根据当前选中内容出现或移除。
- UIResponderStandardEditActions 非正式协议声明了处理复制,剪切,粘贴,选中以及全选命令的接口。当用户点击了一个命令时,响应的方法会被调用。(译者注:UITextView实现了改协议)
剪切板
剪切版是应用内或应用间交换数据的的标准机制。其最常见的用处是复制,剪切,粘贴操作。
- 当用户选中了数据,并且选择了复制(或剪切)命令,那么被选中的数据就被放入了剪切板
- 当用户选择了粘贴命令,在剪切板中的数据就被复制到了当前应用中。
在iOS中,剪切板也用来支持查找操作。并且,你可以使用自定义的URL机制代替复制,剪切,粘贴操作,让剪切板在应用间传输数据。具体信息参照Info.plist的设置。
抛开具体操作,剪切板的基本功能是往里写数据和从中读数据。虽然概念简单,但是包含了诸多细节。主要复杂的地方在于数据的表达有多种方式,这种复杂性就导致了也需要考虑性能。下面就会讨论一下这些问题。
命名剪切板
剪切板可能是共有的或是私有的。共有的剪切板被称作系统剪切板,私有的剪切板是应用创建的,因此被称作应用剪切板。剪切板必须拥有一个唯一的名字。UIPasteboard定义了两个系统剪切板,它们的名字和用处如下:
- UIPasteboardNameGeneral 涉及多种类型数据的剪切,复制和粘贴操作。你可以通过generalPasteboard 类方法获取这个单例。
- UIPasteboardNameFind 用作查找操作。用户在UISearchBar中输入的字符串会被写入到该剪切板中,因此可以在应用间共享。你可以通过调用 pasteboardWithName:create:类方法,传入UIPasteboardNameFind参数来获取该对象。
通常你都会使用系统剪切板,如果有必要你可以通过调用cpasteboardWithName:create:方法创建你自己的应用剪切板。调用pasteboardWithUniqueName这个方法也可以,一个拥有唯一名字的应用剪切板会被创建出来,名字可以通过剪切板对象的name属性查看。
剪切板的持久化
剪切板能够被持久化。如果剪切板被持久化,可以在之前的应用退出之后或者系统重启之后使用。系统剪切板是持久化的。虽然应用剪切板默认是不持久化的,你可以通过设置persistent属性为YES来使他们变成可持久化的。不持久化的剪切板在应用退出时就没了,持久化的剪切板在应用卸载时被移除掉。
剪切板持有者和条目
最后一次向剪切板中加入数据的对象被视作剪切板持有者。每一条向剪切板中加入的数据被视作剪切板条目。剪切板可以持有一条或多条条目。应用能够加入和跟踪任意数量的条目。例如,比如一个用户选择了文字+图片。他们被复制到剪切板时,文本和图片作为不同的条目。应用读取多个条目,可以选择那些需要支持的(例如,只需要处理文字而不需要处理图片)。
表示与UTI
剪切板操作经常是跨应用的。没有应用可以知道另外一个应用可以处理哪些数据。为了最大化这种共享的能力,剪切板可以对一个剪切板条目进行多种表示。例如,一个富文本编辑器可能对一份拷贝数据提供了HTML,PDF和纯文本的表示。一个剪切板条目包括了应用所能提供的所有表示。
每一个剪切板条目通常使用唯一类型标识(UTI)。(UTI就是一个唯一的字符串,用来标记数据类型。)UTI提供了一个统一的标记数据类型的方式。如果你有一个自定义的数据类型想要支持,你必须为它创建一个唯一的标识。对此,你可以使用逆向域名来确保唯一性,例如,一个自定义的类型表示可以是com.myCompany.myApp.myType。更多有关UTI的内容,查看Uniform Type Identifiers Overview。
举个例子,假设一个应用支持富文本和图片的选中。它可能想将富文本的Unicode版本和图片的多种表示都放到剪切板中。每一个条目的每种表示被存储在它自身的数据中,如下图:
通常,为了最大化能够共享数据的可能性,剪切板条目应当包含尽可能多的表示。
一个剪切板的读者必须找到最合适的数据类型。典型地,这意味着选择能够支持的最复杂类型。例如,对于一份拷贝的文本数据,文本编辑器应该提供HTML和普通文本的表示。一个能够支持富文本的应用应该使用HTML的表示,而仅能支持普通文本的应用就使用普通文本的版本。
改变数
改变数是剪切板的变量,每次当剪切板的内容发生变化时,它就增加。具体指的是剪切板条目加入、修改或者删除时。通过检查改变数,应用能够检查当前剪切板中的数据是否跟上次获取到的一致。每次改变数增加时,剪切板会给观察者发送通知。
第一步:识别选中并展示编辑菜单
如果你想使用复制,剪切或者粘贴之类的东西,你首先需要选中(一个粘贴操作通常在一个空选中上操作,例如一个光标)。在选中一个条目之后,你应该展示展示编辑菜单。编辑菜单是一个系统菜单,它可能包含以下命令:拷贝,剪切,粘贴,选中和全选。编辑菜单指向被选中内容。当用户点击一个菜单项时,协议UIResponderStandardEditActions中的方法(诸如cut: paste:等方法)就会被调用。
获取更多有关选中以及菜单的展示管理的内容,查看Managing the Selection and the Edit Menu
复制和剪切
当用户点击复制后者剪切命令式,系统会调用相应对象的copy:或者cut:方法。通常是第一响应者实现了这些方法,如果第一响应者没有实现这些方法,消息会沿着响应链传递。非正式协议UIResponderStandardEditActions中定义了这些方法。
注意:任何类可实现UIResponderStandardEditActions中的方法,但是最好是继承UIResponder,并在响应链中的类。
实现copy:或者cut:方法时,你应该向剪切板中写入选中数据的尽可能多的表达。这些操作包括以下步骤(假设只有一个剪切板条目):
从选中对象中获取数据。二进制数据一定要包在NSData对象中。如果要写入其他类型的数据,那么必须是属性列表对象——也就是说,是如下类型的对象:NSString, NSArray, NSDictionary, NSDate, NSNumber, 或者 NSURL。更多内容,查看Property List Programming Guide
如果可以,产生一个或更多该对象或数据的表达。例如,如果之前选中了一个UIImage对象,你可以使用UIImageJPEGRepresentation和UIImagePNGRepresentation函数来将该图片转换成不同的表示。
获取剪切板对象。通常获取通用剪切板,调用 generalPasteboard类方法。
对于写入剪切板的条目的每一种表示设置一个UTI。
-
写入数据到剪切板的第一个位置,对于不同的数据类型:
- 如果是数据对象,发送setData:forPasteboardType: 消息到剪切板对象
- 如果是属性列表对象,发送setValue:forPasteboardType: 消息到剪切板。
-
如果命令是剪切,从应用的数据中移除选中的对象并更新你的页面。
- (void)copy:(id)sender { UIPasteboard *gpBoard = [UIPasteboard generalPasteboard]; ColorTile *theTile = [self colorTileForOrigin:currentSelection]; if (theTile) { NSData *tileData = [NSKeyedArchiver archivedDataWithRootObject:theTile]; if (tileData) [gpBoard setData:tileData forPasteboardType:ColorTileUTI]; } } - (void)cut:(id)sender { [self copy:sender]; ColorTile *theTile = [self colorTileForOrigin:currentSelection]; if (theTile) { CGPoint tilePoint = theTile.tileOrigin; [tiles removeObject:theTile]; CGRect tileRect = [self rectFromOrigin:tilePoint inset:TILE_INSET]; [self setNeedsDisplayInRect:tileRect]; } }
粘贴选中内容
当用户点击粘贴命令是,系统调用了响应者的paste:方法。同样,该消息也会从第一响应者开始沿响应链传送。
实现paste:方法时,你从剪切板中以一种能够支持的表示读入对象。然后添加被粘贴对象到你的应用中的数据中,并在页面中展示。该操作包含以下步骤:
获取一个剪切板对象
验证剪切板的第一个对象包含应用能支持的表示。通过调用 containsPasteboardTypes:或者pasteboardTypes方法获取所有表示。注意,你需要在canPerformAction:withSender:中执行该步骤。
-
如果剪切板的第一个条目包含该应用能够处理的数据,调用下面方法中的一个来读数据:
- dataForPasteboardType: 如果要读的数据包含在NSData对象中,调用此方法。
- valueForPasteboardType: 如果要读的数据为属性列表数据,调用此方法。
将该对象添加到应用的数据中。
-
展示该数据。
- (void)paste:(id)sender { UIPasteboard *gpBoard = [UIPasteboard generalPasteboard]; NSArray *pbType = [NSArray arrayWithObject:ColorTileUTI]; ColorTile *theTile = [self colorTileForOrigin:currentSelection]; if (theTile == nil && [gpBoard containsPasteboardTypes:pbType]) { NSData *tileData = [gpBoard dataForPasteboardType:ColorTileUTI]; ColorTile *theTile = (ColorTile *)[NSKeyedUnarchiver unarchiveObjectWithData:tileData]; if (theTile) { theTile.tileOrigin = self.currentSelection; [tiles addObject:theTile]; CGRect tileRect = [self rectFromOrigin:currentSelection inset:TILE_INSET]; [self setNeedsDisplayInRect:tileRect]; } } }
结束操作
当cut:, copy: 或者 paste:方法返回时,编辑菜单自动隐藏。你可以让他保持可见,具体可查看Dismissing the Edit Menu