iOS文本编程指南(六)

复制,剪切,粘贴操作

用户可以复制某个应用中的文本,图片,或者其他数据到另外一个地方,到该应用内或者另一个应用都可以。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:方法时,你应该向剪切板中写入选中数据的尽可能多的表达。这些操作包括以下步骤(假设只有一个剪切板条目):

  1. 从选中对象中获取数据。二进制数据一定要包在NSData对象中。如果要写入其他类型的数据,那么必须是属性列表对象——也就是说,是如下类型的对象:NSString, NSArray, NSDictionary, NSDate, NSNumber, 或者 NSURL。更多内容,查看Property List Programming Guide

  2. 如果可以,产生一个或更多该对象或数据的表达。例如,如果之前选中了一个UIImage对象,你可以使用UIImageJPEGRepresentation和UIImagePNGRepresentation函数来将该图片转换成不同的表示。

  3. 获取剪切板对象。通常获取通用剪切板,调用 generalPasteboard类方法。

  4. 对于写入剪切板的条目的每一种表示设置一个UTI。

  5. 写入数据到剪切板的第一个位置,对于不同的数据类型:

    • 如果是数据对象,发送setData:forPasteboardType: 消息到剪切板对象
    • 如果是属性列表对象,发送setValue:forPasteboardType: 消息到剪切板。
  6. 如果命令是剪切,从应用的数据中移除选中的对象并更新你的页面。

     - (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:方法时,你从剪切板中以一种能够支持的表示读入对象。然后添加被粘贴对象到你的应用中的数据中,并在页面中展示。该操作包含以下步骤:

  1. 获取一个剪切板对象

  2. 验证剪切板的第一个对象包含应用能支持的表示。通过调用 containsPasteboardTypes:或者pasteboardTypes方法获取所有表示。注意,你需要在canPerformAction:withSender:中执行该步骤。

  3. 如果剪切板的第一个条目包含该应用能够处理的数据,调用下面方法中的一个来读数据:

    • dataForPasteboardType: 如果要读的数据包含在NSData对象中,调用此方法。
    • valueForPasteboardType: 如果要读的数据为属性列表数据,调用此方法。
  4. 将该对象添加到应用的数据中。

  5. 展示该数据。

     - (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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 162,475评论 4 372
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,744评论 2 307
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 112,101评论 0 254
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,732评论 0 221
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 53,141评论 3 297
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 41,049评论 1 226
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,188评论 2 320
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,965评论 0 213
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,716评论 1 250
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,867评论 2 254
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,341评论 1 265
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,663评论 3 263
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,376评论 3 244
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,200评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,990评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 36,179评论 2 285
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,979评论 2 279

推荐阅读更多精彩内容