再利用可能なボタンバー? ここまで来ましたが、今は「戻るボタン」の要件に問題があります。
次のようなレイアウト ソリューションが必要です。
- iOS 5.0 および 6.0 で動作します
- 上部にいくつかのボタンがあるカスタム ビューがあります。このビューは、シーンごとに Interface Builder でボタンを手動で複製するのではなく、すべての画面 (シーン) で再利用できる必要があります。
- その上部のカスタム ビューにカスタムの「戻る」ボタンがあります。実装しなければならないデザインでは、デフォルトのナビゲーション バーをそのまま使用することはできません
- UINavigationController とうまく連携します。ユーザーが「戻る」ボタンをタップすると、メイン ビュー コントローラー (ボタン バーを含む) はそのまま残りますが、実際のシーン コンテンツを表す子ビュー コントローラーは前のシーンに戻る必要があります。
現在の問題は、「戻る」ボタンで子コントローラーが変更されないことです。親コントローラーが変更され、ボタン バーのあるシーンの前のシーンに戻ります。私はこれをいくつかの異なる方法で試しました。自分のやり方が正しくないのか、それともできないのか、よくわかりません。
1つの可能性は、独自の「戻る」機能を実装し、子View Controllerのスタックを保持し、ユーザーが「戻る」をタップしたときにそれらを手動で変更することです。ただし、これは扱いにくく、UINavigationController を使用する場合に比べて設計が不十分です。
おそらく、私はこれで間違った方向に進んでいます。Interface Builder のすべてのシーンでボタン バーを複製することはできませんが、おそらくプログラムで作成する必要があります。そうすれば、すべてのシーンからそのコードを簡単に呼び出すことができます。次に、「通常の」ビュー コントローラーを用意し、UINavigationController を使用する方が簡単です。しかし、そのルートに行き、これまで持っていたものを完全に破棄する前に、別の方法があるかどうかを確認したかった.
私のソリューションの一部の概要は次のとおりです。
ButtonBarController を作成し、必要なボタンの UIView とコンテンツ ペインの UIView を使用して Storyboard をレイアウトしました。また、戻るボタンの上にアプリのロゴが付いたボタン (アプリのメイン画面に移動するため) を重ねました。
次に、他の各画面用のコントローラーを作成しました。これらのサブスクリーン/子ビュー コントローラーでは、最初にコンテンツ ペインに収まるように適切なサイズで UIView を追加し、次に必要な他のすべてのコントロールを追加します。これらの子ビュー コントローラーはすべて、ボタン バー コントローラーへの参照の取得や、3.5 インチ画面と 4 インチ画面のビューのサイズ変更を支援するコードなど、いくつかの一般的なタスクを処理する別のコントローラーから継承しました。
changeToControllerWithIndex メソッドを作成しました。アプリがロードされたとき、ユーザーがメイン ボタン バーのボタンの 1 つをクリックしてシーンを変更したとき、または別のシーンの変更を必要とするシーンで何かが発生したときに、これを呼び出します。このメソッドをオーバーロードして、2 つの追加情報を提供します。子ビュー コントローラーが必要とする追加情報を NSDictionary に提供し、これが最上位のシーンかどうか、または戻るボタンが必要かどうかを伝えます。
(注: Identity Inspector でこれらの子ビュー コントローラーの Storyboard ID を設定することが重要です。代わりに、Attribute Inspector で誤って Title を設定し続けました)
- (void)changeToControllerWithIndex:(NSInteger)index {
[self changeToControllerWithIndex:index withPayload:nil isRootView:YES];
}
// This is the method that will change the active view controller and the view that is shown
- (void)changeToControllerWithIndex:(NSInteger)index withPayload:(id)payload isRootView:(BOOL)isRootView
{
if (YES) {
self.index = index;
// The code below will properly remove the the child view controller that is
// currently being shown to the user and insert the new child view controller.
UIViewController *vc = [self setupViewControllerForIndex:index withPayload:payload];
if (isRootView) {
NSLog(@"putting navigation controller in");
childNavigationController = [[UINavigationController alloc] initWithRootViewController:vc];
[childNavigationController setNavigationBarHidden:YES];
[self addChildViewController:childNavigationController];
[childNavigationController didMoveToParentViewController:self];
if (self.currentViewController){
[self.currentViewController willMoveToParentViewController:nil];
[self transitionFromViewController:self.currentViewController toViewController:childNavigationController duration:0 options:UIViewAnimationOptionTransitionNone animations:^{
[self.currentViewController.view removeFromSuperview];
} completion:^(BOOL finished) {
[self.currentViewController removeFromParentViewController];
self.currentViewController = childNavigationController;
}];
} else {
[self.currentView addSubview:childNavigationController.view];
self.currentViewController = childNavigationController;
}
[self.currentView addSubview:childNavigationController.view];
//We are at the root of the navigation path, so no back button for us
[homeButton setHidden:NO];
[backButton setHidden:YES];
} else {
//Not a root view -- we're in navigation and want a back button
[childNavigationController pushViewController:vc animated:NO];
[homeButton setHidden:YES];
[backButton setHidden:NO];
}
}
}
次に、個々のView Controllerを設定するためのオーバーロードされたメソッドがあります...一部のものは、他のものよりも少し準備が必要です。
- (UIViewController *)setupViewControllerForIndex:(NSInteger)index {
return [self setupViewControllerForIndex:index withPayload:nil];
}
// This is where you instantiate each child controller and setup anything you need on them, like delegates and public properties.
- (UIViewController *)setupViewControllerForIndex:(NSInteger)index withPayload:(id)payload {
UIViewController *vc = nil;
if (index == CONTROLLER_HOME){
vc = [self.storyboard instantiateViewControllerWithIdentifier:@"Home"];
} else if (index == CONTROLLER_CATEGORIES){
SAVECategoryViewController *child = [self.storyboard instantiateViewControllerWithIdentifier:@"Categories"];
if (payload) {
child.currentCategory = [(NSNumber *) [(NSDictionary *)payload objectForKey:ATTRIBUTE_CAT_ID] integerValue];
} else {
child.currentCategory = CATEGORY_ALL;
}
vc = child;
} //etc for all the other controllers...
payload = nil;
return vc;
}
「戻る」ナビゲーションを管理するのが難しいと言いました。上記のコードは、ナビゲーション コントローラーが適切な「戻る」履歴を維持することを保証し、ボタン バー ボタンの 1 つを使用して画面を変更するたびに新たに開始します。子コントローラー内のボタンを使用してシーンからシーンに移動する場合、次のように戻ることができます。
- (IBAction)backButtonPressed:(id)sender {
[childNavigationController popViewControllerAnimated:YES];
if ([[childNavigationController viewControllers] count] <= 1) {
//Root view
[homeButton setHidden:NO];
[backButton setHidden:YES];
}
}