web-dev-qa-db-ja.com

iOSでNSBundleファイルをロードする

非常に柔軟なグラフィカルユーザーインターフェイス(スキナブル)を持つプロジェクトを作成したいと思います。これを可能にするために、外部リソースからNSBundleをロードしたいと思います。ウェブサイト。バンドルには、メインプロジェクト(IBOutletsおよびIBActions)の一部のプロパティとメソッドに対応するペン先が含まれている必要があります。

AppleはNSBundleをそのような方法で使用する可能性を制限しているようです。これを機能させる方法はありますか?従来の方法でそれが不可能な場合、推奨される代替アプローチは何でしょうか? ?

27

テストされていません。

すべてのコンテンツをZipファイルで配布し、 SSZipArchive を使用して解凍できます。

NSBundle* bundle = [NSBundle bundleWithPath:absolutePathToNibFile].

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

約束通り、ここで私がそれをどのように機能させたかについての簡単な説明。

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クラスを使用していることに注意してください。バンドルはAppleの規則に従った単なるディレクトリとファイルの構造であるため、すべてのコンテンツを効率的にダウンロードするには、バンドルをある種のコンテナにパッケージ化する必要があります。

私のクラスの1つは、IBOutletsとしてラベルとボタンを持つViewControllerです。 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

実際のペン先は、個別のリンクされたプロジェクト(iOSデバイスで機能するようにいくつかのパラメーターが変更されたCocoa NSBundleプロジェクト)で定義されます。ファイルの所有者はTestViewControllerであるため、すべてのアウトレットとアクションにアクセスして適切な接続を確立できます。スーパークラスにはすでにviewプロパティがあるため、TestViewControllerにはview(UIView *)プロパティが定義されていないことに注意してください。ビューがInterfaceBuilderで接続されていることを確認してください。また、nibファイルは、使いやすさのために実際のクラスと同じ名前を使用していることに注意してください。

これを行う方法についてウェブ上で多くの情報を見つけることができなかったので、これが同様の目標を持っている多くの人々に役立つことを願っています。

34