本节书摘来自异步社区《iOS 6高级开发手册(第4版)》一书中的第2章,第2.4节访问基本的设备信息,作者 【美】Erica Sadun,更多章节内容可以访问云栖社区“异步社区”公众号查看
2.4 秘诀:展示活动视图控制器
iOS 6高级开发手册(第4版)
iOS 6新引入的活动视图控制器可以把数据活动集成进图2-4所示的界面中。你只需付出最少的开发成本,这种新型控制器就允许你的用户把项目复制到粘贴板上,发布给社交媒体,以及通过电子邮件和文本共享它们等。内置的活动包括:Facebook(脸书)、Twitter(推特)、Weibo(微博)、SMS、邮件、打印、复制到粘贴板以及把数据分配给联系人。应用程序也可以定义它们自己的自定义服务,在本节后面可以了解这一点:
UIActivityTypePostToFacebook
UIActivityTypePostToTwitter
UIActivityTypePostToWeibo
UIActivityTypeMessage
UIActivityTypeMail
UIActivityTypePrint
UIActivityTypeCopyToPasteboard
UIActivityTypeAssignToContact
![b65a0e3797b0d1900efe51649886390f7bb80d48](https://i-blog.csdnimg.cn/blog_migrate/b41ff198437ff22527cb0eac594544da.png)
这个列表中遗漏了两个重要的活动,即Open in和QuickLook,前者用于在应用程序之间共享文档,后者用于预览文件。本章后面将讨论这种功能,并且利用一些秘诀说明了如何独立地支持这些特性,对于QuickLook来说,则是如何集成进活动视图控制器中。
注意:
无需拥有一台AirPrint打印机以测试打印活动。Ecamm的Printopia(http://www.ecamm.com/mac/printopia/,19.95美元)可以在你的本地网络上创建一台虚拟打印机,可以从设备和模拟器中使用它。可以选择打印到任何本地打印机、打印到Mac上的文或者打印到Dropbox上的文件。对于利用新的活动视图控制器从事的任何开发工作,这都是一笔极好的投资。Netputing制造了一款名为handyPrint的类似产品,handyPrint接受PayPal捐赠。
2.4.1 展示活动视图控制器
展示控制器的方式因设备而异,可以在iPhone家族成员上以模态方式以及在平板电脑上以弹出窗口(popover)显示它。UIBarButtonSystemItemAction图标提供了用于填充链接到这个控制器的栏按钮的完美方式。
最重要的是,你自己几乎不需要做任何工作。在用户选择一种活动之后,控制器将会处理所有进一步的交互,比如展示邮件或Twitter创作单,向机载库中添加图片,或者把它分配给联系人。
2.4.2 活动项目源
秘诀2-4通过代码创建并展示了视图控制器。这种实现使它的主类采用UIActivityItemSource协议,并把自身添加到传递给控制器的项目数组中。在获取数据项目时,采用源协议有助于控制器理解如何使用回调。这表现了创建和展示控制器的两种方式中的第一种。
协议的两个必需的方法提供项目以进行处理(将用于活动的数据),并给该项目提供一个占位符。项目对应于适合于给定活动类型的对象。可以基于传递给回调的活动的类型,来区分返回的对象。例如,你可能发推特说:“我在应用程序名称中创建了一首优秀的歌曲”,但是你可能通过电子邮件发送实际的声音文件。
用于项目的占位符通常是返回的与项目相同的数据,除非具有必须处理或创建的对象。在这种情况下,可以创建一个不带有真实数据的占位符对象。
两个回调(项目和占位符)都在主线程上运行,因此要保持数据比较小。如果需要大量的处理数据,可以考虑代之以使用提供者。
2.4.3 项目提供者
UIActivityItemProvider类允许延迟传递数据。在共享数据前,这种操作可以给你提供操纵数据的灵活性。例如,在可以把大型视频文件上传到社交共享站点或者从较大的序列中对某个音频进行二次抽样之前,可能需要先对它们进行处理。
可以子类化提供者类并实现item方法。它可以代替通常用于操作的main方法。生成处理过的数据,在知道它将异步运行并且不会阻碍用户的交互式体验的情况下它将是安全的。
2.4.4 项目源回调
回调方法允许基于彼此之间的预期用法来区分数据。使用活动类型(比如Facebook或者Add to Contacts,在本节前面列出了它们),选择你想提供的准确数据。在为不同的应用选择分辨率时,这特别重要。在打印时,要保持数据具有较高的质量。在发推特时,低分辨率的图像也可能达到想要的结果。
如果数据是不变的,也就是说,传送给Facebook的数据与传送给电子邮件的相同,就可以直接提供数据项的数组(通常是字符串、图像和URL)。例如,可以像下面这样创建控制器,它使用单独一幅图像:
UIActivityViewController *activity = [[UIActivityViewController alloc]
initWithActivityItems:@[imageView.image]
applicationActivities:nil];
这种直接的方法要简单得多。主类不需要声明项目源协议;不需要实现额外的方法。它是一种为简单项目管理活动的快速、容易的方式。
也不仅限于传递单个项目,可以根据需要在活动项目数组中包括额外的元素。下面的控制器可能把它的两幅图像添加到电子邮件中,或者把它们都保存到系统的相机胶卷中,这依赖于用户的选择。拓宽活动以使用多个项目将允许用户更高效地使用你的应用程序:
UIImage *secondImage = [UIImage imageNamed:@"Default.png"];
UIActivityViewController *activity = [[UIActivityViewController alloc]
initWithActivityItems:@[imageView.image, secondImage]
applicationActivities:nil];
秘诀2-4 活动视图控制器
- (void) presentViewController:
(UIViewController *)viewControllerToPresent
{
if (IS_IPHONE)
{
[self presentViewController:viewControllerToPresent
animated:YES completion:nil];
}
else
{
popover = [[UIPopoverController alloc]
initWithContentViewController:viewControllerToPresent];
popover.delegate = self;
[popover presentPopoverFromBarButtonItem:
self.navigationItem.leftBarButtonItem
permittedArrowDirections:UIPopoverArrowDirectionAny
animated:YES];
}
}
// Return the item to process
- (id)activityViewController:
(UIActivityViewController *)activityViewController
itemForActivityType:(NSString *)activityType
{
return imageView.image;
}
// Return a thumbnail version of that item
- (id)activityViewControllerPlaceholderItem:
(UIActivityViewController *)activityViewController
{
return imageView.image;
}
// Create and present the view controller
- (void) action
{
UIActivityViewController *activity =
[[UIActivityViewController alloc]
initWithActivityItems:@[self] applicationActivities:nil];
[self presentViewController:activity];
}
获取这个秘诀的代码
要查找这个秘诀的完整示例项目,可以浏览https://github.com/erica/iOS-6-Advanced-Cookbook,并进入第2章的文件夹。
2.4.5 添加服务
每个应用程序都可以通过子类化UIActivity类并展示一个自定义的视图控制器,来提供特定于应用程序的服务。视图控制器使用户能够以某种方式处理传递的数据。程序清单2-1介绍了一种最基本的活动,用于展示一个简单的文本视图。该视图列出了通过活动控制器传递给它的项目,它显示了每个项目的类和描述。
这个程序清单包括两个独特的类的详细信息。第一个类实现一个简单的文本控制器,并且打算在导航层次结构内使用。它包括一个视图和一个处理程序,前者用于展示文本,后者用于在用户点按Done时通过发送activityDidFinish:来更新UIActivity调用实例。
添加一种方式以使活动完成是重要的,尤其是当控制器没有自然的终点时。在你的动作把数据上传到FTP服务器时,你就知道它何时完成。如果它发出消息,你就知道状态何时发布。在这个示例中,它取决于用户确定这个活动何时完成。确保视图控制器包含一个指回这个活动的弱属性,以便在工作结束时发送确实完成方法。
活动类包含许多必需的和可选的项目。应该实现这个程序清单中显示的所有方法。支持自定义的活动的方法包括以下一些。
activityType:返回一个描述活动类型的独特字符串。在系统提供的活动中,与这个字符串对应的活动之一是UIActivityTypePostToFacebook。使用类似的命名模式。这个字符串用于确定特定的活动类型以及它将做什么。在这个程序清单中,我返回的是“@"CustomActivityTypeListItemsAndTypes"”,它描述了活动。
activityTitle:提供你想在活动控制器中显示的文本。图2-5中的自定义文本就是由这个方法返回的。在描述自定义的动作时可以使用活动的描述。可以遵照Apple的指导,例如,“Save to Camera Roll”(保存到相机胶卷)、“Print”(打印)、“Copy”(复制)。你的标题应该完成短语“I want to...”(我想要……),例如,“I want to print”(我想要打印)、“I want to copy”(我想要复制),或者在这个示例中,“I want to list items”(我想要列出项目)。使用标题大小写形式,并且除了像“to”和“and”这样的次要单词之外,还要大写每个单词的首字母。
activityImage:返回一幅图像让控制器使用。控制器将添加一个反斜杠,并把图像转换成一幅单值位图(one-value bitmap),然后把它分层放置在顶部。在透明背景上使用简单的艺术作品(在iPhone上是57像素×57像素,在iPad上是72像素×72像素,对于Retina屏幕比例则要加倍),构建图标图像的内容。你将希望在插入艺术作品时至少距离每一边15%,以留出空间插入控制器提供的圆角矩形,给图像加上框架。
canPerformWithActivityItems:扫描传递的项目,并且决定控制器是否可以处理它们。如果是,就返回YES。
prepareWithActivityItems:存储传递的项目以便以后使用(在这里,把它们分配给一个局部实例变量),并且执行任何必要的预处理。
activityViewController:使用以前传递给你的活动项目,返回一个完全初始化的、像样的视图控制器。这个控制器将被自动展示给用户,她在那里执行承诺的动作之前可以自定义选项。
![159305228601c01dd0dcd12100524395b8699631](https://i-blog.csdnimg.cn/blog_migrate/d0c986a170911e34042d570ef93e7f99.png)
添加自定义的动作允许应用程序扩展其数据处理可能性,同时把一些特性集成进一致的系统提供的界面中。它是一种强大的iOS特性。最强大的活动选择将与系统服务相集成(比如复制到粘贴板,或者保存到相册),或者提供对脱离设备的API的连接,比如Facebook、Twitter、Dropbox和FTP。
这个示例只是简单地列出项目,代表一种弱用例。没有理由不能把相同的特性提供为正常的应用程序中的屏幕。在考虑“动作”时,要尝试延伸到应用程序之外。把用户的数据与扩展到正常的GUI之外的共享与处理特性连接起来。
程序清单2-1 应用程序活动
// All activities present a view controller. This custom controller
// provides a full-sized text view.
@interface TextViewController : UIViewController
@property (nonatomic, readonly) UITextView *textView;
@property (nonatomic, weak) UIActivity *activity;
@end
@implementation TextViewController
// Make sure you provide a done handler of some kind, such as this
// or an integrated button that finishes and wraps up
- (void) done
{
[_activity activityDidFinish:YES];
}
// Just a super-basic text view controller
- (id) init
{
if (!(self = [super init])) return nil;
_textView = [[UITextView alloc] init];
_textView.font = [UIFont fontWithName:@"Futura" size:16.0f];
_textView.editable = NO;
[self.view addSubview:_textView];
PREPCONSTRAINTS(_textView);
STRETCH_VIEW(self.view, _textView);
// Prepare a Done button
self.navigationItem.rightBarButtonItem =
BARBUTTON(@"Done", @selector(done));
return self;
}
@end
@interface MyActivity : UIActivity
@end
@implementation MyActivity
{
NSArray *items;
}
// A unique type name
- (NSString *)activityType
{
return @"CustomActivityTypeListItemsAndTypes";
}
// The title listed on the controller
- (NSString *) activityTitle
{
return @"Cookbook";
}
// A custom image, displayed as a bitmap over a textured background
// This one says "iOS" in a rounded rect edge
- (UIImage *) activityImage
{
CGRect rect = CGRectMake(0.0f, 0.0f, 75.0f, 75.0f);
UIGraphicsBeginImageContext(rect.size);
rect = CGRectInset(rect, 15.0f, 15.0f);
UIBezierPath *path = [UIBezierPath
bezierPathWithRoundedRect:rect cornerRadius:4.0f];
[path stroke];
rect = CGRectInset(rect, 0.0f, 10.0f);
[@"iOS" drawInRect:rect
withFont:[UIFont fontWithName:@"Futura" size:18.0f]
lineBreakMode:NSLineBreakByWordWrapping
alignment:NSTextAlignmentCenter];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
// Specify if you can respond to these items
- (BOOL)canPerformWithActivityItems:(NSArray *)activityItems
{
return YES;
}
// Store the items locally for later use
- (void)prepareWithActivityItems:(NSArray *)activityItems
{
items = activityItems;
}
// Return a view controller, in this case one that lists
// its items and their classes
- (UIViewController *) activityViewController
{
TextViewController *tvc = [[TextViewController alloc] init];
tvc.activity = self;
UITextView *textView = tvc.textView;
NSMutableString *string = [NSMutableString string];
for (id item in items)
[string appendFormat:
@"%@: %@\n", [item class], [item description]];
textView.text = string;
// Make sure to provide some kind of done: handler in
// your main controller.
UINavigationController *nav = [[UINavigationController alloc]
initWithRootViewController:tvc];
return nav;
}
@end
2.4.6 项目和服务
为每个项目展示的服务因传递的数据种类而异。表2-1列出了由源数据类型提供的活动。如同在本章中所看到的,预览控制器支持扩展到了这些基础类型之外。
iOS的Quick Look(快速查看)框架把活动控制器集成到它的文件预览中。Quick Look提供的活动控制器可以打印并通过电子邮件发送许多类型的文档。一些文档类型也支持其他的活动。
文档交互控制器(Document Interaction Controller)提供了“open in”特性,允许在应用程序之间共享文件。它将把活动添加到它的“选项”样式表示中,并把活动与“open in”选择结合起来。
![c9c876f988f7b6437fe6f57cedf98358ea249661](https://i-blog.csdnimg.cn/blog_migrate/d594cf26df0ec6216e017078448035e4.png)
所有支持类型的联合体;例如,对于字符串+图像,可以获得:Mail(邮件)、Message(消息)、Twitter(推特)、Facebook(脸书)、Weibo(微博)、Assign to Contact(分配给联系人)、Save to Camera Roll(保存到相机胶卷)、Print(打印)和Copy(复制)
2.4.7 支持HTML电子邮件
如果你想使用电子邮件活动发送HTML,就要确保项目的文本字符串开始于“@""”。只要实现项目源协议,并且依据用户所选的活动返回合适的项目,就可以把基于HTML的电子邮件文本与普通的Twitter内容区分开。
2.4.8 排除活动
可以通过给excludedActivityTypes属性提供一份活动类型的列表,明确地排除一些活动:
UIActivityViewController *activity = [[UIActivityViewController alloc]
initWithActivityItems:items applicationActivities:@[appActivity]];
activity.excludedActivityTypes = @[UIActivityTypeMail];