2011-06-01 107 views
24

我想要創建一個具有非常靈活的圖形用戶界面(換膚)的項目。爲了使這成爲可能,我想從外部資源加載NSBundle,例如一個網站。該捆綁應該包含對應於主項目中的一些屬性和方法的筆尖(IBOutlets & IBActions)在iOS上加載NSBundle文件

看來蘋果限制了以這種方式使用NSBundle的可能性。有什麼辦法可以使這項工作?如果以傳統方式無法實現,那麼可以推薦的替代方法是什麼?

回答

11

未經測試。

您可以將所有內容分發到zip文件中,使用SSZipArchive解壓縮。

NSBundle* bundle = [NSBundle bundleWithPath:absolutePathToNibFile]. 

UIViewController* vc = [[[MyViewController alloc] initWithNibName:@"mycustomnib" bundle:bundle] autorelease]; 
+0

嗯,我終於得到了一個N​​SBundle,通過將它從查找器複製到應用程序的iPhone Simulator文件夾來加載它,所以它可能也應該在真實設備上工作。要真正從服務器複製文件,似乎我必須將壓縮包歸檔爲壓縮文件,所以我會接受您的答案。 **編輯:**一旦我得到一切工作,我會在這個主題上發佈完整的代碼。 – 2011-06-02 22:41:10

+0

我已經使用SSZipArchive分發html內容,它看起來像一個相當強大的組件。它可以在iPhone 3上提取真正大的zip文件(我測試過的大約是40 MB)而不會耗盡內存。 – neoneye 2011-06-03 08:07:00

32

如承諾的,在這裏簡要說明我如何使它工作。

在我的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."); 
     } 
    } 
} 

請注意,我利用SSZipArchive類由neoneye的建議。需要將該軟件包打包到某種容器中以有效地下載所有內容,因爲軟件包僅僅是遵循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 

實際筆尖在一個單獨的鏈接項目中定義( Cocoa NSBundle項目改變了一些參數,使其適用於iOS設備)。該文件的所有者是TestViewController,所以我可以訪問所有插座&操作並進行適當的連接。請注意,TestViewController中沒有定義視圖(UIView *)屬性,因爲超類已具有視圖屬性。確保視圖在Interface Builder中連接。還要注意,爲便於使用,nib文件使用與實際類相同的名稱。

在網上找不到很多關於如何做到這一點的信息,所以我希望這對很多有類似目標的人有幫助。

+0

不錯。另外爲了下載內容,我可以推薦https://github.com/pokeb/asi-http-request/tree它在一個單獨的線程中完成它,讓你在等待它完成時顯示一個進度條。 – neoneye 2011-06-05 08:25:28

+0

優秀的代碼!我有一些問題,但。你如何實例化這個視圖控制器的一個實例?如果我嘗試使用自定義nib名稱進行初始化,它將在主包中查找,但它不在那裏。如果我嘗試使用默認的筆尖進行初始化,則無法加載該軟件包。介意給這個難題增加最後一塊? :) – Mark 2011-06-24 03:54:50

+0

另外,如果我在我下載的版本中連接了插座,是否需要像在loadView()中那樣重新應用它們?謝謝! – Mark 2011-06-24 04:20:09