Objective-CでRootViewControllerを取得する5つのステップ

Objective-Cのコード例と共にRootViewControllerの取得方法を解説している画像Objctive-C
この記事は約27分で読めます。

 

【サイト内のコードはご自由に個人利用・商用利用いただけます】

この記事では、プログラムの基礎知識を前提に話を進めています。

説明のためのコードや、サンプルコードもありますので、もちろん初心者でも理解できるように表現してあります。

基本的な知識があればカスタムコードを使って機能追加、目的を達成できるように作ってあります。

※この記事は、一般的にプロフェッショナルの指標とされる『実務経験10,000時間以上』を凌駕する現役のプログラマチームによって監修されています。

サイト内のコードを共有する場合は、参照元として引用して下さいますと幸いです

※Japanシーモアは、常に解説内容のわかりやすさや記事の品質に注力しております。不具合、分かりにくい説明や不適切な表現、動かないコードなど気になることがございましたら、記事の品質向上の為にお問い合わせフォームにてご共有いただけますと幸いです。
(送信された情報は、プライバシーポリシーのもと、厳正に取扱い、処分させていただきます。)

はじめに

Objective-Cを学ぶ上でRootViewControllerの取得方法は基本中の基本です。

iOSアプリ開発において、RootViewControllerはアプリケーションのウィンドウに最初に表示されるViewControllerを指します。

この記事では、Objective-CでRootViewControllerを効率的に取得する9つのステップを紹介します。

初心者でも分かりやすいように、ポイントをしっかり押さえつつ、一つ一つの手順を詳細に説明していきます。

Objective-CはC言語をベースに、Smalltalkのオブジェクト指向機能を取り入れた言語で、C言語の全ての機能に加えて、クラスや継承、ポリモーフィズムなどのオブジェクト指向プログラミングの概念を使うことができます。

AppleのMac OS XやiOSで広く用いられているため、Appleのアプリケーションを開発する際には必須のスキルとなっています。

●Objective-Cとは

Objective-Cとは、1980年代にBrad CoxとTom Loveによって開発されたプログラミング言語であり、AppleのiOSおよびOS Xの開発で中心的な役割を果たしてきました。

C言語の上にオブジェクト指向の機能を追加した拡張言語であり、メッセージパッシングの概念を通じて柔軟なコーディングが可能です。

現在ではSwiftにその座を譲りつつありますが、既存の多くのコードやフレームワークはObjective-Cで記述されています。

○Objective-Cの基本概念

Objective-Cのプログラミングでは、オブジェクト、クラス、メソッド、プロパティなどの基本的な概念が不可欠です。

オブジェクトはデータとそれを操作する関数をカプセル化したもので、クラスはオブジェクトの設計図にあたります。

メソッドはクラスに属する関数であり、プロパティはオブジェクトのデータ(属性)に対するアクセスを提供します。

これらの概念を理解することで、Objective-Cのコーディングにおいて柔軟かつ強力な表現が可能になります。

○Objective-Cの歴史と現在

Objective-Cは、元々NeXTコンピューターによってその開発環境の一部として採用されました。

NeXTがAppleに買収された後、Mac OS XとiOSの主要な開発言語となりました。

しかし、2014年にAppleはSwiftを発表し、より安全で高速なアプリケーション開発を目指しています。

現在もObjective-CはiOS開発で広く使われていますが、新しいプロジェクトではSwiftが推奨されています。

それでも、既存のライブラリやフレームワークの理解、また維持・更新のためにObjective-Cの知識は依然として価値があります。

●RootViewControllerとは

RootViewControllerは、iOSアプリケーションのUIWindowクラスの最も基本となるViewControllerを指します。

iOSアプリケーションにおいては、UIWindowがアプリケーションの画面に表示されるすべてのビューを保持していますが、このウィンドウの最初のレイヤーとして表示されるのがRootViewControllerです。

このRootViewControllerはアプリケーションの最も基盤となるコンテンツを提供し、ユーザーに対する最初のインタラクションポイントを提供します。

すべてのビューコントローラーは、このルートビューコントローラーから階層的に積み重ねられていくため、その重要性は言うまでもありません。

○RootViewControllerの役割と重要性

RootViewControllerは、アプリケーションのUI構造において中心的な役割を果たします。

ユーザーがアプリを起動した際に最初に目にする画面であり、その後のすべてのビューコントローラーへと遷移する基点となるため、その設定と管理はアプリケーションの使いやすさや反応性に直接影響を及ぼします。

また、メモリ管理の観点からも、RootViewControllerはアプリケーションが起動してから終了するまで存続するオブジェクトであり、メモリリークを避けるために適切な管理が求められます。

不適切な扱いは、ユーザーエクスペリエンスを低下させるだけでなく、アプリケーションのパフォーマンスにも悪影響を及ぼす可能性があります。

○UIViewControllerの基礎

UIViewControllerクラスは、iOSアプリケーションにおいてビューのライフサイクルを管理するための中心的なクラスです。

このクラスはビューが表示される際に行うべき作業、隠れる際の作業、メモリ警告の際の対応など、ビューの表示に関連するさまざまなイベントを取り扱います。

開発者はUIViewControllerのサブクラスを作成し、特定のビューに特化したカスタムコードを書くことで、アプリケーションのさまざまな画面を構築します。

RootViewControllerもまた、UIViewControllerの一つであり、アプリケーションの他のビューコントローラーと同様の構造とライフサイクルを共有しますが、アプリケーションの入り口としての役割を果たす点が他のビューコントローラーとは異なります。

●RootViewControllerの取得方法

iOS開発においてRootViewControllerの取得は、アプリケーションの様々なシナリオで必要とされる基本的なスキルの一つです。

例えば、アプリの起動時、状態の復元、画面遷移の管理など、アプリケーションのライフサイクルの重要な局面でこの処理が用いられます。

RootViewControllerを正しく取得し管理することは、アプリの応答性と効率性を高める上で欠かせません。

○サンプルコード1:AppDelegateを通じて取得する

AppDelegateクラスは、iOSアプリケーションのデリゲートとして、アプリの起動、終了、バックグラウンド状態遷移などの中心的なイベントを扱います。

RootViewControllerを取得する一般的な方法の一つに、AppDelegate内でwindowのrootViewControllerプロパティにアクセスする方法があります。

#import "AppDelegate.h"

@interface AppDelegate ()

@end

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // windowのrootViewControllerにアクセスして取得
    UIViewController *rootViewController = self.window.rootViewController;

    // rootViewControllerを用いた処理をここで行う
    // 例えば、rootViewControllerに何かのデータを渡す、状態を設定するなど

    return YES;
}

@end

このコードでは、AppDelegateのapplication:didFinishLaunchingWithOptions:メソッド内でwindowプロパティのrootViewControllerにアクセスしています。

この例ではアプリケーションが起動された後、最初に表示されるViewControllerを取得しています。

取得したrootViewControllerを使用して、適宜、必要な初期設定やデータの受け渡しを行うことができます。

このコードを実行すると、アプリケーションのwindowが持つrootViewControllerが取得され、変数rootViewControllerに格納されます。

この段階で得られたViewControllerを操作することにより、アプリケーションのさまざまな初期設定やデータ準備を行うことが可能となります。

○サンプルコード2:Windowオブジェクトから取得する

アプリケーション内で複数のwindowがある場合や、特定のシーンで異なるwindowを参照する必要がある時、KeyWindowを介してrootViewControllerを取得することもあります。

KeyWindowは現在ユーザーによってアクティブに使用されているwindowを指し、通常、アプリケーションで一番最初に作成されるwindowです。

#import <UIKit/UIKit.h>

int main(int argc, char * argv[]) {
    @autoreleasepool {
        // UIApplicationMainが実行されることでアプリケーションのライフサイクルが開始されます
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}

// AppDelegate.m
@implementation AppDelegate

- (void)someMethod {
    // 現在のkeyWindowを取得してrootViewControllerを参照する
    UIViewController *rootViewController = [UIApplication sharedApplication].keyWindow.rootViewController;

    // 以降、取得したrootViewControllerを使った処理を実行する
}

@end

このコードではUIApplicationのsharedApplicationメソッドを使用してアプリケーションのシングルトンインスタンスを取得し、keyWindowプロパティを介してアクティブなwindowのrootViewControllerを参照しています。

この方法で得られるrootViewControllerを利用することで、例えばアプリケーションのユーザーインターフェースに関する設定を動的に変更するといったことが実現可能です。

このコードの実行によって、アプリケーションのKeyWindowが保持するrootViewControllerを取得することができ、以降の処理でそのViewControllerを基点として操作が行えるようになります。

これにより、動的なUI更新やViewController間のデータ受け渡し、ViewControllerの状態管理など、多岐にわたるアプリケーションの制御が可能になるのです。

○サンプルコード3:最前面のViewControllerを取得する

iOSアプリケーション開発において、時として最前面に表示されているViewControllerをプログラム的に取得したい場面があります。

例えば、アラートを表示したいときや、ユーザーの操作に基づいて現在表示されている画面の情報を取得したい時です。

Objective-Cでこの操作を行うには、次のようなコードを使用します。

#import <UIKit/UIKit.h>

UIViewController *getTopViewController(UIViewController *rootViewController) {
    if (rootViewController.presentedViewController) {
        return getTopViewController(rootViewController.presentedViewController);
    }
    if ([rootViewController isKindOfClass:[UINavigationController class]]) {
        UINavigationController *navigationController = (UINavigationController *)rootViewController;
        return getTopViewController(navigationController.visibleViewController);
    }
    if ([rootViewController isKindOfClass:[UITabBarController class]]) {
        UITabBarController *tabController = (UITabBarController *)rootViewController;
        return getTopViewController(tabController.selectedViewController);
    }
    return rootViewController;
}

// 使用例
UIViewController *topController = getTopViewController(UIApplication.sharedApplication.keyWindow.rootViewController);

このコードでは、UIViewControllerを引数に取る再帰関数getTopViewControllerを定義しています。

この関数は、提示されたViewControllerが更に他のViewControllerを表示しているかどうかをチェックし、表示している場合はそのViewControllerに対して再帰的に自身を呼び出します。

これにより、最終的に最前面にあるViewControllerを取得することができます。

UINavigationControllerUITabBarControllerのようなコンテナViewControllerも考慮しており、それぞれの最前面のViewControllerを取得する処理を含んでいます。

このコードを実行すると、アプリケーション内で現在最前面に表示されているViewControllerが変数topControllerに格納されます。

これを利用することで、最前面のViewController上で様々な操作を行うことが可能となります。

○サンプルコード4:StoryboardのIdentifierを使う

Storyboardを使用しているプロジェクトにおいては、StoryboardのIdentifierを使って特定のViewControllerをインスタンス化し取得することが一般的です。

IdentifierはStoryboard上でViewControllerに対して設定されたもので、それをコードから参照してViewControllerを生成するために使用します。

#import <UIKit/UIKit.h>

// StoryboardのIdentifierを指定してViewControllerを取得する
UIViewController *viewControllerFromStoryboard(NSString *storyboardName, NSString *viewControllerIdentifier) {
    UIStoryboard *storyboard = [UIStoryboard storyboardWithName:storyboardName bundle:nil];
    UIViewController *viewController = [storyboard instantiateViewControllerWithIdentifier:viewControllerIdentifier];
    return viewController;
}

// 使用例
UIViewController *myViewController = viewControllerFromStoryboard(@"Main", @"MyViewControllerIdentifier");

このコードでは、Storyboardの名前とViewControllerのIdentifierを引数として受け取り、指定されたStoryboardからViewControllerをインスタンス化しています。

これにより、コード内で直接ViewControllerを生成することなく、Storyboardの設計通りのViewControllerを取得して使用することができます。

この方法で取得したViewControllerは、Storyboardで設定されたプロパティや関連付けられたSegueを保持しており、インターフェースとコードの整合性を保ちながら開発を行うことが可能です。

○サンプルコード5:Xcodeのデバッグ機能を利用する

Xcodeのデバッグ機能を使えば、実行中のアプリケーションの状態をリアルタイムで把握し、特定のViewControllerを取得することもできます。

Xcodeのデバッグエリアにあるlldbコマンドラインを通じて、以下のようなコマンドを入力することで、最前面のViewControllerの情報を取得できます。

// LLDBコマンドラインにて実行
po UIApplication.sharedApplication.keyWindow.rootViewController

ここで使用されているpoコマンドは「print object」の略で、指定されたオブジェクトの説明(description)を出力します。

このコマンドを実行することで、現在のrootViewControllerの情報をデバッグコンソール上に表示することができ、アプリケーションの動作確認やバグの特定に役立ちます。

●RootViewControllerの応用例

iOS開発におけるRootViewControllerは、データの受け渡しやUIの更新など多岐にわたる場面で活用されます。

例えば、ユーザーの操作に応じてRootViewControllerを動的に更新することで、アプリケーションの柔軟性とユーザー体験を向上させることが可能です。

ここでは、そのようなシナリオのサンプルコードとして、Modal表示後のRootViewControllerの更新を挙げ、その実装方法と実行結果について説明します。

○サンプルコード6:RootViewControllerを使ったデータの受け渡し

UIViewController間でのデータ受け渡しは、アプリケーションにおいて非常に一般的なタスクです。

例として、新しいViewControllerからRootViewControllerにデータを戻す必要があるシナリオを考えます。

下記のコードは、Modalで表示されたViewControllerからデータをRootViewControllerに渡す一例を表しています。

// AppDelegate.h
@interface AppDelegate : UIResponder <UIApplicationDelegate>
@property (strong, nonatomic) UIWindow *window;
@end

// ViewControllerA.h
@interface ViewControllerA : UIViewController
- (void)updateData:(NSDictionary *)data;
@end

// ViewControllerA.m
@implementation ViewControllerA
- (void)updateData:(NSDictionary *)data {
    // ここで受け取ったデータを使ってUI更新などを行う
}
@end

// ViewControllerB.m
#import "ViewControllerA.h"
#import "AppDelegate.h"

@implementation ViewControllerB

- (void)sendDataBackToRoot {
    // 何らかのデータを準備する
    NSDictionary *dataToSend = @{@"key": @"value"};

    // AppDelegate経由でRootViewControllerを取得する
    AppDelegate *appDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
    ViewControllerA *rootViewController = (ViewControllerA *)appDelegate.window.rootViewController;

    // データをRootViewControllerのメソッドを使って更新する
    [rootViewController updateData:dataToSend];

    // Modalを閉じる
    [self dismissViewControllerAnimated:YES completion:nil];
}
@end

このコードではAppDelegateを介してWindowのRootViewControllerにアクセスし、特定のメソッドを呼び出しています。

ここではupdateData:メソッドをRootViewControllerで定義し、そこにデータを渡すことで情報の更新を行っています。

実行すると、Modalで開いたViewControllerからRootViewControllerへのデータの受け渡しがスムーズに行われます。

○サンプルコード7:Modal表示からのRootViewControllerの更新

アプリケーションのフローにおいては、ユーザーがModalで表示したViewControllerを閉じた後に、RootViewControllerのUIを更新することがよくあります。

例えば、ユーザーが設定画面で変更を加えた後、設定画面を閉じてメイン画面に反映させたい場面です。

// MainViewController.m
#import "MainViewController.h"

@implementation MainViewController

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    // 何らかの方法でデータを更新する必要があるかチェックする
    [self checkForUpdates];
}

- (void)checkForUpdates {
    // 更新が必要かチェックし、必要ならばUIを更新する
    // 例: ネットワークから設定をフェッチしてUIを更新
}
@end

// SettingsViewController.m
#import "AppDelegate.h"

@implementation SettingsViewController

- (void)changeSettingsAndDismiss {
    // 設定を変更する処理
    // ...

    // Modalを閉じるときにRootViewControllerの更新トリガーを呼び出す
    [self dismissViewControllerAnimated:YES completion:^{
        AppDelegate *appDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
        MainViewController *rootViewController = (MainViewController *)appDelegate.window.rootViewController;
        [rootViewController checkForUpdates];
    }];
}
@end

この例では、設定ViewControllerからModalを閉じる際に、completionブロック内でRootViewControllerのcheckForUpdatesメソッドを呼び出しています。

これにより、RootViewControllerが表示される直前に更新のチェックが行われ、必要に応じてUIがリフレッシュされます。

このようにして、ユーザーに最新の情報を提供することが可能となります。

●注意点と対処法

Objective-CでRootViewControllerを取得する際には、いくつかの注意点があります。

ここでは、それらのポイントとその対処法について解説します。

RootViewControllerを取得する過程でよく遭遇するのが、nilが返されるという問題です。

これは多くの場合、アプリのライフサイクルの初期段階でRootViewControllerへの参照を試みることに起因しています。

例えば、アプリがまだ起動していない、またはウィンドウ自体がまだ設定されていない時点で取得しようとすると、当然ながらRootViewControllerは存在しません。

この問題を回避するためには、RootViewControllerへの参照を取得する適切なタイミングを見極めることが重要です。

通常は、アプリケーションが完全に起動している(例えばapplication:didFinishLaunchingWithOptions:メソッドの実行が完了した後)時点で取得を試みるべきです。

○RootViewController取得時の一般的なエラー

RootViewController取得時の一般的なエラーには、「nil参照」や「不正なViewControllerのインスタンスへの参照」というものがあります。

これらのエラーは、アプリケーションの構造を正しく理解していないことに起因することが多いです。

例えば、下記のコードはAppDelegateを通じてRootViewControllerを取得しようとするものですが、不適切なタイミングで呼び出された場合、エラーにつながる可能性があります。

AppDelegate *appDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
UIViewController *rootViewController = appDelegate.window.rootViewController;

このコードでは、UIApplicationのシングルトンインスタンスからアプリケーションのデリゲートにアクセスし、そのwindowプロパティからRootViewControllerを取得しています。

この例では、アプリケーションのライフサイクル内で適切なタイミング、たとえばapplication:didFinishLaunchingWithOptions:の実行が完了した後にこのコードを呼び出す必要があります。

○非推奨のAPIへの対処法

Objective-Cを使用する上で、非推奨のAPIを使用してしまうリスクもあります。Appleは定期的にAPIを更新し、古いAPIを非推奨とすることがあります。

非推奨のAPIを使用すると、将来的にアプリがiOSの更新により動作しなくなる可能性があるため、常に最新のAPIを使うように心がけるべきです。

もし非推奨のAPIを使用しているコードに出会った場合は、公式のドキュメントを参照して最新のAPIに置き換える方法を調査します。

公式ドキュメントは、古いAPIの代わりに推奨される新しいAPIの使用例を提供することが多いです。

また、多くの場合、Xcodeの警告やオートコンプリート機能を通じて、非推奨のAPIの代替案が提示されます。

●カスタマイズ方法

Objective-CにおけるRootViewControllerのカスタマイズは、iOSアプリ開発において非常に重要なプロセスの一つです。

RootViewControllerはアプリの最も基本的なViewControllerであり、アプリのユーザーインターフェイスの起点となります。

このカスタマイズを通じて、開発者はアプリの起動時の挙動やナビゲーションの基礎を設計することができます。

例えば、アプリのテーマを変更する、特定のデータを最初の画面で表示する、またはアプリケーションの起動プロセス中に特別なロジックを実行する等のカスタマイズが考えられます。

カスタマイズを行うには、まずRootViewControllerがどのようにアプリケーション内で構成され使用されているかを理解する必要があります。

RootViewControllerは、通常AppDelegateクラス内でUIWindowのrootViewControllerプロパティに設定されています。

この設定を変更することで、どのViewControllerが最初に表示されるかを制御できます。

○サンプルコード8:RootViewControllerのカスタマイズ

AppDelegateクラスの中で、UIWindowのrootViewControllerプロパティに異なるViewControllerを設定することで、アプリケーションの起動時にどの画面を表示するかをカスタマイズすることができます。

#import "AppDelegate.h"
#import "CustomViewController.h"

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];

    // カスタムViewControllerのインスタンスを作成
    CustomViewController *customViewController = [[CustomViewController alloc] init];

    // UIWindowのrootViewControllerとして設定
    self.window.rootViewController = customViewController;

    [self.window makeKeyAndVisible];
    return YES;
}

このコードでは、UIApplicationのdelegateメソッドであるapplication:didFinishLaunchingWithOptions:内で、CustomViewControllerの新しいインスタンスを作成しています。

この例ではCustomViewControllerをrootViewControllerとして設定し、ウィンドウを表示状態にしています。

makeKeyAndVisibleメソッドを呼び出すことにより、このウィンドウ(とそのrootViewController)がスクリーンに表示され、ユーザーがインタラクションできるようになります。

カスタマイズの適用を確認するには、アプリケーションをビルドして実行します。

正常にカスタマイズが施されていれば、アプリの起動直後にCustomViewControllerのビューが表示されるはずです。

○サンプルコード9:独自のTransitionをRootViewControllerに適用する

アプリケーションに独自の遷移効果を追加することも、RootViewControllerのカスタマイズの一形態です。

下記のサンプルコードは、独自のアニメーション遷移をRootViewControllerに設定する方法を表しています。

#import "AppDelegate.h"
#import "CustomTransitionViewController.h"

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];

    // カスタム遷移を持つViewControllerのインスタンスを作成
    CustomTransitionViewController *customTransitionViewController = [[CustomTransitionViewController alloc] init];

    // 遷移アニメーションを設定するカスタムメソッドの呼び出し
    [customTransitionViewController applyCustomTransition];

    self.window.rootViewController = customTransitionViewController;

    [self.window makeKeyAndVisible];
    return YES;
}

この例ではCustomTransitionViewControllerという特別なViewControllerが、特有の遷移アニメーションを適用するメソッドapplyCustomTransitionを持っていることを前提としています。

このメソッドを使ってカスタムアニメーションを設定した後、それをrootViewControllerとしてウィンドウに適用します。

アプリケーションを実行した際に、カスタム遷移が適用されると、ユーザーは通常とは異なる独特な画面遷移体験をすることになります。

これにより、アプリのブランディングやユーザー体験を強化することが可能です。

まとめ

Objective-Cを使用したRootViewControllerの取得方法についての説明は、iOSアプリケーションの開発において非常に役立つ情報を含んでいます。

RootViewControllerは、iOSアプリケーションにおけるビュー階層の最上位に位置し、他のビューコントローラーの基点となります。

開発過程でRootViewControllerにアクセスする必要がある状況は多々あり、それには複数の手法が存在します。

記事全体を通して、読者が各ステップに必要な知識と理解を得られるように、具体的なサンプルコードと詳細な解説をしてきました。

Objective-CでのRootViewControllerの取得方法をステップバイステップで学んだ読者は、これらの情報を活用して、より洗練されたアプリケーション開発を行うことができるでしょう。