在iOS上加载NSBundle文件

我想创build一个具有非常灵活的graphics用户界面( skinnable )的项目。 为了使这成为可能,我想从外部资源,例如一个网站加载一个NSBundle。 该包应该包含与主项目中的一些属性和方法(IBOutlets&IBActions)相对应的nib,

看来苹果已经限制了以这种方式使用NSBundle的可能性。 有什么办法可以使这项工作? 如果以传统的方式不可能的话,那么可以推荐的替代方法是什么?

未经testing。

您可以将所有内容分发到一个zip文件中,使用SSZipArchive进行解压缩。

NSBundle* bundle = [NSBundle bundleWithPath:absolutePathToNibFile]. UIViewController* vc = [[[MyViewController alloc] initWithNibName:@"mycustomnib" bundle:bundle] autorelease]; 

如所承诺的,这里简单地解释我如何使它工作。

在我的AppDelegate中,我检查服务器上是否有一个新的包(打包在一个zip文件中)。 如果存在,我下载捆绑软件。 该软件包包含我需要的可裁剪graphics界面和其他相关数据(例如graphics文件)的笔尖。 代码看起来有点像这样:

TestAppDelegate.m

 - (void)downloadBundle { NSURL *url = [NSURL URLWithString:@"http://127.0.0.1/~wsc/template.bundle.zip"]; NSURLRequest *request = [NSURLRequest requestWithURL:url]; NSURLResponse *response = nil; NSError *error = nil; NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error]; NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response; NSLog(@"%d", [httpResponse statusCode]); if ([httpResponse statusCode] == 404) // bundle will be deleted and the default interface will be used ... { NSString *path = [documentsDirectory stringByAppendingPathComponent:@"template.bundle"]; [[NSFileManager defaultManager] removeItemAtPath:path error:nil]; return; } else if (error) { NSLog(@"%@", error); } BOOL didWriteData = [data writeToFile:zipFile atomically:YES]; if (didWriteData) { BOOL success = [SSZipArchive unzipFileAtPath:zipFile toDestination:documentsDirectory]; if (!success) { NSLog(@"failed to unzip file."); } } } 

请注意我使用neoneyebuild议的SSZipArchive类。 需要将包打包到某种容器中以有效地下载所有内容,因为包只是遵循苹果公约的目录和文件结构。

我的一个类是一个ViewController,它有一个标签和button作为IBOutlets。 ViewController中最重要的代码如下所示:

TestViewController.h

 @interface TestViewController : UIViewController { UIButton *button; UILabel *label; } @property (nonatomic, retain) IBOutlet UIButton *button; @property (nonatomic, retain) IBOutlet UILabel *label; - (IBAction)buttonTouched:(id)sender; @end 

TestViewController.m

 @implementation TestViewController @synthesize button, label; - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; if (self) { // Custom initialization } return self; } // the -init method is overridden to use nib file from bundle, if bundle exists ... - (id)init { NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *documentsDirectory = [paths objectAtIndex:0]; NSString *file = [documentsDirectory stringByAppendingPathComponent:@"template.bundle"]; NSBundle *bundle = [NSBundle bundleWithPath:file]; if (!bundle) { NSLog(@"no bundle found, falling back to default gui ..."); return [self initWithNibName:nil bundle:nil]; } NSString *nibName = NSStringFromClass([self class]); return [self initWithNibName:nibName bundle:bundle]; } - (void)dealloc { [button release]; [label release]; [view release]; [super dealloc]; } #pragma mark - View lifecycle - (void)loadView { if (self.nibName && self.nibBundle) { // connect outlets to proxy objects ... NSDictionary *objects = [NSDictionary dictionaryWithObjectsAndKeys: self.label, @"label", self.button, @"button", nil]; NSDictionary *proxies = [NSDictionary dictionaryWithObject:objects forKey:UINibExternalObjects]; NSArray *nibs = [self.nibBundle loadNibNamed:self.nibName owner:self options:proxies]; // connection happens here ... NSLog(@"nibs found with name %@: %d", self.nibName, [nibs count]); return; } // show default gui if no nib was found ... CGRect frame = [UIScreen mainScreen].applicationFrame; self.view = [[[UIView alloc] initWithFrame:frame] autorelease]; [self.view setBackgroundColor:[UIColor lightGrayColor]]; self.button = [UIButton buttonWithType:UIButtonTypeRoundedRect]; [self.button setFrame:CGRectMake(0.0f, 0.0f, 60.0f, 30.0f)]; [self.button setCenter:CGPointMake(160.0f, 100.0f)]; [self.button addTarget:self action:@selector(buttonTouched:) forControlEvents:UIControlEventTouchUpInside]; [self.view addSubview:self.button]; self.label = [[[UILabel alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 300.0f, 30.0f)] autorelease]; [self.label setCenter:CGPointMake(160.0f, 50.0f)]; [self.label setTextAlignment:UITextAlignmentCenter]; [self.view addSubview:self.label]; } // Implement viewDidLoad to do additional setup after loading the view, typically from a nib. - (void)viewDidLoad { [super viewDidLoad]; // for my purposes I'll add the localized string from the mainBundle here for the standard controls, // this will override the text set in the nibs, since the nibs are loaded and displayed at this point ... [self.button setTitle:NSLocalizedString(@"TestButton", nil) forState:UIControlStateNormal]; [self.label setText:NSLocalizedString(@"TestLabel", nil)]; } - (void)viewDidUnload { [super viewDidUnload]; self.button = nil; self.label = nil; } - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation { return (interfaceOrientation == UIInterfaceOrientationPortrait); } #pragma mark - - (IBAction)buttonTouched:(id)sender { [[[[UIAlertView alloc] initWithTitle:NSLocalizedString(@"DialogTitle", nil) message:NSLocalizedString(@"ButtonTouchedText", nil) delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil] autorelease] show]; } @end 

实际的笔尖是在一个单独的链接项目中定义的(Cocoa NSBundle项目,其中一些参数被改变,以使其适用于iOS设备)。 该文件的所有者是TestViewController,所以我可以访问所有的网点和操作,并进行适当的连接。 请注意,在TestViewController中没有定义视图 (UIView *)属性,因为超类已经有一个视图属性。 确保视图在Interface Builder中连接。 还要注意,nib文件使用与实际类相同的名称以方便使用。

在网上找不到关于如何做到这一点的许多信息,所以我希望这对许多有相似目标的人有帮助。