文章转至个人我的博客: https://cainluo.github.io/15132142909284.htmlhtml
众所周知iOS
是一个封闭的系统, 每一个App
都有一个属于它们本身的沙盒, App
与App
之间是不能够互相访问的, 也因为这一点, iOS
也能够被称为安全的系统.git
但这又会带来另外一个问题, 就是在管理文件的方式上比较复杂, 用户没法自由的浏览文件, 咱们须要专门的工具与流程才能在iOS
上的应用程序之间共享文件.github
为了解决这个问题, 苹果爸爸在iOS 11
里加入了一个用于管理文件的新工具:UIDocumentBrowserViewController
.数组
这个全新的视图控制器可让咱们建立文档浏览器的App
, 就像iOS 11
上的新的文件App
同样, 可让咱们自由管理全部可用位置上的文档.浏览器
PS: 这篇文章所演示的
Demo
在模拟器上是不能正常工做的, 最好备好iOS 11
的设备来构建和测试该App
.安全
转载声明:如须要转载该文章, 请联系做者, 而且注明出处, 以及不能擅自修改本文.app
在开始建立这个项目以前, 咱们要了解iOS Document
的工做原理, UIDocument
是一个几乎运行在全部基于文档的应用的强大类.异步
UIDocument
是一个物理文件的包装, 它能够在咱们的设备, iCloud
上异步读取/写入文件, 自动保存, 文件版本控制等等.ide
虽然咱们能够不用强制性使用UIDocument
, 但我仍是强烈建议去使用, 由于它能够帮咱们省下不少事情, 好比咱们须要在iPhone
和MacBook
上使用同一个iCloud
文件, UIDocument
就能够帮咱们处理全部的事情.工具
提供文档的App
扩展容许其余App
在你的文件上进行修改, 避免沙盒化.
而UIDocumentPickerViewController
就是该扩展的一部分, 这是其余App
将显示你的App
中选择文档的可视化界面.
提供文档的App
容许其余App
从它自己内导入文件, 这也就说明这个文件会被拷贝一份, 而原始文件则会保持不变.
固然咱们也能够容许其余App
直接打开提供文档的App
, 直接选择文件进行处理并覆盖源文件.
这里咱们就讲UIDocumentBrowserViewController
, 它是一种App
的类型, 并非咱们所写的App
的扩展名.
咱们定制的UIDocumentBrowserViewController
子类必须是App
的根视图, 换一句话说, 这就是iOS 11
文档类型App
的自定义实现.
这里咱们就拿颜色管理来做为场景, 在文件浏览器上去管理这些RGB
颜色值, 它的扩展名也定义为.color
.
咱们在建立好工程以后, 须要在Info.plist
里把UISupportsDocumentBrowser
设置为YES
.
咱们在使用.color
扩展名的文件时, 须要用逗号分隔RGB
颜色值, 好比白色的话, 咱们就会声明为255,255,255
.
在此, 若是要使得文档浏览器中支持自定义的.color
扩展名, 咱们须要注册一个新的赞成类型标识符(UTI这个东西在上一章文章的末尾就有介绍, )
, 而后咱们须要将新的文件与咱们的App
关联.
打开咱们的项目找到Info
这个选项, 而后把咱们自定义的内容填好:
Exported UTIs
所填写的内容:
RGB Color File
com.cainluo.colorExtension
UTI
能够符合其余类型, 就好像父类子类同样, 这里咱们就写public.text
, 由于咱们的.color
也是简单的文本类型, 若是你是HTML
的话, 那你能够写成public.html
.写完这个以后, 咱们还须要指定扩展名, 展开Additional exported UTI properties
, 填入一个UTTypeTagSpecification
字典, 而后在这个字典里建立一个名为public.filename-extension
的数组, 最后再填入一个color
对象.
定义好这个UTI
以后, 咱们须要在Document Types
填入对应的Name
和Types
, Name
就填入Color Extension
, Types
就填入com.razeware.colorExtension
.
若是你想了解更多关于UTI
的内容, 能够去apple.co/2v0FiHO看看.
建立好对应的控制器以后, 咱们要设置一下:
self.delegate = self;
self.allowsDocumentCreation = YES;
复制代码
而后遵照UIDocumentBrowserViewControllerDelegate
和UIViewControllerTransitioningDelegate
两个协议, 而且实现它们的代理方法:
#pragma mark - UIViewControllerTransitioningDelegate
- (nullable id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented
presentingController:(UIViewController *)presenting
sourceController:(UIViewController *)source {
return self.documentBrowserTransitionController;
}
#pragma mark - UIDocumentBrowserViewControllerDelegate
- (void)documentBrowser:(UIDocumentBrowserViewController *)controller
didRequestDocumentCreationWithHandler:(void(^)(NSURL *_Nullable urlToImport, UIDocumentBrowserImportMode importMode))importHandler {
NSURL *url = [[NSBundle mainBundle] URLForResource:@"ColorFile"
withExtension:@"color"];
importHandler(url, UIDocumentBrowserImportModeCopy);
}
- (void)documentBrowser:(UIDocumentBrowserViewController *)controller
didImportDocumentAtURL:(NSURL *)sourceURL
toDestinationURL:(NSURL *)destinationURL {
[self presentColorControllerWithDocument:[[ColorDocument alloc] initWithFileURL:destinationURL]];
}
- (void)documentBrowser:(UIDocumentBrowserViewController *)controller
failedToImportDocumentAtURL:(NSURL *)documentURL
error:(NSError * _Nullable)error {
[self showAlertViewControllerWithTitle:@"Failed" message:@"Failed to import"];
}
- (void)documentBrowser:(UIDocumentBrowserViewController *)controller
didPickDocumentURLs:(NSArray <NSURL *> *)documentURLs {
[self presentColorControllerWithDocument:[[ColorDocument alloc] initWithFileURL:documentURLs[0]]];
}
- (NSArray<__kindof UIActivity *> *)documentBrowser:(UIDocumentBrowserViewController *)controller
applicationActivitiesForDocumentURLs:(NSArray <NSURL *> *)documentURLs {
ColorDocument *colorDocument = [[ColorDocument alloc] initWithFileURL:documentURLs[0]];
return @[[[DocumentActivity alloc] initDocumentActivityWithColorDocument:colorDocument]];
}
复制代码
另外咱们还须要配置一些东西, 而且使得能够跳转到咱们须要跳转的编辑页面:
#pragma mark - Present Controller
- (void)presentColorControllerWithDocument:(ColorDocument *)colorDocument {
UIStoryboard *mineStoryboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
ColorController *colorController = [mineStoryboard instantiateViewControllerWithIdentifier:@"ColorController"];
colorController.colorDocument = colorDocument;
colorController.transitioningDelegate = self;
self.documentBrowserTransitionController = [self transitionControllerForDocumentURL:colorDocument.fileURL];
[self presentViewController:colorController animated:YES completion:nil];
}
复制代码
解释一下代理方法:
- (void)documentBrowser:(UIDocumentBrowserViewController *)controller
didImportDocumentAtURL:(NSURL *)sourceURL
toDestinationURL:(NSURL *)destinationURL;
复制代码
ColorDocument
对象, 而ColorDocument
又是UIDocument
的子类, 负责保存和加载色彩文件, 待会咱们就会ColorController
里预览和编辑颜色文件.- (void)documentBrowser:(UIDocumentBrowserViewController *)controller
didRequestDocumentCreationWithHandler:(void(^)(NSURL *_Nullable urlToImport, UIDocumentBrowserImportMode importMode))importHandler;
复制代码
importHandler
回调中传递的文件时.- (void)documentBrowser:(UIDocumentBrowserViewController *)controller
failedToImportDocumentAtURL:(NSURL *)documentURL
error:(NSError * _Nullable)error;
复制代码
UIDocumentBrowserViewController
支持打开多个文件, 但咱们这里只须要使用一个, 因此就只去第一个.- (void)documentBrowser:(UIDocumentBrowserViewController *)controller
didPickDocumentURLs:(NSArray <NSURL *> *)documentURLs;
复制代码
打开ColorController
以后, 咱们须要设置一下, 看看是否能够读取对应的文件:
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
ColorDocument *colorDocument = self.colorDocument;
if (!colorDocument) {
return;
}
if (colorDocument.documentState == UIDocumentStateNormal) {
[self configControllerUI];
} else {
[colorDocument openWithCompletionHandler:^(BOOL success) {
if (success) {
[self configControllerUI];
} else {
[self showAlertViewControllerWithTitle:@"Error"
message:@"Can't Open Document"];
}
}];
}
}
复制代码
为了能够保存修改后的内容, 这里用了一个保存的方法:
- (IBAction)saveColorModel:(UIButton *)sender {
ColorDocument *colorDocument = self.colorDocument;
if (!colorDocument) {
return;
}
colorDocument.colorModel = [[ColorModel alloc] initColorModelWithRedValue:self.redSlider.value
greenValue:self.greenSlider.value
blueValue:self.blueSlider.value];
[colorDocument saveToURL:colorDocument.fileURL
forSaveOperation:UIDocumentSaveForOverwriting
completionHandler:^(BOOL success) {
if (success) {
[self showAlertViewControllerWithTitle:@"Success"
message:@"Saved file"];
} else {
[self showAlertViewControllerWithTitle:@"Error"
message:@"Failed to save file"];
}
}];
}
复制代码
若是咱们直接从文件App
中打开颜色文件, 你会发现咱们的App
是打开了, 但不会工做.
那是由于咱们的App
和.color
扩展名关联, 但咱们的编辑页面并无显示.
因为这个文件是在别的App
里打开的, 因此咱们须要在AppDelegate
里处理这个事情:
- (BOOL)application:(UIApplication *)app
openURL:(NSURL *)url
options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
if (!url.isFileURL) {
return NO;
}
DocumentBrowserController *documentBrowserController = (DocumentBrowserController *)self.window.rootViewController;
if (!documentBrowserController) {
return NO;
}
[documentBrowserController revealDocumentAtURL:url
importIfNeeded:YES
completion:^(NSURL * _Nullable revealedDocumentURL, NSError * _Nullable error) {
if (error) {
return;
}
[documentBrowserController presentColorControllerWithDocument:[[ColorDocument alloc] initWithFileURL:revealedDocumentURL]];
}];
return YES;
}
复制代码
在这里面咱们还能够自定义一下咱们的文档浏览器样式:
#pragma mark - Custom Document Browser Controller
- (void)customDocumentBrowserController {
self.view.tintColor = [UIColor colorNamed:@"MarineBlue"];
self.browserUserInterfaceStyle = UIDocumentBrowserUserInterfaceStyleLight;
UIDocumentBrowserAction *documentBrowserAction = [[UIDocumentBrowserAction alloc] initWithIdentifier:@"com.cainluo.action"
localizedTitle:@"Lighter Color"
availability:UIDocumentBrowserActionAvailabilityMenu
handler:^(NSArray<NSURL *> * _Nonnull urls) {
ColorDocument *colorDocument = [[ColorDocument alloc] initWithFileURL:urls[0]];
[colorDocument openWithCompletionHandler:^(BOOL success) {
if (success) {
colorDocument.colorModel = [colorDocument.colorModel lighterColorWithToAdd:60];
[self presentColorControllerWithDocument:colorDocument];
}
}];
}];
documentBrowserAction.supportedContentTypes = @[@"com.cainluo.colorExtension"];
self.customActions = @[documentBrowserAction];
UIBarButtonItem *aboutButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"About"
style:UIBarButtonItemStylePlain
target:self
action:@selector(openAbout)];
self.additionalTrailingNavigationBarButtonItems = @[aboutButtonItem];
}
- (void)openAbout {
[self showAlertViewControllerWithTitle:@"关于咱们" message:@"Color Document 1.0 by Cain Luo"];
}
复制代码
这个颜色值是设置在Assets.xcassets
中.
咱们能够为演示文稿添加一个动画:
@property (nonatomic, strong) UIDocumentBrowserTransitionController *documentBrowserTransitionController;
复制代码
#pragma mark - UIViewControllerTransitioningDelegate
- (nullable id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented
presentingController:(UIViewController *)presenting
sourceController:(UIViewController *)source {
return self.documentBrowserTransitionController;
}
复制代码
咱们刚刚的那个present
方法里就写好了对应的配置:
colorController.transitioningDelegate = self;
self.documentBrowserTransitionController = [self transitionControllerForDocumentURL:colorDocument.fileURL];
复制代码
最后面咱们还能够添加多一个活动列表, 这个时候咱们就须要定义一个UIActivity
的自雷, 而且将.color
文件以字符串的形式复制到粘贴板上:
- (instancetype)initDocumentActivityWithColorDocument:(ColorDocument *)colorDocument {
self = [super init];
if (self) {
self.colorDocument = colorDocument;
}
return self;
}
+ (UIActivityCategory)activityCategory {
return UIActivityCategoryAction;
}
- (UIActivityType)activityType {
return @"ColorBrowserCopy";
}
- (NSString *)activityTitle {
return @"Color Copy";
}
- (UIImage *)activityImage {
return [UIImage imageNamed:@"copy_activity_icon"];
}
- (BOOL)canPerformWithActivityItems:(NSArray *)activityItems {
return YES;
}
- (void)performActivity {
[self.colorDocument openWithCompletionHandler:^(BOOL success) {
if (success) {
NSString *string = [self.colorDocument stringRepresentation];
if (string) {
[UIPasteboard generalPasteboard].string = string;
[self activityDidFinish:YES];
}
}
}];
}
复制代码
最后咱们用一个代理方法设置对应的活动事件:
#pragma mark - Custom Activity
- (NSArray<__kindof UIActivity *> *)documentBrowser:(UIDocumentBrowserViewController *)controller
applicationActivitiesForDocumentURLs:(NSArray <NSURL *> *)documentURLs {
ColorDocument *colorDocument = [[ColorDocument alloc] initWithFileURL:documentURLs[0]];
return @[[[DocumentActivity alloc] initDocumentActivityWithColorDocument:colorDocument]];
}
复制代码
https://github.com/CainLuo/iOS-11-Characteristic/tree/master/6.Document