可以在NSBundles上共享图像吗?

时间:2011-08-20 09:13:30

标签: ios uiimage nsbundle

为模糊的问题标题道歉...

这是我的情景:

我从NSBundles中存储的nib文件动态加载UIView内容

BOOL bundlePathExists = [fm fileExistsAtPath:bundlePath];
if (bundlePathExists) {
    NSBundle *tempBundle = [NSBundle bundleWithPath:bundlePath];

    UIViewController *vc = [[UIViewController alloc] initWithNibName:nibName bundle:tempBundle];

    [self.view addSubview:vc.view];
}

(注意:以上是实际代码的简化摘录,以免混淆问题的性质 - 这样做的机制相对较好地记录here

这些nib文件包含许多图像,其中一些图像对于该图像束是唯一的,而其他图像则在多个图像束中共享。

我希望能够存储主要包中常见的图像,因此它们不占用其他包中的空间,并最大限度地减少整个项目的维护 - 例如,如果我改变其中一个常见的图像,我宁愿不必重建它所引用的每个包。

我意识到我可以使用

以编程方式执行此操作
[commonImageViewIvar setImage:[UIImage imageWithName:commonImageName]]; 

但是我更愿意在不为每个视图编写自定义代码的情况下实现这一点(即实例化的视图控制器不是每个nib自定义的,因此所有信息都需要存储在nib中)

1 个答案:

答案 0 :(得分:0)

正如我所承诺的那样,我提出了解决方案:

捆绑文件本身按照here所述下载。要添加到那里列出的信息,我发现如果您首先在iphone应用程序中创建视图,则可以在模拟器中预览它们。然后,您可以创建一个新项目,作为一个包,并拖放所有图像文件&将xib& .h文件放入新项目中。不要拖动.m文件,因为这会产生构建问题。然后,确保项目设置将BASE SDK定义为“最新IOS”,而不是已选择的默认Mac OS X设置。然后你可以通过双击它来编辑xib。通过取消该文件的“目标”列,可以从构建中取消选择任何公共文件。请参阅本答复文章后面的“附加信息”。

一旦我从服务器下载了一个zip文件中的每个包,我就会使用以下为此目的编码的方法:

-(NSArray *) imageFileExtensions {
    return [NSArray arrayWithObjects:@".png",@".gif",@".jpg",@".jpeg",@".bmp",  nil];
}

-(void) cloneImageFilesInPath:(NSString *)path toPath:(NSString*)clonePath {

    NSFileManager *fm = [NSFileManager defaultManager];
    NSArray *extensions = [self imageFileExtensions];
    if ([fm fileExistsAtPath:path]) {
        NSArray *files = [fm contentsOfDirectoryAtPath:path error:nil];

        for (NSString *file in files){
            for (NSString *ext in extensions) {
                if ([file hasSuffix:ext]) {
                    NSString *targetFile = [clonePath stringByAppendingPathComponent:file];
                    if (![fm fileExistsAtPath:targetFile]) {
                        [fm createSymbolicLinkAtPath:targetFile withDestinationPath:[path  stringByAppendingPathComponent:file] error:nil];
                    }
                    break;
                }
            }

        }
    }

}

这些方法创建扫描主应用程序包目录,并且对于不在自定义包目录中的每个图像文件,在自定义包目录内创建符号链接以指回主应用程序包目录。

在我的app委托中调用它们如下:

        ...(insert code to unzip the file to bundlePath here)...

        [self cloneImageFilesInPath:[[NSBundle mainBundle] bundlePath] toPath:bundlePath];

其他信息

创建实际的包,我创建了一个单独的应用程序,从自定义包目录中删除任何图像文件,如果这些文件名存在于包含主应用程序包中部署的公共图像文件的目录中。我后来发现你可以通过取消选择该文件的目标列复选框来阻止xcode包含构建中的某些文件,所以不一定需要这一步 - 如果你要创建很多视图,那么可能更容易离开它们在捆绑中,并使用此方法将它们剥离。

-(void) removeDuplicatedImageFilesInPath:(NSString *)sourcePath fromTargetPath:(NSString*)path {

    NSFileManager *fm = [NSFileManager defaultManager];
    NSArray *extensions = [self imageFileExtensions];
    if ([fm fileExistsAtPath:path]) {
        NSArray *files = [fm contentsOfDirectoryAtPath:sourcePath error:nil];

        for (NSString *file in files){
            for (NSString *ext in extensions) {
                if ([file hasSuffix:ext]) {
                    NSString *targetPath = [path stringByAppendingPathComponent:file];
                    if ([fm fileExistsAtPath:targetPath]) {
                        [fm removeItemAtPath:targetPath error:nil];
                    }
                    break;
                }
            }

        }
    }

}

有关处理zip文件的更多信息

我选择使用ObjectiveZip(遵循海报here的建议。为了简化任务,我将其包装在以下2个app委托方法中(一个用于实际应用,另一个用于离线捆绑创建应用程序)

主应用程序中的

-(void) unzipArchive:(NSString *)zipFileName toPath:(NSString *)path {
    NSFileManager *fm  = [NSFileManager defaultManager];

    ZipFile *unzipFile = [[ZipFile alloc] initWithFileName:zipFileName mode:ZipFileModeUnzip];

    NSArray *infos= [unzipFile listFileInZipInfos];

    [unzipFile goToFirstFileInZip];
    for (FileInZipInfo *info in infos) {

        ZipReadStream *read1= [unzipFile readCurrentFileInZip];

        NSMutableData *fileData = [[[NSMutableData alloc] initWithLength:info.length] autorelease];
        int bytesRead1 = [read1 readDataWithBuffer:fileData];

        if (bytesRead1 == info.length) {
            NSString *fileName = [path stringByAppendingPathComponent:info.name ];
            NSString *filePath = [fileName stringByDeletingLastPathComponent];

            [fm createDirectoryAtPath:filePath withIntermediateDirectories:YES attributes:nil error:nil];


            [fileData writeToFile:fileName atomically:YES];

        }

        [read1 finishedReading];        

        [unzipFile goToNextFileInZip];

    }

    [unzipFile close];
    [unzipFile release];


}

并在自定义捆绑包创建离线应用中。

-(void) createArchive:(NSString *) zipFileName usingPath:(NSString *)path {
    NSArray *files = [self filesInDirectory:path];
    ZipFile *zipFile= [[ZipFile alloc] initWithFileName:zipFileName mode:ZipFileModeCreate];

    for (NSString *file in files) {

        NSString *fileNameForZip = [file substringFromIndex:path.length];

        ZipWriteStream *stream= [zipFile writeFileInZipWithName:fileNameForZip fileDate:[NSDate dateWithTimeIntervalSinceNow:-86400.0] compressionLevel:ZipCompressionLevelBest];

        [stream writeData:[NSData dataWithContentsOfFile:file]];

        [stream finishedWriting];
    }
    [zipFile close];
    [zipFile release];
}

注意:前一个方法依赖于以下两个方法,这两个方法创建一个NSArray,其中包含给定路径中所有文件的完全限定路径(递归到子目录中)

-(void)loadFilesInDirectory:(NSString *)path intoArray:(NSMutableArray *)files {
    NSFileManager *fm  = [NSFileManager defaultManager];

    NSMutableArray *fileList = [NSMutableArray arrayWithArray:[fm contentsOfDirectoryAtPath:path error:nil]];
    for (NSString *file in fileList) {
        BOOL isDirectory = NO;
        NSString *filePath = [path stringByAppendingPathComponent:file];
        if ([fm fileExistsAtPath:filePath isDirectory:&isDirectory]) {
            if (isDirectory) {
                [self loadFilesInDirectory:filePath intoArray:files];
            } else {
                [files addObject:filePath];
            };          

        };
    }
}


-(NSArray *)filesInDirectory:(NSString *)path {
    NSMutableArray *files = [NSMutableArray array];
    [self loadFilesInDirectory:path intoArray:files];
    return files;
}