web-dev-qa-db-ja.com

Objective-Cに独自のUIViewControllerを持つサブビューを追加する方法は?

独自のUIViewControllersを持つサブビューで苦労しています。ビュー(明るいピンク色)とUIViewControllerに2つのボタンがあるtoolbarがあります。最初のボタンが押されたときに青色のビューを表示し、2番目のボタンで黄色のビューを表示したい。ビューを表示したいだけなら簡単なはずです。ただし、青いビューにはテーブルが含まれるため、独自のコントローラーが必要です。それが私の最初のレッスンでした。 this SO question から始めました。テーブルのコントローラーが必要だと知りました。

それで、私はバックアップして、ここでいくつかの赤ん坊のステップを踏むつもりです。以下は、ユーティリティViewController(メインビューコントローラー)と他の2つのコントローラー(青と黄色)を使用した簡単な開始点の写真です。 Utility ViewController(メインビュー)が最初に表示されたときに、ピンクのビューがある場所に青(デフォルト)ビューが表示されることを想像してください。ユーザーは2つのボタンをクリックして前後に移動でき、ピンクのビューは表示されません。ピンクのビューがある場所に青いビューを移動し、ピンクのビューがある場所に黄色のビューを移動するだけです。これが理にかなっていることを願っています。

Simple Storyboard image

addChildViewControllerを使用しようとしています。私が見てきたことから、これを行うには2つの方法があります。プログラムでstoryboardまたはaddChildViewControllerのコンテナビュー。プログラムでそれをしたいです。 NavigationControllerまたはタブバーを使用したくありません。コントローラーを追加し、関連するボタンが押されたときに正しいビューをピンクのビューに押し込みたいだけです。

以下は私がこれまでに持っているコードです。私がやりたいのは、ピンクのビューがあるところに青いビューを表示することだけです。私が見たものから、私はただaddChildViewControllerとaddSubViewができるはずです。このコードは私のためにそれをしていません。私の混乱は私を良くしています。誰かがピンクのビューがある場所に青いビューを表示するのを手伝ってくれますか?

このコードは、viewDidLoadに青色のビューを表示すること以外は何もしません。

IDUtilityViewController.h

#import <UIKit/UIKit.h>

@interface IDUtilityViewController : UIViewController
@property (strong, nonatomic) IBOutlet UIView *utilityView;
@end

IDUtilityViewController.m

#import "IDUtilityViewController.h"
#import "IDAboutViewController.h"

@interface IDUtilityViewController ()
@property (nonatomic, strong) IDAboutViewController *aboutVC;
@end

@implementation IDUtilityViewController

- (void)viewDidLoad
{
    [super viewDidLoad];

    self.aboutVC = [[IDAboutViewController alloc]initWithNibName:@"AboutVC" bundle:nil];
    [self addChildViewController:self.aboutVC];
    [self.aboutVC didMoveToParentViewController:self];
    [self.utilityView addSubview:self.aboutVC.aboutView];
}

@end

-------------------------- EDIT -------------- ----------------

Self.aboutVC.aboutViewはnilです。しかし、私はそれをstoryboardに接続しました。まだインスタンス化する必要がありますか?

enter image description here

52
Patricia

最新のiOSの初期の日付のこの投稿は、通常Swift 4のような最新の構文を持っています。iOS、autolayoutなどから始めている場合は、うまくいきます。

今日のiOS「すべてがコンテナビュー」。これが今日のアプリの基本的な作成方法です。

アプリは非常に単純なため、1つのビューしかありません。ただし、その場合でも、画面上の各「モノ」はコンテナビューです。

これは簡単です...


(A)コンテナビューをシーンにドラッグします...

コンテナビューをシーンビューにドラッグします。 (たとえば、UIButtonにドラッグするのと同じように。)

コンテナビューは、この画像の茶色のものです。実際にはinsideあなたのシーンビューです。

enter image description here

コンテナビューをシーンビューにドラッグすると、Xcodeは自動的に2つのものを与えます

  1. コンテナビューをシーンビュー内に取得します、そして

  2. 新品のUIViewControllerを取得します。これはストーリーボードの白のどこかに座っているだけです

2つはconnectedと「Masonic Symbol」のことです-以下で説明します!


(B)その新しいView Controllerをクリックします(Xcodeが白い領域のどこかにあなたのために作った新しいもの、シーン内のものではなく)...そして、クラスを変更します!

本当に簡単です。

完了です。


これは視覚的に説明したものと同じです。

(A)container viewに注意してください。

(B)controllerに注意してください。

shows a container view and the associated view controller

Bをクリックします(AではなくBです!)。

右上のインスペクターに移動します。 「UIViewController」と表示されていることに注意してください

[enter image description here] [3]

UIViewControllerである独自のカスタムクラスに変更します。

enter image description here

したがって、Swift class SnapであるUIViewControllerがあります。

enter image description here

インスペクターの「UIViewController」と表示されている場所に「Snap」と入力しました。

(通常どおり、「スナップ...」と入力し始めると、Xcodeは「スナップ」を自動補完します。)

これですべてです。完了です。


コンテナビューを変更する方法-たとえば、テーブルビューに変更します。

そのため、クリックしてコンテナビューを追加すると、Appleは自動的にストーリーボード上にリンクされたView Controllerを提供します。

偶然にも(2017):デフォルトでUIViewControllerになります。

それはばかげています。どのタイプが必要かを尋ねるべきです。たとえば、多くの場合、テーブルビューが必要です。異なるものに変更する方法は次のとおりです。

執筆時点では、XcodeはデフォルトでUIViewControllerを提供します。代わりにUICollectionViewControllerが欲しいとしましょう:

(i)コンテナビューをシーンにドラッグします。 Xcodeがデフォルトで提供するストーリーボード上のUIViewControllerを見てください。

(ii)新しいUICollectionViewControllerをストーリーボードのメインの白い領域のどこかにドラッグします。

(iii)シーン内のコンテナビューをクリックします。接続インスペクターをクリックします。 1つの「トリガーセグエ」があることに注意してください。 「トリガーされたセグエ」をマウスオーバーして、Xcodehighlights不要なUIViewController。

(iv)「x」をクリックして、セグエをトリガーしたdeleteを実際に削除します。

(v)トリガーされたセグエからのドラッグ(viewDidLoadが唯一の選択肢です)。ストーリーボード上で新しいUICollectionViewControllerにドラッグします。放すとポップアップが表示されます。 embedを選択する必要があります。

(vi)すべての不要なUIViewControllerを単にdeleteします。できました。

短いバージョン:不要なUIViewControllerを削除します。ストーリーボードに新しいUICollectionViewControllerを配置します。コントロールドラッグ:コンテナビューの接続-トリガーセグエ-viewDidLoad、新しいコントローラー。ポップアップで[埋め込み]を選択してください。


テキスト識別子を入力しています...

これらのいずれか "正方形の正方形"フリーメーソンシンボルの事柄:コンテナビューをView Controllerに接続する「ベンディライン」上にあります。

「フリーメーソンのシンボル」はセグエです。

enter image description here

(フリーメーソンのシンボル)の()をクリックしてセグエを選択します。

右側を見てください。

あなた[〜#〜] must [〜#〜]テキスト識別子を入力してくださいセグエ用。

あなたは名前を決めます。 任意のテキスト文字列を使用できます。賢明な選択は、多くの場合「segueClassName」です。

このパターンに従うと、すべてのセグエはsegueClockView、seguePersonSelector、segueSnap、segueCardsなどと呼ばれます。

次に、そのテキスト識別子をどこで使用しますか?


子コントローラーに「接続」する方法...

次に、コード全体で、シーン全体のViewControllerで以下を実行します。

シーンに3つのコンテナビューがあるとします。各コンテナビューには、「Snap」、「Clock」、「Other」などの異なるコントローラーがあります。

最新のSwift3構文(2017)

var snap:Snap?
var clock:Clock?
var other:Other?

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if (segue.identifier == "segueSnap")
            { snap = (segue.destination as! Snap) }
    if (segue.identifier == "segueClock")
            { clock = (segue.destination as! Clock) }
    if (segue.identifier == "segueOther")
            { other = (segue.destination as! Other) }
}

とても簡単です。 prepareForSegue呼び出しを使用して、コントローラーを参照する変数を接続します。


親までの「他の方向」への接続方法...

コンテナビューに配置したコントローラーに「入っている」としましょう(この例では「スナップ」)。

あなたの上にある「ボス」ビューコントローラー(例では「ダッシュ」)に到達するのはわかりにくいかもしれません。幸いなことに、これは簡単です。

// Dash is the overall scene.
// Here we are in Snap. Snap is one of the container views inside Dash.

class Snap {

var myBoss:Dash?    
override func viewDidAppear(_ animated: Bool) { // MUST be viewDidAppear
    super.viewDidAppear(animated)
    myBoss = self.parent as? Dash
}

クリティカル:viewDidAppear以降でのみ動作します。 viewDidLoadでは機能しません。

できました。


重要:onlyはコンテナビューに対してのみ機能します。

重要な高度なヒント:コンテナービューでのみ機能することを忘れないでください。

最近では、ストーリーボードIDを使用して、画面に新しいビューをポップするだけのことが一般的です(Android development)のように。だから、ユーザーが何かを編集したいとしましょう...

    // let's just pop a view on the screen.
    // this has nothing to do with container views
    //
    let e = ...instantiateViewController(withIdentifier: "Edit") as! Edit
    e.modalPresentationStyle = .overCurrentContext
    self.present(e, animated: false, completion: nil)

コンテナビューを使用する場合、IT IS GUARANTEEDDashはSnapの親ビューコントローラになります。

ただし、instantiateViewControllerを使用する場合は、CASEではありません。

非常に紛らわしいことに、iOSでは、親View Controllerは、インスタンス化したクラスに関連していません。 (Itmightは同じですが、通常は同じではありません。)self.parentパターンはonlyコンテナビューの場合。

(instantiateViewControllerパターンで同様の結果を得るには、プロトコルとデリゲートを使用する必要があります。デリゲートは弱いリンクになることに注意してください。)


prepareForSegueの名前が不十分です...

「prepareForSegue」は本当に悪い名前であることに注意する価値があります!

「prepareForSegue」は、コンテナビューの読み込みと、シーン間のセグメンテーションの2つの目的で使用されます。

しかし、実際には、シーン間でのセグエはめったにありませんほぼすべてのアプリには、当然のことながら、多くのコンテナビューがあります。

「prepareForSegue」が「loadingContainerView」のようなものと呼ばれる場合、それははるかに理にかなっています。


複数の...

一般的な状況は次のとおりです。画面上にいくつかの異なるView Controllerの1つを表示したい小さな領域がある。たとえば、4つのウィジェットの1つ。

これを行う最も簡単な方法:4つの異なるコンテナビューをすべて同じ同じ領域に配置するだけです。コードで、4つすべてを非表示にして、表示したいものをオンにします。

ストーリーボードに、4つのコンテナビューを保持する空の「ホルダー」UIViewを1つ用意します。次に、「ホルダー」をサイズ変更または移動することにより、4つすべてを一度にサイズ変更または移動できます。コードには、コンテナビューごとに1つずつ、合計4つのUIViewアウトレットがあります。上記のコード「子コントローラーへの接続方法」をコピーして貼り付け、含まれている4つのView Controllerを接続します。


注-ストーリーボードの参照が届きます!

SimplGyが以下に指摘するように "iOS 9の ストーリーボードリファレンス コンテナービューをさらに素晴らしいものにします。再利用可能なビュー(コントローラー)を好きな場所に定義し、どのコンテナービューからでも参照できます複数のモジュール式ストーリーボードで。」

また、かなり混乱したことに注意してください。今日では、コンテナビューに煩わされないことがよくあります。

多くの場合、単にinstantiateViewController#withIdentifierします。

ただし、上記で説明した.parentについての「ゴッチャ」に注意してください。コンテナビューの全体的なポイントは、親チェーンを即座にかつ簡単に保証することです。

ストーリーボードリファレンスを使用してinstantiateViewController#withIdentifierを使用する場合、プロトコルとデリゲートをいじる必要があります(デリゲートはウィークリンクになることを思い出してください)。しかし、その後、どこでも「オンザフライ」で柔軟に使用できます。

対照的に、いわゆる「固定」を使用すると、コンテナビューは非常にシンプルになり、上で説明したように、親と子の間で即座に接続します。

131
Fattie

2つの問題があります。まず、ストーリーボードでコントローラーを作成しているため、instantiateViewControllerWithIdentifier:ではなくinitWithNibName:bundle:でインスタンス化する必要があります。次に、ビューをサブビューとして追加するときに、フレームを指定する必要があります。そう、

- (void)viewDidLoad
{
    [super viewDidLoad];

    self.aboutVC = [self.storyboard instantiateViewControllerWithIdentifier:@"aboutVC"]; // make sure you give the controller this same identifier in the storyboard
    [self addChildViewController:self.aboutVC];
    [self.aboutVC didMoveToParentViewController:self];
    self.aboutVC.view.frame = self.utilityView.bounds;
    [self.utilityView addSubview:self.aboutVC.aboutView];
}
5
rdelmar