是否可以跨 NSBundle 共享图像?

Posted

技术标签:

【中文标题】是否可以跨 NSBundle 共享图像?【英文标题】:is it possible to share images across NSBundles? 【发布时间】:2011-08-20 09:13:30 【问题描述】:

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

这是我的场景:

我正在从存储在 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 中)

【问题讨论】:

看来我可以使用设备上捆绑文件夹中的符号链接来做我需要的事情(例如,通过链接 myApp.app/Documents/instance.bundle/commonImage.png --> myApp.app/ commonImage.png)我将很快发布我的通用解决方案。 编辑了上面的原始问题以正确链接(请参阅相对有据可查的“此处”) - 对任何试图点击该链接的人表示歉意。 【参考方案1】:

这里承诺的是我想出的解决方案:

捆绑文件本身按照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;
                
            

        
    


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

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

        ...(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 个应用程序委托方法中(一个用于实际应用程序,另一个用于离线包创建应用程序)

在主应用中

-(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];

注意:前面的方法依赖于以下 2 种方法,它们创建一个 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;

【讨论】:

以上是关于是否可以跨 NSBundle 共享图像?的主要内容,如果未能解决你的问题,请参考以下文章

是否可以跨多个项目共享角度组件?

跨多个视图控制器共享背景图像

是否可以跨多个应用共享应用内结算购买?

THREE.js:跨域资源共享策略拒绝跨域图像加载

无法在 NSBundle 中获取图像路径?

MagicalRecord 3.0 设置