分享

从Samples中入门IOS开发(三)------ 文档浏览

IOS提供了对各种文档(PDF, PPT, word, txt, jpg等)的浏览功能,这个非常使用,因为我们难免需要app里浏览各种文档。官方的Sample code DocInteraction展示了如何在IOS浏览各种格式的文档。
本Sample非常简单,在tableview里列出了预定的和制定目录下的文档列表,点击某个文档时,能切换view并预览文档内容:
1.jpg
从这个sample里能学到的关键点是:
从文件目录中载入文件列表 监控文档目录中文件的变动,并实时反馈在UI上 预览各种文档内容从文件目录中载入文件列表
IOS提供了NSFileManager来访问文件系统,从以下代码能很清晰地了解到如何通过NSFileManager查询某个目录路径下所有所有文件:
- (void)directoryDidChange:(DirectoryWatcher *)folderWatcher
{
        [self.documentURLs removeAllObjects];    // clear out the old docs and start over
        
        NSString *documentsDirectoryPath = [self applicationDocumentsDirectory];
        
        NSArray *documentsDirectoryContents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:documentsDirectoryPath error:NULL];
   
        for (NSString* curFileName in [documentsDirectoryContents objectEnumerator])
        {
                NSString *filePath = [documentsDirectoryPath stringByAppendingPathComponent:curFileName];
                NSURL *fileURL = [NSURL fileURLWithPath:filePath];
               
                BOOL isDirectory;
        [[NSFileManager defaultManager] fileExistsAtPath:filePath isDirectory:&isDirectory];
               
        // proceed to add the document URL to our list (ignore the "Inbox" folder)
        if (!(isDirectory && [curFileName isEqualToString: @"Inbox"]))
        {
            [self.documentURLs addObject:fileURL];
        }
        }
        
        [self.tableView reloadData];
}在UI上展现文件列表就是通过tableView的datasource来完成,在上一节的分享中已分析此部分,此时只需reloadData刷新一下即可。
监控文档目录中文件的变动,并实时反馈在UI上
对文件目录的监控本例子是借助系统内核函数来完成,先看代码:
- (BOOL)startMonitoringDirectory:(NSString *)dirPath
{
        // Double initializing is not going to work...
        if ((dirKQRef == NULL) && (dirFD == -1) && (kq == -1))
        {
                // Open the directory we're going to watch
                dirFD = open([dirPath fileSystemRepresentation], O_EVTONLY);
                if (dirFD >= 0)
                {
                        // Create a kqueue for our event messages...
                        kq = kqueue();
                        if (kq >= 0)
                        {
                                struct kevent eventToAdd;
                                eventToAdd.ident  = dirFD;
                                eventToAdd.filter = EVFILT_VNODE;
                                eventToAdd.flags  = EV_ADD | EV_CLEAR;
                                eventToAdd.fflags = NOTE_WRITE;
                                eventToAdd.data   = 0;
                                eventToAdd.udata  = NULL;
                                
                                int errNum = kevent(kq, &eventToAdd, 1, NULL, 0, NULL);
                                if (errNum == 0)
                                {
                                        CFFileDescriptorContext context = { 0, self, NULL, NULL, NULL };
                                        CFRunLoopSourceRef      rls;
                                        // Passing true in the third argument so CFFileDescriptorInvalidate will close kq.
                                        dirKQRef = CFFileDescriptorCreate(NULL, kq, true, KQCallback, &context);
                                        if (dirKQRef != NULL)
                                        {
                                                rls = CFFileDescriptorCreateRunLoopSource(NULL, dirKQRef, 0);
                                                if (rls != NULL)
                                                {
                                                        CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
                                                        CFRelease(rls);
                                                        CFFileDescriptorEnableCallBacks(dirKQRef, kCFFileDescriptorReadCallBack);
                                                        
                                                        // If everything worked, return early and bypass shutting things down
                                                        return YES;
                                                }
                                                // Couldn't create a runloop source, invalidate and release the CFFileDescriptorRef
                                                CFFileDescriptorInvalidate(dirKQRef);
                        CFRelease(dirKQRef);
                                                dirKQRef = NULL;
                                        }
                                }
                                // kq is active, but something failed, close the handle...
                                close(kq);
                                kq = -1;
                        }
                        // file handle is open, but something failed, close the handle...
                        close(dirFD);
                        dirFD = -1;
                }
        }
        return NO;
}
这段代码基本上都是对内核c函数的使用,虽然有些晦涩,但流程还是很简单的,首先是对系统事件的监控,然后基于IOS的thread框架来callback文件目录变动后的回调,回调的具体内容实现在KQCallback中,此方法也非常简单,实际上就是基于Delegate模式调用到上一段的directoryDidChange方法,继而刷新tableview的内容。具体的c函数的用法这里就不多说,但值得我们学习的是如何在IOS的层面上调用底层BSD系统API。
预览各种文档内容
本例子中最核心的点反而是最简单的,IOS提供了QLPreviewController,通过此controller能直接得到预览各种文档的view,你只需要把view塞到navigationController中即可,代码如下:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSURL *fileURL;
    if (indexPath.section == 0)
    {
        fileURL = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:documents[indexPath.row] ofType:nil]];
    }
    else
    {
        fileURL = [self.documentURLs objectAtIndex:indexPath.row];
    }
    [self setupDocumentControllerWithURL:fileURL];
    [self.docInteractionController presentPreviewAnimated:YES];
   
    QLPreviewController *previewController = [[QLPreviewController alloc] init];
    previewController.dataSource = self;
    previewController.delegate = self;
   
    // start previewing the document at the current section index
    previewController.currentPreviewItemIndex = indexPath.row;
    [[self navigationController] pushViewController:previewController animated:YES];
    [previewController release];
}具体如何设定预览的内容和行为,是在QLPreviewControllerDelegate和QLPreviewControllerDataSource中完成的,所以这里只需要在controller中实现QLPreviewControllerDelegate和QLPreviewControllerDataSource相关方法即可:
// Returns the number of items that the preview controller should preview
- (NSInteger)numberOfPreviewItemsInPreviewController:(QLPreviewController *)previewController
{
    NSInteger numToPreview = 0;
   
    NSIndexPath *selectedIndexPath = [self.tableView indexPathForSelectedRow];
    if (selectedIndexPath.section == 0)
        numToPreview = NUM_DOCS;
    else
        numToPreview = self.documentURLs.count;
   
    return numToPreview;
}
// returns the item that the preview controller should preview
- (id)previewController:(QLPreviewController *)previewController previewItemAtIndex:(NSInteger)idx
{
    NSURL *fileURL = nil;
   
    NSIndexPath *selectedIndexPath = [self.tableView indexPathForSelectedRow];
    if (selectedIndexPath.section == 0)
    {
        fileURL = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:documents[idx] ofType:nil]];
    }
    else
    {
        fileURL = [self.documentURLs objectAtIndex:idx];
    }
   
    return fileURL;
}
第一个方法是设定一组文档浏览的个数,会影响到在文档预览中的上/下一页,第二个方法是指定文档预览的文件路径,由此可见在IOS中预览各种文档是件很简单的事儿,并且可预览的格式非常广,只要在mac的预览能支持的,这里都能支持,简单测试后发现基本能支持所有常规文档。

没找到任何评论,期待你打破沉寂

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关闭

推荐上一条 /2 下一条