在RN中利用原生Document Interaction Controller来预览和打开文档

2017/5/15 posted in  iOS

在react-native开发中有一个功能需求为,打开之前已经下载到Documents中的文件。一开始本想用webview来加载文件。后来发现有UIDocumentInteractionController这个api来更好的完成这个功能。

首先在RN引用的类中加入UIDocumentInteractionControllerDelegate我们要用到这个来呈现预览视图:

NativeUtil.h

#import <Foundation/Foundation.h>
#import "RCTBridgeModule.h"
#import "UIView+React.h"       //要引入这个头文件,在rn中创建和加载原声视图


@interface NativeUtil : NSObject <RCTBridgeModule,UIDocumentInteractionControllerDelegate>

@end

在NativeUtil.m中实现该委托。定义一个暴露给RN的方法,在js中调用:

RCT_EXPORT_METHOD(ShowFileChooser: (RCTResponseSenderBlock)callback){
  
    NSString *filePath = @"ceshi007";           callback(@[[NSNull null],filePath]);    //测试callback,从native向rn传值。


    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);           //获取应用本沙盒内的目录。
    NSString *documentpath = paths[0];      //获取document的路径
    NSString *fileName = [documentpath stringByAppendingPathComponent:@"Screenshot_2016-12-15-21-29-07-50.png"];            //指定上述图片的路径。
  NSURL *fileURL = [NSURL fileURLWithPath:fileName];
  dispatch_async(dispatch_get_main_queue(), ^{              //运行主线程来加载界面,并初始化UIDocumentInteractionController
    UIDocumentInteractionController *documentInteractionController = [UIDocumentInteractionController interactionControllerWithURL:fileURL];
    documentInteractionController.delegate =self ;
    [documentInteractionController presentPreviewAnimated:YES];             //present出来文件的预览界面。
  });
  
}

之后定义(UIViewController *)documentInteractionControllerViewControllerForPreview:(UIDocumentInteractionController *)controller方法,要加载出来预览的界面,上述方法必须实现,且返回一个当前的页面的ViewController。用于作为预览视图的父ViewController来弹出modal。

- (UIViewController *)documentInteractionControllerViewControllerForPreview:(UIDocumentInteractionController *)controller{
  UIViewController *newVC =[self getPresentedViewController];  //获取当前屏幕中present出来的viewcontroller。
   return newVC;
};

这里我们是在一个非视图类创建并在加载一个视图,所以我们要先获取到当前界面的ViewController,将被展示的view加到当前view的子视图,或用当前view presentViewController,或pushViewContrller。这里我从网上找到了两个方法:

//获取当前屏幕显示的viewcontroller
- (UIViewController *)getCurrentVC  
{  
    UIViewController *result = nil;  
      
    UIWindow * window = [[UIApplication sharedApplication] keyWindow];  
    if (window.windowLevel != UIWindowLevelNormal)  
    {  
        NSArray *windows = [[UIApplication sharedApplication] windows];  
        for(UIWindow * tmpWin in windows)  
        {  
            if (tmpWin.windowLevel == UIWindowLevelNormal)  
            {  
                window = tmpWin;  
                break;  
            }  
        }  
    }  
      
    UIView *frontView = [[window subviews] objectAtIndex:0];  
    id nextResponder = [frontView nextResponder];  
      
    if ([nextResponder isKindOfClass:[UIViewController class]])  
        result = nextResponder;  
    else  
        result = window.rootViewController;  
      
    return result;  
} 
//获取当前屏幕中present出来的viewcontroller
- (UIViewController *)getPresentedViewController  
{  
    UIViewController *appRootVC = [UIApplication sharedApplication].keyWindow.rootViewController;  
    UIViewController *topVC = appRootVC;  
    if (topVC.presentedViewController) {  
        topVC = topVC.presentedViewController;  
    }  
      
    return topVC;  
}

在RN中,使用modal组件之后,弹出的modal视图的viewcontroller相当于present出来的viewcontroller.

之后使用[self getPresentedViewController]就可以获得viewcontroller来加载视图。