玩轉iOS開發:iOS 11 新特性《基於文檔管理的App》

文章轉至個人我的博客: https://cainluo.github.io/15132142909284.htmlhtml


衆所周知iOS是一個封閉的系統, 每一個App都有一個屬於它們本身的沙盒, AppApp之間是不能夠互相訪問的, 也因爲這一點, iOS也能夠被稱爲安全的系統.git

但這又會帶來另外一個問題, 就是在管理文件的方式上比較複雜, 用戶沒法自由的瀏覽文件, 咱們須要專門的工具與流程才能在iOS上的應用程序之間共享文件.github

爲了解決這個問題, 蘋果爸爸在iOS 11里加入了一個用於管理文件的新工具:UIDocumentBrowserViewController.數組

這個全新的視圖控制器可讓咱們建立文檔瀏覽器的App, 就像iOS 11上的新的文件App同樣, 可讓咱們自由管理全部可用位置上的文檔.瀏覽器

PS: 這篇文章所演示的Demo在模擬器上是不能正常工做的, 最好備好iOS 11的設備來構建和測試該App.安全

轉載聲明:如須要轉載該文章, 請聯繫做者, 而且註明出處, 以及不能擅自修改本文.app

開始前的知識補充

在開始建立這個項目以前, 咱們要了解iOS Document的工做原理, UIDocument是一個幾乎運行在全部基於文檔的應用的強大類.異步

UIDocument是一個物理文件的包裝, 它能夠在咱們的設備, iCloud上異步讀取/寫入文件, 自動保存, 文件版本控制等等.ide

雖然咱們能夠不用強制性使用UIDocument, 但我仍是強烈建議去使用, 由於它能夠幫咱們省下不少事情, 好比咱們須要在iPhoneMacBook上使用同一個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填入對應的NameTypes, Name就填入Color Extension, Types就填入com.razeware.colorExtension.

2

若是你想了解更多關於UTI的內容, 能夠去apple.co/2v0FiHO看看.

開始寫代碼

建立好對應的控制器以後, 咱們要設置一下:

self.delegate = self;
    self.allowsDocumentCreation = YES;
複製代碼

而後遵照UIDocumentBrowserViewControllerDelegateUIViewControllerTransitioningDelegate兩個協議, 而且實現它們的代理方法:

#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"];
    
    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]];
}
複製代碼

最終效果

3

4

工程

https://github.com/CainLuo/iOS-11-Characteristic/tree/master/6.Document

最後

碼字很費腦, 看官賞點飯錢可好

支付寶
相關文章
相關標籤/搜索