web-dev-qa-db-ja.com

Segueを使用したView Controller間のデータの受け渡し

私はiOSが初めてです。 ViewController間でデータを渡す際に問題が発生しています。 3つのviewController(view_1、view_2、view_3)があります。

ここに私のセットアップ:-

  • View_1を選択
  • view_2をプッシュします
  • view_3をプッシュします

「view_1」のViewController reference(id)を「view_3」に送信したい。だから私はinclude "view_3"を「view_1」に設定し、「view_3」の変数に値を設定します(view_3 *v3=[[view_3 alloc] init ]; v3.reference=self;)。コンソールには次のように表示されます:

ビューコントローラー:-; <ViewController:0x89e9540>

「view_1」ではなく「view_3」で、コンソールでは

ビューコントローラー(ヌル)

しかし、このデータを処理するために「view_2」を使用した場合。しかし、どのように?私はこの行動を知りたい、そしてこれを作成するための解決策はありますか?

助けてください。

24
MAX

セグエがトリガーされたときの「宛先コントローラーへのデータの受け渡し」は、メソッドprepareForSegue:sender:をオーバーライドすることにより実現されます。

通常、ソースView ControllerではなくdataをデスティネーションView Controllerに渡します。 「データ」は、アプリケーションの「モデル」の特定の側面です。 「ユーザー」などのオブジェクト、または「ユーザー」を含む配列などです。

destination View Controllerは、notsource View Controllerの知識を持っているものとします。つまり、宛先View Controllerは、ソースView Controllerのヘッダーをインポートする必要はありません。

一方、source view controller (may_は、destination view controllerの具体的なクラスまたはbaseクラスの知識を持っています宛先View Controller。したがって、宛先View Controllerのヘッダーをインポートします。

参照: セグエがトリガーされたときの宛先コントローラーの構成

送信元と送信先の間に何らかの「通信プロトコル」が必要な場合は、委任を使用して他のView Controllerと通信できます。これには、@ protocolの定義(たとえば、メソッドdoneButtonを持つ)と、宛先View Controllerで定義されたプロパティdelegateが含まれます。プロトコルは、宛先View Controllerのspecificの場合、宛先View Controllerのヘッダーで定義する必要があります。通常、プロトコルは、ソースコントローラーの要件からではなく、宛先コントローラーの観点から定義します。

次に、ソースView Controllerはデリゲートを作成し(それ自体がすでにそうでない場合)、宛先View Controllerのdelegateを設定します。宛先View Controllerはデリゲートメソッドをデリゲートに送信し、デリゲートがそれを処理します。

これで、VC_AからVC_Bに「データ」を渡すのは簡単です。 prepareForSegue:sender:を使用するいくつかの例を読む必要があります。たとえば、destination View Controllerには、表示するthingを表すプロパティdataがあります。ソースView Controllerは、prepareForSegue:sender:でこのプロパティを設定する必要があります。

VC_AからVC_B経由でVC_Cにデータを渡すことも簡単です。

注:各View Controllerは、nextに適したdataにするために、tailor(分離、変更、準備、スライス、変換など)itsdataを実行できます。ビューコントローラー。


ソースビューコントローラVC_Bで使用できないデータがVC_Cに必要な場合、これを解決する方法がいくつかあります。ただし、これは通常bad designのサインです。

couldには、globalというアプリケーションモデルがあります。 「アプリケーションモデル」がタイプDocumentのオブジェクトであるとします。いつでも、そのアプリケーションモデルのインスタンスはoneだけであるとします。次に、モデルは「シングルトン」であり、次のようにアプリ内のどこからでもcouldにアクセスできます。

Document* document = [Document sharedDocument];

ただし、モデルのインスタンスを取得するための好ましい方法は、first View Controllerでのアクセスです。この場合、VC_Aにアクセスする必要があります。

次に、VC_AはDocumentインスタンスを次のビューコントローラーVC_Bに渡します。また、VC_BはドキュメントオブジェクトをVC_Cに渡します。

公式ドキュメント「 View Controller Programming Guide for iOS 」をお読みください。


例1

「ユーザー」リストがあるとします。リストはTable View Controllerに表示される必要があり、1人のユーザーの詳細を示す詳細ビューも必要です。

Table View Controllerには、「data」プロパティusersがあります。

UsersTableViewController.h

@interface UsersTableViewController : UIViewController
@property (nonatomic, readonly) NSArray* users;
@end

(厳密には、このuserプロパティはパブリックである必要はありません。たとえば、テーブルビューがユーザー自体のリストを内部で取得する場合、外部からアクセスする必要はありません。

「users」配列は、行で表示される表ビューのdataです。各行には、ユーザーの「概要」が表示されます。

ユーザーの詳細は、detail view controllerに表示される必要があります。 Detail View Controllerのデータは、Userタイプの単一ユーザーです。

ユーザーがテーブルビューで特定の行をタブで移動すると、詳細ビ​​ューコントローラーが表示されます。表示する前に、Table View ControllerはDetail View Controllerを設定する必要があります。TableView Controllerは、Detail View Controllerの「データプロパティ」に現在選択されているserを割り当てます。したがって、Detail View Controllerには、publicプロパティuserが必要です。

@interface UserViewController : UIViewController
@property (nonatomic) User* user;
@end

Table View Controllerは、prepareForSegue:sender:のdetail View Controllerを構成します。

InUsersTableViewController.m

- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    if ([[segue identifier] isEqualToString:@"ShowUserDetails"]) {
        UserViewController* userViewController = [segue destinationViewController];
        userViewController.user = [self.users objectInListAtIndex:[self.tableView indexPathForSelectedRow].row];
    }
}

例2

2番目の例はより複雑で、コントローラー間の通信を確立する手段として「委任」を使用します。

注意:

これは完全な例ではありません。この例の目的は、「委任」の使用方法を示します。例に示すように、データタスクのフル機能の実装には、かなりの労力が必要です。このようなシナリオでは、「委任」がこれを達成するための最も好ましいアプローチ(IMHO)になります。

したいとしましょう

  • ユーザーを表示
  • ユーザーの変更(編集)
  • 新しいユーザーを作成し、
  • ユーザーを削除する

詳細ビュー内から。

これらの「データタスク」は、詳細ビューコントローラー自体によって実行されるものではなく、代わりにデリゲートがこれらのデータタスクを担当します。

これらのデータアクションは、デリゲートによって処理されます。

@protocol UserDataSourceDelegateProtocol <NSObject>
- (User*) viewControllerUser:(UserViewControllerBase*)viewController;
- (void) viewController:(UserViewControllerBase*)viewController dismissWithUpdatedUser:(User*)user;
- (void) viewController:(UserViewControllerBase*)viewController dismissWithDeletedUser:(User*)user;
- (void) viewController:(UserViewControllerBase*)viewController dismissWithCreatedUser:(User*)user;
@end

このプロトコルは、基本的なCRUDメソッド(作成、読み取り、更新、削除)を反映しています。

繰り返しますが、Detail View Controller 自体がこれらのデータメソッドを実行することは望ましくありませんが、代わりにUserDataSourceDelegateProtocolを実装するインスタンスによって実行されます。 Detail View Controllerにはこのデリゲートのプロパティがあり、これらの「データタスク」をデリゲートに送信します。

いくつかの詳細ビューコントローラ、抽象クラスUserViewControllerBaseのすべてのサブクラスがあり、showeditおよびcreateタスクを処理します。ユーザーの削除は、テーブルビューと「ユーザーの表示」ビューコントローラーで実行できます。

  • ShowUserViewController
  • EditUserViewController
  • NewUserViewController

たとえば、EditUserViewControllerは、ユーザーが[戻る]ボタンを押したとき、およびユーザーがユーザーオブジェクトを変更した場合にviewController:dismissWithUpdatedUser:を送信します。現在、デリゲートは詳細ビューを閉じることを許可する場合と許可しない場合があります。たとえば、検証エラーがある場合は許可されない場合があります。

UserDataSourceDelegateProtocolプロトコルmayは、ルートビューコントローラー、たとえばテーブルビューコントローラーに実装されます。ただし、データタスクの処理を唯一の責任とする別のクラスの方が適切な場合があります。以下のサンプルでは、​​Table View Controllerもこのデータハンドラになります。

UserDataSourceDelegateProtocolは、追加のヘッダーで定義できます。

UsersTableViewController.m

#import "UserDataSourceDelegateProtocol.h"
#import "ShowUserViewController.h"


@interface UsersTableViewController () <UserDataSourceDelegateProtocol> 
@property (nonatomic, readonly) NSArray* users;
@end


// This delegate will be called when the detail view controller request 
// the user object which shall be displayed.
- (User*) viewControllerUser:(UserViewControllerBase*)viewController {
    return [self.users objectInListAtIndex:[self.tableView indexPathForSelectedRow].row];
}

ここで、Table View ControllerはShow User Detail View Controllerを設定します:

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    if ([segue.identifier isEqualToString:UserShowSegueID])
    {
        ShowUserViewController* showViewController = segue.destinationViewController;
        showViewController.delegate = self; // Data Source Handler is self
    }
}

「ユーザーの編集」ビューコントローラーは通常、「ユーザーの表示」ビューコントローラーの宛先ビューコントローラーであり、ユーザーが「編集」ボタンを押すと表示されます。

「ユーザーの表示」View Controllerは、「ユーザーの編集」View Controllerのデリゲートをセットアップし、同じデリゲートを取得します。

InShowUserViewController.m

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    if ([segue.identifier isEqualToString:UserEditSegueID])
    {
        EditUserViewController* editViewController = segue.destinationViewController;
        editViewController.delegate = self.delegate; // pass through the data source hanlder
    }
}

データデリゲートは、更新されたユーザーを次のように処理できます。

UsersTableViewController.m

- (void) viewController:(UserViewControllerBase*)viewController
 dismissWithUpdatedUser:(User*)user {
    if (/* is valid user and can be saved */) {
        [viewController.presentingViewController dismissViewControllerAnimated:YES
                                                                     completion:nil];
    }
}
46
CouchDeveloper

YouTubeのSwiftプロジェクト は、これを行う方法を最終的に理解するのに役立ちました。

同様の線に沿って簡単な例をセットアップしました。テキストフィールドにテキストを入力してボタンを押すと、次のView Controllerのラベルにテキストが配置されます。

ストーリーボードをセットアップする

enter image description here

それほど難しくありません。 Interface Builderでストーリーボードレイアウトを作成します。セグエを作るには control ボタンをクリックして、2番目のView Controllerにドラッグします。

First View Controller

最初のView Controllerのコードは

import UIKit

class FirstViewController: UIViewController {

    @IBOutlet weak var textField: UITextField!

    // This function is called before the segue
    override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {

        // get a reference to the second view controller
        let secondViewController = segue.destinationViewController as! SecondViewController

        // set a variable in the second view controller with the String to pass
        secondViewController.receivedString = textField.text!
    }

}

セカンドビューコントローラー

そして、2番目のView Controllerのコードは

import UIKit

class SecondViewController: UIViewController {

    @IBOutlet weak var label: UILabel!

    // This variable will hold the data being passed from the First View Controller
    var receivedString = ""

    override func viewDidLoad() {
        super.viewDidLoad()

        // Used the text from the First View Controller to set the label
        label.text = receivedString
    }

}

忘れないで

  • UITextFieldおよびUILabelのアウトレットを接続します。
  • 最初と2番目のView Controllerを適切なSwift IBのファイルに設定します。

データをThird View Controllerに渡すプロセスは同じです。

12
Suragch

解決策はありますが、最も適切な方法はありません。

ViewController3.h

-(void)getUser:(NSString *)strPassedUser;

viewController3.mに移動し、@ interfaceの上に次のような変数を追加します

NSString *recievingVariable ;

次に、ViewController3.m内のいくつか

-(void)getUser:(NSString *)strPassedUser
{
    recievingVariable = strPassedUser;
}

ViewController1とViewController3をインポートすると、このようになります..

ViewController3 * vc3 = [ViewController3 alloc]getUser :@"me"];

この場合、関数getUserが呼び出され、receivingVariable= me

2
naveen

より良い簡単なソリューション。

UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
UIViewController *vc = [storyboard instantiateViewControllerWithIdentifier:@"webView"];
webView = (WebViewController *)vc;
webView.strWebLink = @"http://www.Google.com/";
[self.navigationController showViewController:vc sender:self];
1
iPhone 7

シングルトンパターンを使用します。

ソフトウェアエンジニアリングのシングルトンパターンは、クラスが1つのインスタンスのみを持ち、グローバルアクセスポイントを提供する設計パターンです。

一意のインスタンスがあるため、クラス変数とメソッドはアプリケーション/ネームスペース全体で共有されます。

例:

class Singleton {
    static let sharedInstance = Singleton()
    var studentId = 1281
}

そして、次のようにアプリのどこでも使用できます。

var studentId = Singleton.sharedInstance.studentId
print("Student Id: \(studentId)")
1
Alzayed

in Swift 4.0

 // In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    let vc = segue.destination as! SecondViewController
    vc.username = self.username
}

確認してください

segue.destinationは、ここで本当の違いです。

1
Zumry Mohamed