专栏名称: CainLuo
iOS
目录
相关文章推荐
什么值得买  ·  这个节电小玩意,骗了多少农村老人?? ·  2 天前  
上海市场监管  ·  明天开始!新一轮消费券就按攻略这么领→ ·  3 天前  
上海市场监管  ·  明天开始!新一轮消费券就按攻略这么领→ ·  3 天前  
华人生活网  ·  手速要快!巴塔、Burberry、CK、汤米 ... ·  3 天前  
华人生活网  ·  手速要快!巴塔、Burberry、CK、汤米 ... ·  3 天前  
51好读  ›  专栏  ›  CainLuo

玩转iOS开发:iOS 11 新特性《基于文档管理的App》

CainLuo  · 掘金  ·  · 2017-12-14 16:53

正文

文章转至我的个人博客: https://cainluo.github.io/15132142909284.html


众所周知 iOS 是一个封闭的系统, 每个 App 都有一个属于它们自己的沙盒, App App 之间是不可以互相访问的, 也由于这一点, iOS 也可以被称为安全的系统.

但这又会带来另一个问题, 就是在管理文件的方式上比较复杂, 用户无法自由的浏览文件, 我们需要专门的工具与流程才能在 iOS 上的应用程序之间共享文件.

为了解决这个问题, 苹果爸爸在 iOS 11 里加入了一个用于管理文件的新工具: UIDocumentBrowserViewController .

这个全新的视图控制器可以让我们创建文档浏览器的 App , 就像 iOS 11 上的新的 文件App 一样, 可以让我们自由管理所有可用位置上的文档.

PS: 这篇文章所演示的 Demo 在模拟器上是不能正常工作的, 最好备好 iOS 11 的设备来构建和测试该 App .

转载声明:如需要转载该文章, 请联系作者, 并且注明出处, 以及不能擅自修改本文.

开始前的知识补充

在开始创建这个项目之前, 我们要了解 iOS Document 的工作原理, UIDocument 是一个几乎运行在所有基于文档的应用的强大类.

UIDocument 是一个物理文件的包装, 它可以在我们的设备, iCloud 上异步读取/写入文件, 自动保存, 文件版本控制等等.

虽然我们可以不用强制性使用 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 这个选项, 然后把我们自定义的内容填好:

1

Exported UTIs 所填写的内容:

  • Description: 文件名的描述, 这里输入 RGB Color File
  • Identifier: 文件唯一标识符, 这里我们输入为 com.cainluo.colorExtension
  • Conforms To: 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 .

2

如果你想了解更多关于 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];
}

解释一下代理方法:

  1. 在成功导入新文档的时候会调用
- (void)documentBrowser:(UIDocumentBrowserViewController *)controller
 didImportDocumentAtURL:(NSURL *)sourceURL
       toDestinationURL:(NSURL *)destinationURL;
  1. 这是在将要呈现新文档时会调用, 它接受一个 ColorDocument 对象, 而 ColorDocument 又是 UIDocument 的子类, 负责保存和加载色彩文件, 待会我们就会 ColorController 里预览和编辑颜色文件.
- (void)documentBrowser:(UIDocumentBrowserViewController *)controller
didRequestDocumentCreationWithHandler:(void(^)(NSURL *_Nullable urlToImport, UIDocumentBrowserImportMode importMode))importHandler;
  1. 在新文档无法导入时的时候就会调用, 比如当我们无权访问 importHandler 回调中传递的文件时.
- (void)documentBrowser:(UIDocumentBrowserViewController *)controller
failedToImportDocumentAtURL:(NSURL *)documentURL
                  error:(NSError * _Nullable)error;
  1. 当用户选择要在文档浏览器中打开文件时, 就会调用该方法, 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 是打开了, 但不会工作.

那是因为我们的 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"






请到「今天看啥」查看全文