我想创建一个具有非常灵活的图形用户界面( skinnable )的项目。为了实现这一点,我想从外部资源加载NSBundle,例如一个网站。捆绑包应包含与主项目中的某些属性和方法相对应的笔尖(IBOutlets& IBActions)
Apple似乎限制了以这种方式使用NSBundle的可能性。有什么方法可以让我的工作吗?如果以传统方式无法实现,那么什么是可推荐的替代方法?
答案 0 :(得分:34)
正如所承诺的,这里有一个关于我如何使其发挥作用的简短说明。
在我的AppDelegate中,我检查服务器上是否有新的软件包(打包在zip文件中)。如果它存在,我下载捆绑包。该软件包包含我的可换肤图形界面和其他相关数据(例如图形文件)所需的笔尖。代码看起来有点像这样:
<强> 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.");
}
}
}
请注意我按照 neoneye 的建议使用SSZipArchive类。需要将捆绑包装在某种容器中以有效地下载所有内容,因为捆绑包只是一个目录&amp;文件结构遵循Apple的惯例。
我的一个类是一个ViewController,它有一个标签和按钮作为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
实际的nib是在一个单独的链接项目中定义的(Cocoa NSBundle项目,其中一些参数已更改,以使其适用于iOS设备)。该文件的所有者是TestViewController,所以我可以访问所有的出口和放大器。行动并建立适当的联系。请注意,TestViewController中定义了 view (UIView *)属性,因为超类已经具有 view 属性。确保视图已在Interface Builder中连接。另请注意,nib文件使用与实际类相同的名称以便于使用。
无法在网上找到有关如何执行此操作的大量信息,因此我希望这会对很多有类似目标的人有所帮助。
答案 1 :(得分:12)
未经测试。
您可以使用SSZipArchive解压缩zip文件中的所有内容。
NSBundle* bundle = [NSBundle bundleWithPath:absolutePathToNibFile].
UIViewController* vc = [[[MyViewController alloc] initWithNibName:@"mycustomnib" bundle:bundle] autorelease];