如何使用NSBundle从下载的bundle中加载NIB并回退到主bundle

时间:2015-08-22 05:00:58

标签: ios uiview nsbundle nsdocumentdirectory loadnibnamed

这是基本问题:我需要一个视图加载机制,尝试从Documents中下载的NIB创建视图,然后在无法创建视图的情况下回退到主包。

在进行此项工作之前,我经历了大量的研究和反复试验,所以我想与其他人分享解决方案。

1 个答案:

答案 0 :(得分:0)

以下是步骤:

1)以正常方式在主包中创建NIB。我建议使用指向文件夹的文件组,以保留将用于下载的包的所有资产。我们称之为NIB_Resources。

在Project Navigator中的文件夹下创建NIB:

  1. 右键单击文件组。
  2. 选择新文件...
  3. 选择用户界面,然后选择查看。
  4. 2)为资产包添加目标。

    1. 点击目标面板中的+。
    2. 在OS X下的Framework and Library类别中选择Bundle模板。它属于该类别,因为它是一种资产库。
    3. 对于产品名称,输入要调用资产库的名称。保留其他所有内容,然后选择要将产品添加到的项目。
    4. 在新产品的构建设置中,将Base SDK从最新OS X更改为最新iOS。
    5. 3)将资产添加到资产包中。

      1. 选择新产品的复制包资源构建阶段。
      2. 拖放要包含在捆绑中的资产。如果可以添加资产,光标将显示+图标。
      3. 4)构建资产包。

        1. 为新创建的目标选择Scheme。
        2. 选择iOS设备作为构建目标。
        3. 构建
        4. 如果这样做是正确的,那么新捆绑包的产品应该在项目导航器的Products文件夹下从红色变为黑色。
        5. 5)压缩资产包

          1. 右键单击Products文件夹中新构建的产品,然后选择在Finder中显示。
          2. 将捆绑包复制到某个位置,例如专用于此项目的目录中的某个文件夹。
          3. 右键单击包含该捆绑包的目录,可能还有其他NIB文件,图像等
          4. 选择压缩。
          5. 6)将资产包上传到您具有下载访问权限的位置。

            7)下载压缩资产包:

            以下代码隐藏在便捷功能中,处理许多低级文件系统操作的便捷文件。 FS前缀是指文件系统。

            在返回主线程之前,可以从辅助线程调用

            FSDownloadTempFileWithURLString

            我使用NSData同步方法initWithContentsOfURL:,因为调用很可能是从辅助线程进行的。基本策略是在执行任何必要的准备工作并将文件解压缩到Documents目录之前,将zip文件下载到临时位置(Caches目录通常是用于此目的的良好候选者)。在Apple文件中采用了在头文件中定义内联静态操作的方法。

            //Documents directory
            #define FSDocumentsDirectory    [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject]
            
            //Caches directory
            #define FSCachesDirectory       [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject]
            
            /**
             * Get the path to a file under the caches directory. The given filename can have
             * multiple file separators.
             */
            inline static NSString* FSCachesPath(NSString *filename)
            {
                return [FSCachesDirectory stringByAppendingPathComponent:filename];
            }
            
            
            /**
             * Download a file from a specified URL, and copy to the caches directory, to the same filename as the URL's filename.
             *
             * Returns the result.
             */
            inline static BOOL FSDownloadTempFileWithURLString(NSString *urlString)
            {
                NSData *data = getDataFromURL(urlString);
                if (!data) {
                    //Error already logged
                    return FALSE;
                }
            
                NSString *path = FSCachesDirectory;
                NSString *filename = [urlString lastPathComponent];
                path = [path stringByAppendingPathComponent:filename];
            
                NSError *error = nil;
                if (![data writeToFile:path options:NSDataWritingAtomic error:&error]) {
                    NSLog(@"Error occurred while trying to write the file to: %@\n", path);
                    NSLog(@"%@", error);
            
                    return FALSE;
                }
            
                return TRUE;
            }
            
            /**
             * Get the data from a specified URL.
             */
            inline static NSData* getDataFromURL(NSString *urlString)
            {
                NSString *escapedUrlString = [urlString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
            
                NSURL *url = [NSURL URLWithString:escapedUrlString];
                NSData *data = [[NSData alloc] initWithContentsOfURL:url];
                if (!data) {
                    debugLog(@"Could not download file: %@", escapedUrlString);
                    return nil;
                }
            
                return data;
            }
            

            8)使用SSZipArchive或类似内容将下载解压缩到Documents目录:

            NSString *cachesPath = FSCachesPath(URL_RESOURCE_FILENAME);
            if (![SSZipArchive unzipFileAtPath:cachesPath toDestination:FSDocumentsDirectory delegate:nil]) {
                return;
            }
            

            9)最后,尝试从Documents目录的bundle中的NIB文件加载一个视图,然后回退到主bundle。

            可以从尝试从Nib加载视图的视图控制器调用下面的FSResourceNib操作,如下所示:

            UIView *view = FSResourceNib(ResourcesBundle, nibName, self);
            
            /**
             * Get a NIB from the documents directory, otherwise fall back to the bundle.
             *
             * Returns nil, if an error occurs.
             */
            inline static UIView* FSResourceNib(NSString *bundleFilename, NSString *nibName, id owner)
            {
                UIView *resourceView = nil;
            
                //If bundld doesn't exist in the documents path, then use the main bundle
                NSString *resourcePath = FSDocumentsPath(bundleFilename);
                if ([[NSFileManager defaultManager] fileExistsAtPath:resourcePath]) {
                    NSBundle *resourceBundle = [NSBundle bundleWithPath:resourcePath];
            
                    @try {
                        //Try to load the NIB from the given bundle
                        resourceView = [[resourceBundle loadNibNamed:nibName owner:owner options:nil] lastObject];
                    }
                    @catch (NSException *exception) {
                        //do nothing - will try main bundle
                    }
                }
            
                //If loading from the given bundle failed, try loading from the main bundle
                if (!resourceView) {
                    NSBundle *resourceBundle = [NSBundle mainBundle];
            
                    @try {
                        resourceView = [[resourceBundle loadNibNamed:nibName owner:owner options:nil] lastObject];
                    }
                    @catch (NSException *exception) {
                        //do nothing - will return nil, indicating an error occurred
                    }
                }
            
                return resourceView;
            }