私はiOSとObjective-C、そしてMVC全体のパラダイムに不慣れです。
データ入力フォームとして機能するビューがあり、ユーザーに複数の製品を選択するオプションを与えたいと思います。製品はUITableViewController
で別のビューに表示されており、複数選択が可能になっています。
私の質問は、あるビューから別のビューにデータを転送する方法を教えてください。 UITableView
の選択を配列で保持しますが、フォームの送信時に他のデータと一緒にCore Dataに保存できるようにするにはどうすればよいでしょうか。
私は周りをサーフィンして、何人かの人々がアプリデリゲートで配列を宣言するのを見ました。私はシングルトンについて何かを読みましたが、それらが何であるかを理解していません、そして私はデータモデルを作成することについて何かを読みます。
これを実行するための正しい方法は何でしょうか。また、どうすればよいでしょうか。
この質問はここではstackoverflowで非常に人気があるように思われるので、私は私のようにiOSの世界から始める人々を助けるために私はもっと良い答えを出すことを試みると思いました。
私はこの答えが人々に理解されるのに十分明確であり、私が何も見逃していないことを願っています。
データを転送する
別のView ControllerからView Controllerにデータを渡す。ナビゲーションスタックにプッシュしている可能性があるオブジェクト/値をあるView Controllerから別のView Controllerに渡す場合は、このメソッドを使用します。
この例では、ViewControllerA
とViewControllerB
があります。
BOOL
からViewControllerA
にViewControllerB
値を渡すためには、次のようにします。
ViewControllerB.h
でBOOL
のプロパティを作成します
@property (nonatomic, assign) BOOL isSomethingEnabled;
ViewControllerA
ではViewControllerB
について説明する必要があるので、
#import "ViewControllerB.h"
それからビューをロードしたい場所。 didSelectRowAtIndex
またはIBAction
は、navスタックにプッシュする前にViewControllerB
にプロパティを設定する必要があります。
ViewControllerB *viewControllerB = [[ViewControllerB alloc] initWithNib:@"ViewControllerB" bundle:nil];
viewControllerB.isSomethingEnabled = YES;
[self pushViewController:viewControllerB animated:YES];
これはisSomethingEnabled
のViewControllerB
をBOOL
の値YES
に設定します。
Seguesを使用してデータを転送する
ストーリーボードを使用している場合は、セグエを使用している可能性が最も高いので、データを転送するにはこの手順が必要になります。これは上記と似ていますが、View Controllerをプッシュする前にデータを渡す代わりに、というメソッドを使用します。
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
BOOL
からViewControllerA
にViewControllerB
を渡すには、次のようにします。
ViewControllerB.h
でBOOL
のプロパティを作成します
@property (nonatomic, assign) BOOL isSomethingEnabled;
ViewControllerA
ではViewControllerB
について説明する必要があるので、
#import "ViewControllerB.h"
ストーリーボード上でViewControllerA
からViewControllerB
へのセグエを作成し、それに識別子を付けます。この例では"showDetailSegue"
と呼びます。
次に、任意のセグエが実行されたときに呼び出されるメソッドをViewControllerA
に追加する必要があります。そのため、どのセグエが呼び出されたのかを検出してから何かを実行する必要があります。この例では"showDetailSegue"
をチェックし、それが実行された場合はBOOL
の値をViewControllerB
に渡します。
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if([segue.identifier isEqualToString:@"showDetailSegue"]){
ViewControllerB *controller = (ViewControllerB *)segue.destinationViewController;
controller.isSomethingEnabled = YES;
}
}
ビューをナビゲーションコントローラに埋め込む場合は、上記の方法を次のように少し変更する必要があります。
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if([segue.identifier isEqualToString:@"showDetailSegue"]){
UINavigationController *navController = (UINavigationController *)segue.destinationViewController;
ViewControllerB *controller = (ViewControllerB *)navController.topViewController;
controller.isSomethingEnabled = YES;
}
}
これはisSomethingEnabled
のViewControllerB
をBOOL
の値YES
に設定します。
データの受け渡し
データをViewControllerB
からViewControllerA
に戻すには、Protocols and DelegatesまたはBlocks)を使う必要があります。後者は、コールバックの疎結合メカニズムとして使用できます。
これを行うには、ViewControllerA
をViewControllerB
のデリゲートにします。これによりViewControllerB
はViewControllerA
にメッセージを送り返し、データを送り返すことができます。
ViewControllerA
をViewControllerB
のデリゲートにするには、ViewControllerB
のプロトコルに準拠する必要があります。これはどのメソッドを実装しなければならないかをViewControllerA
に伝えます。
ViewControllerB.h
では、#import
の下、@interface
の上にはプロトコルを指定します。
@class ViewControllerB;
@protocol ViewControllerBDelegate <NSObject>
- (void)addItemViewController:(ViewControllerB *)controller didFinishEnteringItem:(NSString *)item;
@end
次にまだViewControllerB.h
でdelegate
プロパティを設定してViewControllerB.m
に合成する必要があります
@property (nonatomic, weak) id <ViewControllerBDelegate> delegate;
ViewControllerB
では、View Controllerを開くときにdelegate
上のメッセージを呼び出します。
NSString *itemToPassBack = @"Pass this value back to ViewControllerA";
[self.delegate addItemViewController:self didFinishEnteringItem:itemToPassBack];
ViewControllerB
については以上です。 ViewControllerA.h
では、ViewControllerA
にViewControllerB
をインポートし、そのプロトコルに準拠するように指示します。
#import "ViewControllerB.h"
@interface ViewControllerA : UIViewController <ViewControllerBDelegate>
ViewControllerA.m
では、私たちのプロトコルから以下のメソッドを実装してください。
- (void)addItemViewController:(ViewControllerB *)controller didFinishEnteringItem:(NSString *)item
{
NSLog(@"This was returned from ViewControllerB %@",item);
}
viewControllerB
をナビゲーションスタックにプッシュする前に、ViewControllerB
がそのデリゲートであることをViewControllerA
に伝える必要があります。そうしないとエラーが発生します。
ViewControllerB *viewControllerB = [[ViewControllerB alloc] initWithNib:@"ViewControllerB" bundle:nil];
viewControllerB.delegate = self
[[self navigationController] pushViewController:viewControllerB animated:YES];
NS通知センター データを渡す別の方法です。
// add observer in controller(s) where you want to receive data
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleDeepLinking:) name:@"handleDeepLinking" object:nil];
-(void) handleDeepLinking:(NSNotification *) notification {
id someObject = notification.object // some custom object that was passed with notification fire.
}
// post notification
id someObject;
[NSNotificationCenter.defaultCenter postNotificationName:@"handleDeepLinking" object:someObject];
あるクラスから別のクラスにデータを渡す (クラスには、任意のコントローラ、ネットワーク/セッションマネージャ、UIViewサブクラス、またはその他のクラスを指定できます)
ブロックは無名関数です。
この例では、 Controller B から Controller A にデータを渡します。
ブロックを定義します
@property void(^selectedVoucherBlock)(NSString *); // in ContollerA.h
ブロックハンドラ(リスナ)の追加 値が必要な場所(たとえば、ControllerAでAPI応答が必要、またはAでContorllerBデータが必要)
// in ContollerA.m
- (void)viewDidLoad {
[super viewDidLoad];
__unsafe_unretained typeof(self) weakSelf = self;
self.selectedVoucherBlock = ^(NSString *voucher) {
weakSelf->someLabel.text = voucher;
};
}
コントローラBに行く
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
ControllerB *vc = [storyboard instantiateViewControllerWithIdentifier:@"ControllerB"];
vc.sourceVC = self;
[self.navigationController pushViewController:vc animated:NO];
ファイアブロック
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:
(NSIndexPath *)indexPath {
NSString *voucher = vouchersArray[indexPath.row];
if (sourceVC.selectVoucherBlock) {
sourceVC.selectVoucherBlock(voucher);
}
[self.navigationController popToViewController:sourceVC animated:YES];
}
ここやStackOverflowの周りにはたくさんの説明がありますが、基本的なものを使いこなそうとしているだけの初心者であれば、このYouTubeチュートリアルを見てみてください(それがやっと使い方を理解するのに役立ちました)。
以下はビデオに基づいた例です。これは、First View ControllerのテキストフィールドからSecond View Controllerのラベルに文字列を渡すことです。
Interface Builderでストーリーボードレイアウトを作成します。セグエを作るために、あなただけ Control ボタンをクリックしてSecond View Controllerまでドラッグします。
First View Controller
First View Controllerのコードは
import UIKit
class FirstViewController: UIViewController {
@IBOutlet weak var textField: UITextField!
// This function is called before the segue
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// get a reference to the second view controller
let secondViewController = segue.destination as! SecondViewController
// set a variable in the second view controller with the String to pass
secondViewController.receivedString = textField.text!
}
}
セカンドビューコントローラー
Second 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から最初のView Controllerにデータを戻すには、 プロトコルとデリゲート を使用します。このビデオは、そのプロセスについては非常に明確な説明です。
以下は、ビデオを基にした例です(いくつか変更を加えています)。
Interface Builderでストーリーボードレイアウトを作成します。また、セグエを作るために、あなただけ Control ボタンからSecond View Controllerにドラッグします。セグエ識別子をshowSecondViewController
に設定します。また、次のコードの名前を使用してアウトレットとアクションを接続することを忘れないでください。
First View Controller
First View Controllerのコードは
import UIKit
class FirstViewController: UIViewController, DataEnteredDelegate {
@IBOutlet weak var label: UILabel!
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showSecondViewController" {
let secondViewController = segue.destination as! SecondViewController
secondViewController.delegate = self
}
}
func userDidEnterInformation(info: String) {
label.text = info
}
}
私たちのカスタムDataEnteredDelegate
プロトコルの使用に注意してください。
セカンドビューコントローラとプロトコル
2番目のView Controllerのコードは
import UIKit
// protocol used for sending data back
protocol DataEnteredDelegate: class {
func userDidEnterInformation(info: String)
}
class SecondViewController: UIViewController {
// making this a weak variable so that it won't create a strong reference cycle
weak var delegate: DataEnteredDelegate? = nil
@IBOutlet weak var textField: UITextField!
@IBAction func sendTextBackButton(sender: AnyObject) {
// call this method on whichever class implements our delegate protocol
delegate?.userDidEnterInformation(info: textField.text!)
// go back to the previous view controller
_ = self.navigationController?.popViewController(animated: true)
}
}
protocol
は、View Controllerクラスの外部にあります。
それでおしまい。今すぐアプリを実行すると、2番目のView Controllerから最初のView Controllerへデータを送り返すことができるはずです。
MVCのMは "Model"のためのものであり、MVCパラダイムでのモデルクラスの役割はプログラムのデータを管理することです。モデルはビューの反対です。ビューはデータの表示方法を認識していますが、データの処理方法については何も認識していません。一方、モデルはデータの処理方法についてはすべて認識しています。モデルは複雑になる可能性がありますが、必ずしもそうである必要はありません。アプリのモデルは、文字列や辞書の配列のように単純なものでもかまいません。
コントローラーの役割は、ビューとモデルを仲介することです。そのため、1つ以上のビューオブジェクトと1つ以上のモデルオブジェクトへの参照が必要です。モデルが辞書の配列で、各辞書がテーブルの1行を表すとしましょう。アプリのルートビューにはそのテーブルが表示され、ファイルから配列をロードすることに責任があるかもしれません。ユーザがテーブルに新しい行を追加することにしたとき、彼らはいくつかのボタンをタップします、そして、あなたのコントローラは新しい(可変の)辞書を作成してそれを配列に追加します。行を埋めるために、コントローラは詳細ビューコントローラを作成し、それに新しいディクショナリを渡します。詳細ビューコントローラは辞書を埋めて戻ります。辞書はすでにモデルの一部なので、他に何もする必要はありません。
IOSの異なるクラスにデータを受信する方法はいくつかあります。例えば -
NSUserDefaults
に保存 - 後でアクセスするためしかし、現在のクラスで割り当てが行われている別のクラスに値を渡すという単純なシナリオでは、最も一般的で好ましい方法は、割り当て後に値を直接設定することです。これは次のように行われます: -
2つのコントローラーを使ってそれを理解することができます - Controller1とController2
Controller1クラスでController2オブジェクトを作成し、渡されたString値でそれをプッシュするとします。これはこのようにすることができます: -
- (void)pushToController2 {
Controller2 *obj = [[Controller2 alloc] initWithNib:@"Controller2" bundle:nil];
[obj passValue:@"String"];
[self pushViewController:obj animated:YES];
}
Controller2クラスの実装では、この関数は次のようになります。
@interface Controller2 : NSObject
@property (nonatomic , strong) NSString* stringPassed;
@end
@implementation Controller2
@synthesize stringPassed = _stringPassed;
- (void) passValue:(NSString *)value {
_stringPassed = value; //or self.stringPassed = value
}
@end
これと同じ方法で、Controller2クラスのプロパティを直接設定することもできます。
- (void)pushToController2 {
Controller2 *obj = [[Controller2 alloc] initWithNib:@"Controller2" bundle:nil];
[obj setStringPassed:@"String"];
[self pushViewController:obj animated:YES];
}
複数の値を渡すには、次のように複数のパラメータを使用できます -
Controller2 *obj = [[Controller2 alloc] initWithNib:@"Controller2" bundle:nil];
[obj passValue:@“String1” andValues:objArray withDate:date];
また、共通の機能に関連する3つ以上のパラメータを渡す必要がある場合は、値をModelクラスに格納し、そのmodelObjectを次のクラスに渡すことができます。
ModelClass *modelObject = [[ModelClass alloc] init];
modelObject.property1 = _property1;
modelObject.property2 = _property2;
modelObject.property3 = _property3;
Controller2 *obj = [[Controller2 alloc] initWithNib:@"Controller2" bundle:nil];
[obj passmodel: modelObject];
あなたがしたいのであれば短いので -
1) set the private variables of the second class initialise the values by calling a custom function and passing the values.
2) setProperties do it by directlyInitialising it using the setter method.
3) pass more that 3-4 values related to each other in some manner , then create a model class and set values to its object and pass the object using any of the above process.
お役に立てれば
さらに調査した結果、Protocols and Delegatesがこれを実行するための正しい方法であることがわかった。
私はこの例を使いました
View Controllerと他のオブジェクトとの間でデータを共有する @ iPhone Dev SDK
うまくいき、ビュー間で文字列と配列を前後に渡すことができました。
ご協力ありがとうございます
ブロックを渡すことで、最もシンプルで洗練されたバージョンを見つけることができます。返されたデータを "A"として、返されるView Controllerを "B"として名前を付けます。この例では、2つの値を取得します。1つはType1、2つ目はType2です。
Storyboardを使用すると仮定すると、最初のコントローラは、たとえばセグエ準備中にコールバックブロックを設定します。
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.destinationViewController isKindOfClass:[BViewController class]])
{
BViewController *viewController = segue.destinationViewController;
viewController.callback = ^(Type1 *value1, Type2 *value2) {
// optionally, close B
//[self.navigationController popViewControllerAnimated:YES];
// let's do some action after with returned values
action1(value1);
action2(value2);
};
}
}
そして "B"ビューコントローラはコールバックプロパティ、BViewController.hを宣言する必要があります。
// it is important to use "copy"
@property (copy) void(^callback)(Type1 *value1, Type2 *value2);
実装ファイルBViewController.mよりも、コールバックを返すために必要な値を取得した後で呼び出す必要があります。
if (self.callback)
self.callback(value1, value2);
覚えておくべき1つのことは、ブロックを使うことはしばしば説明された here のように強くて弱い参照を管理する必要があるということです。
与えられた答えの多くにいくつかの良い情報がありますが、どれも質問に完全に対処しません。
質問は、View Controller間での情報の受け渡しについて尋ねています。具体的な例では、ビュー間で情報を受け渡しすることについて説明していますが、iOSには自明の新しさがあり、元のポスターはビュー間ではなくViewControllers間を意味していました(ViewControllerの関与なし)。すべての答えが2つのView Controllerに焦点を合わせているように見えますが、情報交換に2つ以上のView Controllerを含める必要があるようにアプリが進化した場合はどうなりますか?
元のポスターはまた シングルトン と AppDelegate の使用について尋ねました。これらの質問は答えられる必要があります。
完全な答えを望んでいるこの質問を見ている他の誰かを助けるために、私はそれを提供しようとするつもりです。
アプリケーションシナリオ
非常に仮説的で抽象的な議論をするのではなく、それは具体的なアプリケーションを念頭に置いておくのに役立ちます。 2ビューコントローラの状況と2ビューコントローラ以上の状況を定義するために、2つの具体的なアプリケーションシナリオを定義します。
シナリオ1: 最大で2つのView Controllerが情報を共有する必要があります。図1を参照してください。
アプリケーションには2つのView Controllerがあります。 ViewControllerA(データ入力フォーム)とView Controller B(製品リスト)があります。商品リストで選択されている項目は、データ入力フォームのテキストボックスに表示されている項目と一致する必要があります。このシナリオでは、ViewControllerAとViewControllerBは互いに直接通信する必要があり、他のView Controllerとは通信しません。
シナリオ2 :3人以上のView Controllerが同じ情報を共有する必要があります図2を参照してください。
アプリケーションには4つのView Controllerがあります。家の在庫を管理するためのタブベースのアプリケーションです。 3つのView Controllerは、同じデータに対して異なる方法でフィルタ処理されたビューを表示します。
個々の項目が作成または編集されたときはいつでも、他のView Controllerとも同期する必要があります。たとえば、ViewControllerDにボートを追加してもまだ保険に入っていない場合は、ユーザーがViewControllerA(高級品)およびViewControllerC(ホーム在庫全体)に移動したときにボートが表示される必要があります。 ViewControllerB(非保険商品)。新しい項目を追加するだけでなく、項目を削除したり(4つのView Controllerのいずれかから許可されている場合があります)、既存の項目を編集したりすることもできます。編集用).
すべてのView Controllerが同じデータを共有する必要があるため、4つすべてのView Controllerが同期している必要があります。したがって、1つのView Controllerが基本データを変更するたびに、他のすべてのView Controllerと何らかの通信を行う必要があります。このシナリオでは、各View Controllerが他のView Controllerと直接通信することを望まないことは明らかです。はっきりしない場合は、(4つではなく)20個の異なるView Controllerがあるかどうかを検討してください。 1台のView Controllerが変更されたときに、他の19台のView Controllerそれぞれに通知するのは、どれほど困難でエラーが発生しやすいでしょうか。
解決策:デリゲートとオブザーバパターン、そしてシングルトン
シナリオ1では、他の答えが示したように、いくつかの実行可能な解決策があります。
シナリオ2では、他に実行可能な解決策があります。
シングルトン はクラスのインスタンスであり、そのインスタンスはその存続期間中に存在する唯一のインスタンスです。シングルトンは、それが単一のインスタンスであるという事実からその名前を取得します。通常シングルトンを使用する開発者はそれらにアクセスするための特別なクラスメソッドを持っています。
+ (HouseholdInventoryManager*) sharedManager; {
static dispatch_once_t onceQueue;
static HouseholdInventoryManager* _sharedInstance;
// dispatch_once is guaranteed to only be executed once in the
// lifetime of the application
dispatch_once(&onceQueue, ^{
_sharedInstance = [[self alloc] init];
});
return _sharedInstance;
}
シングルトンとは何かを理解したので、シングルトンがどのようにオブザーバパターンに収まるかを説明しましょう。オブザーバパターンは、あるオブジェクトが別のオブジェクトによる変更に応答するために使用されます。 2番目のシナリオでは、4つの異なるView Controllerがあり、それらすべてが基礎となるデータへの変更について知りたいと考えています。 「基礎となるデータ」は単一のインスタンス、シングルトンに属する必要があります。 「変化について知る」ことは、シングルトンに加えられた変化を観察することによって達成される。
家の在庫アプリケーションは在庫品目のリストを管理するように設計されているクラスの単一のインスタンスを持つでしょう。管理者は、世帯品目の集まりを管理します。以下はデータマネージャのクラス定義です。
#import <Foundation/Foundation.h>
@class JGCHouseholdInventoryItem;
@interface HouseholdInventoryManager : NSObject
/*!
The global singleton for accessing application data
*/
+ (HouseholdInventoryManager*) sharedManager;
- (NSArray *) entireHouseholdInventory;
- (NSArray *) luxuryItems;
- (NSArray *) nonInsuredItems;
- (void) addHouseholdItemToHomeInventory:(JGCHouseholdInventoryItem*)item;
- (void) editHouseholdItemInHomeInventory:(JGCHouseholdInventoryItem*)item;
- (void) deleteHoueholdItemFromHomeInventory:(JGCHouseholdInventoryItem*)item;
@end
ホームインベントリアイテムのコレクションが変更されたら、View Controllerにこの変更を知らせる必要があります。上記のクラス定義は、これがどのように発生するのかを明確にしていません。観察者パターンに従う必要があります。 View ControllerはsharedManagerを正式に監視する必要があります。他のオブジェクトを観察する方法は2つあります。
シナリオ2では、KVOを使用して観察できるHouseholdInventoryManagerの単一プロパティはありません。簡単に観察できる単一のプロパティがないため、この場合はオブザーバパターンをNSNotificationCenterを使用して実装する必要があります。 4つのView Controllerはそれぞれ通知を購読し、sharedManagerは必要に応じて通知センターに通知を送信します。在庫マネージャは、在庫アイテムのコレクションがいつ変更されるかを知ることに関心がある可能性がある、View Controllerまたは他のクラスのインスタンスについて何も知る必要はありません。 NSNotificationCenterはこれらの実装の詳細を引き受けます。ビューコントローラは単に通知を購読し、データマネージャは単に通知を投稿します。
多くの初心者プログラマは、アプリケーションの有効期間中に常に1つの Application Delegate が存在するという事実を利用します。初心者プログラマーは、アプリケーション内の他の場所からアクセスするための便利さとして、この事実を使ってオブジェクトと機能をappDelegateに詰め込みます。 AppDelegateがシングルトンであるという理由だけで、他のすべてのシングルトンを置き換える必要があるという意味ではありません。 1つのクラスに負担がかかり過ぎるため、これは不適切な方法です。オブジェクト指向の良い方法では実行できません。各クラスには、簡単に説明できる明確な役割があります。多くの場合、クラスの名前だけです。
Application Delegateが肥大化し始めたときはいつでも、機能をシングルトンに削除し始めます。たとえば、コアデータスタックはAppDelegateに残すべきではなく、代わりに独自のクラスであるcoreDataManagerクラスに入れるべきです。
参考文献
ViewController 2(destination)からviewController 1(Source)にデータを戻すことは、もっとおもしろいことです。 :
それらについてはすでに説明しました。
私はもっとたくさんの方法があることがわかりました:
-Using Blockコールバック:
vC1のprepareForSegue
メソッドでそれを使う
NextViewController *destinationVC = (NextViewController *) segue.destinationViewController;
[destinationVC setDidFinishUsingBlockCallback:^(NextViewController *destinationVC)
{
self.blockLabel.text = destination.blockTextField.text;
}];
- ストーリーボードの使用アンワインド(終了)
次のように、VC 1にUIStoryboardSegue引数を持つメソッドを実装します。
-(IBAction)UnWindDone:(UIStoryboardSegue *)segue { }
StoryBoardの中で、「戻る」ボタンをvcの緑の終了ボタン(アンワインド)にフックします。 ] VC2のprepareForSegue内のdestinationViewControllerプロパティおよび VC1のプロパティを元に戻す前に変更します。
ストーリーボードを使用する別のオプション巻き戻し(終了) - あなたはVC1で書いた方法を使うことができます
-(IBAction)UnWindDone:(UIStoryboardSegue *)segue {
NextViewController *nextViewController = segue.sourceViewController;
self.unwindLabel.text = nextViewController.unwindPropertyPass;
}
そしてVC1のprepareForSegueでは、共有したい任意のプロパティを変更できます。
どちらのアンワインドオプションでも、ボタンのtagプロパティを設定し、prepareForSegueのでチェックすることができます。
私が議論に何かを追加したことを願っています。
:)乾杯。
データを共有する方法は複数あります。
NSUserDefaults
を使っていつでもデータを共有できます。選択したキーに関して共有する値を設定し、次のView Controllerでそのキーに関連付けられたNSUserDefault
から値を取得します。
[[NSUserDefaults standardUserDefaults] setValue:value forKey:key]
[[NSUserDefaults standardUserDefaults] objectForKey:key]
viewcontrollerA
にプロパティを作成するだけです。 viewcontrollerA
内にviewcontrollerB
のオブジェクトを作成し、そのプロパティに目的の値を割り当てます。
このためにカスタムデリゲートを作成することもできます。
OPはView Controllerについて言及していませんでしたが、答えの多くはそうしているので、あるView Controllerから別のView Controllerへデータを渡したいときにLLVMの新機能のいくつかがこれを容易にしたのです。いくつかの結果を取り戻します。
ストーリーボードセグ、ARCとLLVMブロックはこれを私にとってこれまで以上に簡単にします。上記のいくつかの答えはすでにストーリーボードとセグエスを述べていますが、それでも代表団に頼っていました。デリゲートを定義することは確かにうまくいきますが、ポインターやコードブロックを渡す方が簡単なことがあるかもしれません。
UINavigatorとセグエでは、サブサービスコントローラーに情報を渡して情報を取り戻す簡単な方法があります。 ARCは、NSObjectsから派生したものへのポインタの受け渡しを簡単にするので、サブサービスコントローラにデータの追加/変更/変更を行わせる場合は、可変インスタンスへのポインタを渡します。ブロックを使用するとアクションの受け渡しが簡単になるため、サブサービスコントローラーに上位コントローラーのアクションを呼び出させたい場合は、ブロックに渡します。あなたにとって意味のある引数をいくつでも受け入れるようにブロックを定義します。また、複数のブロックを使用するようにAPIを設計することもできます。
ここにセグエのりの2つの些細な例があります。 1つ目は入力用に渡された1つのパラメーター、もう1つは出力用に渡されたパラメーターを示しています。
// Prepare the destination view controller by passing it the input we want it to work on
// and the results we will look at when the user has navigated back to this controller's view.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
[[segue destinationViewController]
// This parameter gives the next controller the data it works on.
segueHandoffWithInput:self.dataForNextController
// This parameter allows the next controller to pass back results
// by virtue of both controllers having a pointer to the same object.
andResults:self.resultsFromNextController];
}
この2番目の例は、2番目の引数にコールバックブロックを渡すことを示しています。私はブロックを使うのが好きです。なぜならそれは関連する細部をソース - より高いレベルのソース - の近くに密接に保つからです。
// Prepare the destination view controller by passing it the input we want it to work on
// and the callback when it has done its work.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
[[segue destinationViewController]
// This parameter gives the next controller the data it works on.
segueHandoffWithInput:self.dataForNextController
// This parameter allows the next controller to pass back results.
resultsBlock:^(id results) {
// This callback could be as involved as you like.
// It can use Grand Central Dispatch to have work done on another thread for example.
[self setResultsFromNextController:results];
}];
}
あるコントローラから別のコントローラにデータを渡したい場合は、このコードを試してください。
FirstViewController.h
@property (nonatomic, retain) NSString *str;
SecondViewController.h
@property (nonatomic, retain) NSString *str1;
FirstViewController.m
- (void)viewDidLoad
{
// message for the second SecondViewController
self.str = @"text message";
[super viewDidLoad];
}
-(IBAction)ButtonClicked
{
SecondViewController *secondViewController = [[SecondViewController alloc] initWithNibName:@"SecondViewController" bundle:nil];
secondViewController.str1 = str;
[self.navigationController pushViewController:secondViewController animated:YES];
}
私は長い間この解決策を探していました、私はそれを見つけました。まず、SecondViewController.hファイル内のすべてのオブジェクトを次のように宣言します。
@interface SecondViewController: UIviewController
{
NSMutableArray *myAray;
CustomObject *object;
}
今すぐあなたの実装ファイルでこのようにそれらのオブジェクトのためにメモリを割り当てます
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self)
{
// Custom initialization
myAray=[[NSMutableArray alloc] init];
object=[[CustomObject alloc] init];
}
return self;
}
これでArray
とobjectにメモリを割り当てました。これで、このViewController
をプッシュする前に、そのメモリをいっぱいにすることができます
SecondViewController.hに行き、2つのメソッドを書きます。
-(void)setMyArray:(NSArray *)_myArray;
-(void)setMyObject:(CustomObject *)_myObject;
実装ファイルでは、関数を実装することができます
-(void)setMyArray:(NSArray *)_myArray
{
[myArra addObjectsFromArray:_myArray];
}
-(void)setMyObject:(CustomObject *)_myObject
{
[object setCustomObject:_myObject];
}
あなたのCustomObject
がそれにセッター関数を持たなければならないことを期待しています。
これで基本的な作業は完了です。あなたがSecondViewController
をプッシュしたい場所に行き、以下のことをする
SecondViewController *secondView= [[SecondViewController alloc] initWithNibName:@"SecondViewController " bundle:[NSBundle MainBundle]] ;
[secondView setMyArray:ArrayToPass];
[secondView setMyObject:objectToPass];
[self.navigationController pushViewController:secondView animated:YES ];
スペルミスには注意してください。
これはそれを行う方法ではありません、あなたはデリゲートを使用するべきです、私は2つのView Controller ViewController1とViewController2があり、このチェックは最初のものにあると仮定します適切な方法でそれを達成するには、以下を実行する必要があります。
プロジェクトに新しいファイルを追加する(Objective-Cプロトコル)File - > New。今度はそれをViewController1Delegateまたはあなたが望むものに名前を付けて@interfaceと@endディレクティブの間にそれらを書く
@optional
- (void)checkStateDidChange:(BOOL)checked;
今ViewController2.hに行き、追加します
#import "ViewController1Delegate.h"
次にその定義を
@interface ViewController2: UIViewController<ViewController1Delegate>
それではViewController2.mに行き、実装の中に以下を追加してください。
- (void)checkStateDidChange:(BOOL)checked {
if (checked) {
// Do whatever you want here
NSLog(@"Checked");
}
else {
// Also do whatever you want here
NSLog(@"Not checked");
}
}
ViewController1.hに移動して、次のプロパティを追加します。
@property (weak, nonatomic) id<ViewController1Delegate> delegate;
何らかのイベントの後にViewController2内にViewController1を作成している場合は、NIBファイルを使用して次のようにしてください。
ViewController1* controller = [[NSBundle mainBundle] loadNibNamed:@"ViewController1" owner:self options:nil][0];
controller.delegate = self;
[self presentViewController:controller animated:YES completion:nil];
ViewController1で変更されたチェックのイベントが検出されたときはいつでも、あなたはすべて設定済みです。
[delegate checkStateDidChange:checked]; // You pass here YES or NO based on the check state of your control
私があなたの質問をきちんと理解していなかったならば、はっきりしない何かがあるならば私に言いなさい。
あるビューコントローラから別のビューコントローラにデータを送信したい場合は、次のようにします。
ViewController:viewControllerAとviewControllerBがあるとします。
今viewControllerB.hに
@interface viewControllerB : UIViewController {
NSString *string;
NSArray *array;
}
- (id)initWithArray:(NSArray)a andString:(NSString)s;
ViewControllerB.m内
#import "viewControllerB.h"
@implementation viewControllerB
- (id)initWithArray:(NSArray)a andString:(NSString)s {
array = [[NSArray alloc] init];
array = a;
string = [[NSString alloc] init];
string = s;
}
ViewControllerA.m内
#import "viewControllerA.h"
#import "viewControllerB.h"
@implementation viewControllerA
- (void)someMethod {
someArray = [NSArray arrayWithObjects:@"One", @"Two", @"Three", nil];
someString = [NSString stringWithFormat:@"Hahahahaha"];
viewControllerB *vc = [[viewControllerB alloc] initWithArray:someArray andString:someString];
[self.navigationController pushViewController:vc animated:YES];
[vc release];
}
これがデリゲートを設定せずにviewControllerAからviewControllerBにデータを渡す方法です。 ;)
私の場合、アプリ内のほぼどこからでもデータにアクセスできるグローバルオブジェクトとして機能できるシングルトンクラスを使用しました。まず、シングルトンクラスを構築します。ページを参照してください。 " Objective-Cシングルトンはどのように見えるべきですか? "そして、オブジェクトをグローバルにアクセス可能にするためにしたことは、インポートを適用するためのappName_Prefix.pch
すべてのクラスのステートメント。このオブジェクトにアクセスして使用するには、クラスメソッドを実装して、独自の変数を含む共有インスタンスを返すだけです。
1. 2番目のView Controllerに最初のView Controllerのインスタンスを作成し、そのプロパティを@property (nonatomic,assign)
にします。
2. このビューコントローラのSecondviewController
インスタンスを割り当てます。
2. 選択操作が完了したら、最初のView Controllerに配列をコピーします。SecondViewをアンロードすると、FirstViewは配列データを保持します。
お役に立てれば。
以下のようにFirstViewControllerからSecondViewControllerにデータを渡す
例えば:
FirstViewController文字列値として
StrFirstValue = @"first";
それで、以下のステップを使用して、この値を2番目のクラスに渡すことができます。
1> SecondViewController.hファイルに文字列オブジェクトを作成する必要があります
NSString *strValue;
2> .hファイルでの宣言の下に、次のようにプロパティを宣言する必要があります。
@property (strong, nonatomic) NSString *strSecondValue;
3>ヘッダー宣言の下のFirstViewController.mファイルでその値を合成する必要があります。
@synthesize strValue;
そしてFirstViewController.hで:
@property (strong, nonatomic) NSString *strValue;
4> FirstViewControllerでは、どのメソッドから2番目のビューに移動するかを、そのメソッドのコードの下に書いてください。
SecondViewController *secondView= [[SecondViewController alloc]
initWithNibName:@"SecondViewController " bundle:[NSBundle MainBundle]];
[secondView setStrSecondValue:StrFirstValue];
[self.navigationController pushViewController:secondView animated:YES ];
私は現在MCViewFactoryと呼ばれるプロジェクトを通してこの問題に対するオープンソースの解決策に貢献しています。
https://github.com/YetiHQ/manticore-iosviewfactory
アイデアは、あなたが見ているビューを管理するためにグローバルファクトリを使用し、ビュー間でデータを切り替えて渡すために「インテント」を使用して、Androidの意図パラダイムを模倣することです。すべてのドキュメントはgithubページにありますが、ここでいくつかハイライトします。
ファクトリを初期化しながら、すべてのビューを.XIBファイルに設定し、それらをアプリデリゲートに登録します。
// Register activities
MCViewFactory *factory = [MCViewFactory sharedFactory];
// the following two lines are optional.
[factory registerView:@"YourSectionViewController"];
これで、あなたのVCでは、新しいVCに移動してデータを渡したいときはいつでも、新しいインテントを作成してそのデータをその辞書(savedInstanceState)に追加します。次に、単にfactoryの現在の目的を設定します。
MCIntent* intent = [MCIntent intentWithSectionName:@"YourSectionViewController"];
[intent setAnimationStyle:UIViewAnimationOptionTransitionFlipFromLeft];
[[intent savedInstanceState] setObject:@"someValue" forKey:@"yourKey"];
[[intent savedInstanceState] setObject:@"anotherValue" forKey:@"anotherKey"];
// ...
[[MCViewModel sharedModel] setCurrentSection:intent];
これに準拠するビューはすべてMCViewControllerのサブクラスである必要があります。これにより、新しいonResume:メソッドをオーバーライドして、渡したデータにアクセスできるようになります。
-(void)onResume:(MCIntent *)intent {
NSObject* someValue = [intent.savedInstanceState objectForKey:@"yourKey"];
NSObject* anotherValue = [intent.savedInstanceState objectForKey:@"anotherKey"];
// ...
// ensure the following line is called, especially for MCSectionViewController
[super onResume:intent];
}
何人かの人がこの解決策が便利で面白いと思うことを願っています。
次のview controller .h
でプロパティを作成し、getterとsetterを定義します。
NextVCのNextVC.hにこのproperty
を追加してください
@property (strong, nonatomic) NSString *indexNumber;
追加する
NextVC.mの@synthesize indexNumber;
そして最後
NextVC *vc=[[NextVC alloc]init];
vc.indexNumber=@"123";
[self.navigationController vc animated:YES];
私はこれが殴打された主題であることを知っています、しかしSwiftの傾斜でこの質問に答えて、素朴な例が欲しい人のために。
ボタン、ラベルなどがないことを除いて上記と同じです。あるビューから次のビューにデータを渡すだけです。
ストーリーボードの設定
3つの部分があります。
これは、それらの間にセグエがある非常に単純なビューレイアウトです。
これが送信者の設定です。
これが受信機の設定です。
最後に、セグエの設定です。
ビューコントローラ
これは単純なので、ボタンではなくアクションではなく、アプリケーションがロードされたときにデータを送信側から受信側に移動し、送信された値をコンソールに出力するだけです。
このページは最初にロードされた値を受け取り、それを渡します。
//
// ViewControllerSender.Swift
// PassDataBetweenViews
//
// Created by Chris Cantley on 8/25/15.
// Copyright (c) 2015 Chris Cantley. All rights reserved.
//
import UIKit
class ViewControllerSender: UIViewController {
// THE STUFF - put some info into a variable
let favoriteMovie = "Ghost Busters"
override func viewDidAppear(animated: Bool) {
// PASS IDENTIFIER - go to the recieving view controller.
self.performSegueWithIdentifier("goToReciever", sender: self)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
//GET REFERENCE - ...to the receiver view.
var viewControllerReceiver = segue.destinationViewController as? ViewControllerReceiver
//PASS STUFF - pass the variable along to the target.
viewControllerReceiver!.yourFavMovie = self.favoriteMovie
}
}
このページはロード時に変数の値をコンソールに送信するだけです。この時点で、私たちのお気に入りの映画はその変数にあるはずです。
//
// ViewControllerReceiver.Swift
// PassDataBetweenViews
//
// Created by Chris Cantley on 8/25/15.
// Copyright (c) 2015 Chris Cantley. All rights reserved.
//
import UIKit
class ViewControllerReceiver: UIViewController {
//Basic empty variable waiting for you to pass in your fantastic favorite movie.
var yourFavMovie = ""
override func viewDidLoad() {
super.viewDidLoad()
//And now we can view it in the console.
println("The Movie is \(self.yourFavMovie)")
}
}
それはあなたがセグエを使用したいがあなたがナビゲーションコントローラの下にあなたのページを持っていないならあなたがそれに取り組むことができる方法です。
実行されると自動的に受信者ビューに切り替わり、送信者から受信者に値が渡され、コンソールに値が表示されます。
.xibファイルを使用しているときに委任がそのような操作を実行する唯一の解決策ですが、上記のすべての答えは、委任を使用する必要がある.xibファイルのstoryboard
に対するものです。それが唯一の解決策です。
もう1つの解決策は、シングルトンクラスパターンを使用して一度初期化し、それをアプリケーション全体で使用することです。
viewControlerOneからViewControllerTwoにデータを渡したい場合は、これらを試してください。
viewControlerOne.hでこれらを行います。
@property (nonatomic, strong) NSString *str1;
viewControllerTwo.hでこれらを行います。
@property (nonatomic, strong) NSString *str2;
ViewControllerTwo.mでstr2を合成します。
@interface ViewControllerTwo ()
@end
@implementation ViewControllerTwo
@synthesize str2;
viewControlerOne.mでこれらを行います。
- (void)viewDidLoad
{
[super viewDidLoad];
// Data or string you wants to pass in ViewControllerTwo..
self.str1 = @"hello world";
}
ボタンクリックイベントでこれを行います..
-(IBAction)ButtonClicked
{ //Navigation on buttons click event from ViewControlerOne to ViewControlerTwo with transferring data or string..
ViewControllerTwo *objViewTwo=[self.storyboard instantiateViewControllerWithIdentifier:@"ViewControllerTwo"];
obj.str2=str1;
[self.navigationController pushViewController: objViewTwo animated:YES];
}
viewControllerTwo.mでこれらを行います。
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(@"%@",str2);
}
これを行うにはたくさんの方法があります。正しい方法を選ぶことが重要です。最も大きなアーキテクチャ上の決定の1つは、モデルコードがアプリ全体でどのように共有またはアクセスされるかにあります。
私はしばらく前にこのブログ記事を書きました: モデルコードの共有 。これが簡単な要約です。
1つの方法は、View Controller間でモデルオブジェクトへのポインタを共有することです。
Segueの準備が最も一般的なので、ここに例があります。
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
var next = segue.destinationViewController as NextViewController
next.dataSource = dataSource
}
もう1つの方法は、一度にデータでいっぱいになった画面を処理し、View Controllerを互いに結合するのではなく、各View Controllerを単一のデータソースに結合して独立して取得できるようにすることです。
これを私が見た最も一般的な方法は シングルトン インスタンスです。したがって、シングルトンオブジェクトがDataAccess
であれば、UIViewControllerのviewDidLoadメソッドで次の操作を実行できます。
func viewDidLoad() {
super.viewDidLoad()
var data = dataAccess.requestData()
}
データの受け渡しにも役立つ追加のツールがあります。
コアデータのいいところは、逆の関係にあるということです。それで、単にNotesViewControllerにnotesオブジェクトを渡したいのであれば、それはノートブックのような他のものと逆の関係になるでしょう。 NotesViewControllerでノートブックのデータが必要な場合は、次のようにしてオブジェクトグラフに戻ることができます。
let notebookName = note.notebook.name
私のブログ記事でこれについてもっと読んでください: モデルコードの共有
NewsViewController
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tbl_View deselectRowAtIndexPath:indexPath animated:YES];
News *newsObj = [newstitleArr objectAtIndex:indexPath.row];
NewsDetailViewController *newsDetailView = [[NewsDetailViewController alloc] initWithNibName:@"NewsDetailViewController" bundle:nil];
newsDetailView.newsHeadlineStr = newsObj.newsHeadline;
[self.navigationController pushViewController:newsDetailView animated:YES];
}
NewsDetailViewController.h
@interface NewsDetailViewController : UIViewController
@property(nonatomic,retain) NSString *newsHeadlineStr;
@end
NewsDetailViewController.m
@synthesize newsHeadlineStr;
アプリケーションデリゲートにデータを保存して、アプリケーションのView Controller間でアクセスできます。アプリデリゲートの共有インスタンスを作成するだけです。
AppDelegate *appDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
例えば
あなたがNSArray object *arrayXYZ
を宣言するならば、あなたはappDelegate.arrayXYZ
によってそれをどんなView Controllerででもアクセスすることができます
ユーザーが選択した内容をキャンセルできる場合は、NSProxyをベースにしたModelオブジェクトとMockオブジェクトを使用してデータをコミットまたは破棄するという考え方が好きです。
単一のオブジェクトまたは複数のオブジェクトであるためデータの受け渡しが簡単で、UINavigationControllerコントローラとした場合、モデルへの参照を内部に保持でき、プッシュされたすべてのView ControllerはNavigation Controllerから直接アクセスできます。
あるビューコントローラから別のビューコントローラにデータを送信したい場合は、次のようにします。
ViewControllerがあるとしましょう:ViewControllerとNewViewController。
viewController.hで
#import <UIKit/UIKit.h>
@interface ViewController : UIViewController
{
IBOutlet UITextField *mytext1,*mytext2,*mytext3,*mytext4;
}
@property (nonatomic,retain) IBOutlet UITextField *mytext1,*mytext2,*mytext3,*mytext4;
-(IBAction)goToNextScreen:(id)sender;
@end
viewController.m内
#import "ViewController.h"
#import "NewViewController.h"
@implementation ViewController
@synthesize mytext1,mytext2,mytext3,mytext4;
-(IBAction)goToNextScreen:(id)sender
{
NSArray *arr = [NSArray arrayWithObjects:mytext1.text,mytext2.text,mytext3.text,mytext4.text, nil];
NewViewController *newVc = [[NewViewController alloc] initWithNibName:@"NewViewController" bundle:nil];
newVc.arrayList = arr;
[self.navigationController pushViewController:newVc animated:YES];
}
NewViewController.h内
#import <UIKit/UIKit.h>
@interface NewViewController : UITableViewController
{
NSArray *arrayList;
NSString *name,*age,*dob,*mobile;
}
@property(nonatomic, retain)NSArray *arrayList;
@end
NewViewController.m内
#import "NewViewController.h"
#import "ViewController.h"
@implementation NewViewController
@synthesize arrayList;
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return [arrayList count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
// Configure the cell...
cell.textLabel.text = [arrayList objectAtIndex:indexPath.row];
return cell;
}
@end
このようにして、あるView Controllerから別のView Controllerにデータを渡すことができます。
私はdidSelectRowAtPath
メソッドを使ってこれを複雑にすることについて多くの人が見ています。私の例ではCore Dataを使用しています。
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
//this solution is for using Core Data
YourCDEntityName * value = (YourCDEntityName *)[[self fetchedResultsController] objectAtIndexPath: indexPath];
YourSecondViewController * details = [self.storyboard instantiateViewControllerWithIdentifier:@"nameOfYourSecondVC"];//make sure in storyboards you give your second VC an identifier
//Make sure you declare your value in the second view controller
details.selectedValue = value;
//Now that you have said to pass value all you need to do is change views
[self.navigationController pushViewController: details animated:YES];
}
メソッド内に4行のコードを入力すれば完了です。
実際にはうまくいくであろうView Controller通信を実行するためのさまざまな方法を提供しているこの質問に対する多くの回答がありますが、私はどこが実際に最も適しているか、そしてどれが避けるべきか言及しません。
UIViewController
のprepare(for:sender:)
メソッドをオーバーライドするこれらのソリューションは、短期的には機能しますが、依存関係が多すぎてアプリケーションのアーキテクチャを乱し、後で問題を引き起こす可能性があります。
興味のある人のために、私はこれらの点をより深く取り上げ、そしてさまざまな欠点を強調したいくつかの記事を書きました:
あるViewControllerから別のViewControllerにデータを渡すための3つのタイプがあります。
デモプロジェクトのリンクはこちら - https://github.com/kamanijasmin13/Swift-Pass-data-between-viewcontrollers
デモプロジェクトのリンクはこちら - https://github.com/kamanijasmin13/Swift-Pass-data-between-viewcontrollers
スイフト5
Well Matt Priceの回答 データの受け渡しにはまったく問題ありませんが、最新のSwiftバージョンで書き直すつもりです。元の記事はObjectiveにあるので、新しいプログラマは新しいSyntaxやメソッド/フレームワークのせいでやりがいがあると思います。 -C.
View Controller間でデータを渡す方法は複数あります。
私は最新のiOSフレームワークでSwiftで彼の論理を書き直すつもりです
ナビゲーションコントローラプッシュによるデータの受け渡し :ViewControllerAからViewControllerBへ
ステップ1 - ViewControllerBで変数を宣言する
var isSomethingEnabled = false
ステップ2 ViewControllerBのViewDidLoadメソッドで変数を印刷する
override func viewDidLoad() {
super.viewDidLoad()
//Print value received through segue, navigation Push
print("Value of 'isSomethingEnabled' from ViewControllerA : ", isSomethingEnabled)
}
ステップ3 ViewControllerAでNavigation Controllerを押しながらデータを渡す
if let viewControllerB = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "ViewControllerB") as? ViewControllerB {
viewControllerB.isSomethingEnabled = true
if let navigator = navigationController {
navigator.pushViewController(viewControllerB, animated: true)
}
}
だからここに完全なコードです:
ViewControllerA
import UIKit
class ViewControllerA: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
//MARK:Passing Data through Navigation PushViewController
@IBAction func goToViewControllerB(_ sender: Any) {
if let viewControllerB = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "ViewControllerB") as? ViewControllerB {
viewControllerB.isSomethingEnabled = true
if let navigator = navigationController {
navigator.pushViewController(viewControllerB, animated: true)
}
}
}
}
ViewControllerB
import UIKit
class ViewControllerB: UIViewController {
//MARK: - Variable for Passing Data through Navigation Push
var isSomethingEnabled = false
override func viewDidLoad() {
super.viewDidLoad()
//Print value received through navigation Push
print("Value of 'isSomethingEnabled' from ViewControllerA : ", isSomethingEnabled)
}
}
Segueを通してデータを渡す :ViewControllerAからViewControllerBへ
ステップ1 - ViewControllerAからViewControllerBへのSegueを作成し、ストーリーボードでIdentifier = showDetailSegueを以下のように指定します。
ステップ2 ViewControllerBで isSomethingEnabled という名前の実行可能ファイルを宣言し、その値を印刷します。
ステップ3 ViewControllerAでは、Segueを渡すときにisSomethingEnabledの値を渡します。
だからここに完全なコードです:
ViewControllerA
import UIKit
class ViewControllerA: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
//MARK: - - Passing Data through Segue - -
@IBAction func goToViewControllerBUsingSegue(_ sender: Any) {
performSegue(withIdentifier: "showDetailSegue", sender: nil)
}
//Segue Delegate Method
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "showDetailSegue") {
let controller = segue.destination as? ViewControllerB
controller?.isSomethingEnabled = true//passing data
}
}
}
ViewControllerB
import UIKit
class ViewControllerB: UIViewController {
var isSomethingEnabled = false
override func viewDidLoad() {
super.viewDidLoad()
//Print value received through segue
print("Value of 'isSomethingEnabled' from ViewControllerA : ", isSomethingEnabled)
}
}
デリゲートを介してデータを渡す :ViewControllerBからViewControllerAへ
手順1 - プロトコルの宣言ViewControllerBDelegateViewControllerBファイル内、ただしクラス外
protocol ViewControllerBDelegate: NSObjectProtocol {
// Classes that adopt this protocol MUST define
// this method -- and hopefully do something in
// that definition.
func addItemViewController(_ controller: ViewControllerB?, didFinishEnteringItem item: String?)
}
ステップ2 ViewControllerBでデリゲート変数インスタンスを宣言する
var delegate: ViewControllerBDelegate?
ステップ3 ViewControllerBのviewDidLoadメソッド内にデリゲート用のデータを送信する
delegate?.addItemViewController(self, didFinishEnteringItem: "Data for ViewControllerA")
ステップ4 ViewControllerAでViewControllerBDelegateを確認する
class ViewControllerA: UIViewController, ViewControllerBDelegate {
// to do
}
ステップ5. ViewControllerAにデリゲートを実装することを確認します
if let viewControllerB = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "ViewControllerB") as? ViewControllerB {
viewControllerB.delegate = self//confirming delegate
if let navigator = navigationController {
navigator.pushViewController(viewControllerB, animated: true)
}
}
ステップ6 ViewControllerAでデータを受け取るためのデリゲートメソッドを実装する
func addItemViewController(_ controller: ViewControllerB?, didFinishEnteringItem item: String?) {
print("Value from ViewControllerB's Delegate", item!)
}
だからここに完全なコードです:
ViewControllerA
import UIKit
class ViewControllerA: UIViewController, ViewControllerBDelegate {
override func viewDidLoad() {
super.viewDidLoad()
}
//Delegate method
func addItemViewController(_ controller: ViewControllerB?, didFinishEnteringItem item: String?) {
print("Value from ViewControllerB's Delegate", item!)
}
@IBAction func goToViewControllerForDelegate(_ sender: Any) {
if let viewControllerB = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "ViewControllerB") as? ViewControllerB {
viewControllerB.delegate = self
if let navigator = navigationController {
navigator.pushViewController(viewControllerB, animated: true)
}
}
}
}
ViewControllerB
import UIKit
//Protocol decleare
protocol ViewControllerBDelegate: NSObjectProtocol {
// Classes that adopt this protocol MUST define
// this method -- and hopefully do something in
// that definition.
func addItemViewController(_ controller: ViewControllerB?, didFinishEnteringItem item: String?)
}
class ViewControllerB: UIViewController {
var delegate: ViewControllerBDelegate?
override func viewDidLoad() {
super.viewDidLoad()
//MARK: - - - - Set Data for Passing Data through Delegate - - - - - -
delegate?.addItemViewController(self, didFinishEnteringItem: "Data for ViewControllerA")
}
}
通知オブザーバを介してデータを渡す :ViewControllerBからViewControllerAへ
ステップ1. ViewControllerBの通知オブザーバにデータを設定して投稿する
let objToBeSent = "Test Message from Notification"
NotificationCenter.default.post(name: Notification.Name("NotificationIdentifier"), object: objToBeSent)
ステップ2. ViewControllerAに通知オブザーバを追加する
NotificationCenter.default.addObserver(self, selector: #selector(self.methodOfReceivedNotification(notification:)), name: Notification.Name("NotificationIdentifier"), object: nil)
ステップ3. ViewControllerAで通知データ値を受け取る
@objc func methodOfReceivedNotification(notification: Notification) {
print("Value of notification : ", notification.object ?? "")
}
だからここに完全なコードです:
ViewControllerA
import UIKit
class ViewControllerA: UIViewController{
override func viewDidLoad() {
super.viewDidLoad()
// add observer in controller(s) where you want to receive data
NotificationCenter.default.addObserver(self, selector: #selector(self.methodOfReceivedNotification(notification:)), name: Notification.Name("NotificationIdentifier"), object: nil)
}
//MARK: Method for receiving Data through Post Notification
@objc func methodOfReceivedNotification(notification: Notification) {
print("Value of notification : ", notification.object ?? "")
}
}
ViewControllerB
import UIKit
class ViewControllerB: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
//MARK:Set data for Passing Data through Post Notification
let objToBeSent = "Test Message from Notification"
NotificationCenter.default.post(name: Notification.Name("NotificationIdentifier"), object: objToBeSent)
}
}
ブロックを介してデータを渡す :ViewControllerBからViewControllerAへ
ステップ1. ViewControllerBでブロックを宣言する
var authorizationCompletionBlock:((Bool) - >())? = {_ in}
ステップ2. ViewControllerBでデータをブロックに設定する
if authorizationCompletionBlock != nil
{
authorizationCompletionBlock!(true)
}
ステップ3. ViewControllerAでブロックデータを受信する
//Receiver Block
controller!.authorizationCompletionBlock = { isGranted in
print("Data received from Block is :", isGranted)
}
だからここに完全なコードです:
ViewControllerA
import UIKit
class ViewControllerA: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
//MARK:Method for receiving Data through Block
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "showDetailSegue") {
let controller = segue.destination as? ViewControllerB
controller?.isSomethingEnabled = true
//Receiver Block
controller!.authorizationCompletionBlock = { isGranted in
print("Data received from Block is :", isGranted)
}
}
}
}
ViewControllerB
import UIKit
class ViewControllerB: UIViewController {
//MARK:Variable for Passing Data through Block
var authorizationCompletionBlock:((Bool)->())? = {_ in}
override func viewDidLoad() {
super.viewDidLoad()
//MARK:Set data for Passing Data through Block
if authorizationCompletionBlock != nil
{
authorizationCompletionBlock!(true)
}
}
}
あなたは私のGitHubで完全なサンプルアプリケーションを見つけることができます あなたがこれに関して何か質問があれば私に知らせてください。
これは、 チュートリアル が欲しい人にとっては本当に素晴らしいです。これがサンプルコードです:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:@"myIdentifer]) {
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
myViewController *destViewController = segue.destinationViewController;
destViewController.name = [object objectAtIndex:indexPath.row];
}
}
通知センターを使用する
let imageDataDict:[String: UIImage] = ["image": image]
// post a notification
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "notificationName"), object: nil, userInfo: imageDataDict)
// `default` is now a property, not a method call
// Register to receive notification in your class
NotificationCenter.default.addObserver(self, selector: #selector(self.showSpinningWheel(_:)), name: NSNotification.Name(rawValue: "notificationName"), object: nil)
// handle notification
func showSpinningWheel(_ notification: NSNotification) {
print(notification.userInfo ?? "")
if let dict = notification.userInfo as NSDictionary? {
if let id = dict["image"] as? UIImage{
// do something with your image
}
}
}
let imageDataDict:[String: UIImage] = ["image": image]
// post a notification
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "notificationName"), object: nil, userInfo: imageDataDict)
// `default` is now a property, not a method call
// Register to receive notification in your class
NotificationCenter.default.addObserver(self, selector: #selector(self.showSpinningWheel(_:)), name: NSNotification.Name(rawValue: "notificationName"), object: nil)
// handle notification
@objc func showSpinningWheel(_ notification: NSNotification) {
print(notification.userInfo ?? "")
if let dict = notification.userInfo as NSDictionary? {
if let id = dict["image"] as? UIImage{
// do something with your image
}
}
}
あるVCから他にデータを送信するには、次の簡単な方法を使用します。
YourNextVC *nxtScr = (YourNextVC*)[self.storyboard instantiateViewControllerWithIdentifier:@"YourNextVC"];//Set this identifier from your storyboard
nxtScr.comingFrom = @"PreviousScreen"l
[self.navigationController nxtScr animated:YES];
それでも、デリゲートシステムやstoryboardSegueを使用して作業できる方法はほとんどありません。
1- As working with setter and getter method like in viewController.h
@property (retain, nonatomic) NSString *str;
now, in viewController.m
@synthesize str;
here i have pdf url and segue to another viewController like this and pdfObject is my pdfModel basicilly is NSOBJECT class.
str =[NSString stringWithFormat:@"%@",pdfObject.objPath];
NSLog(@"pdfUrl :***: %@ :***:",pdfUrl);
[self performSegueWithIdentifier:@"programPDFViewController_segue" sender:self];
// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([[segue identifier] isEqualToString:@"programPDFViewController_segue"]){
programPDFViewController *pdfVC = [segue destinationViewController];
[pdfVC setRecievedPdfUrl:str];
}
}
今正常に私は私のpdfのurl文字列と他のViewControllerを受け取り、webviewでその文字列を使います...
2-このようなデリゲートを扱うとき、私はdateFormatter、sharedInstance、EscapeWhiteSpaceCharacters、convertImageToGrayScaleなどの私のメソッドを含むユーティリティの1つのNSObjectクラスを持っています。
この場合は、utilities.h で作成した文字列変数を一度だけ使用して、データをあるビューコントローラから別のビューコントローラに解析するときに変数を作成する必要はありません。そしてまた使った
@interface Utilities : NSObject
Utilities.h
+(Utilities*)sharedInstance;
@property(nonatomic,retain)NSString* strUrl;
utilities.mに
@implementation utilities
+(utilities*)sharedInstance
{
static utilities* sharedObj = nil;
if (sharedObj == nil) {
sharedObj = [[utilities alloc] init];
}
return sharedObj;
}
now its done come to your firstViewController.m and call delegate
NSString*str =[NSString stringWithFormat:@"%@",pdfObject.objPath];
[Connection sharedInstance].strUrl=nil;
[Connection sharedInstance].strUrl=str;
Now go to you secondViewController.m directly use it without creating variable
in viewwillapear what i did
-(void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:YES];
[self webViewMethod:[Connection sharedInstance].strUrl];
}
-(void)WebViewMethod:(NSString)Url{
// working with webview enjoy coding :D
}
この委任作業はメモリ管理で信頼できます。
ブロック/クロージャとカスタムコンストラクタをお勧めします。
FirstViewControllerからSecondViewControllerに文字列を渡す必要があるとします。
あなたの最初のView Controller。
class FirstViewController : UIViewController {
func moveToViewControllerB() {
let second_screen = SecondViewController.screen(string: "DATA TO PASS", call_back: {
[weak self] (updated_data) in
///This closure will be called by second view controller when it updates something
})
self.navigationController?.pushViewController(second_screen, animated: true)
}
}
あなたのセカンドビューコントローラ
class SecondViewController : UIViewController {
var incoming_string : String?
var call_back : ((String) -> Void)?
class func screen(string: String?, call_back : ((String) -> Void)?) -> SecondViewController {
let me = SecondViewController(nibName: String(describing: self), bundle: Bundle.main);
me.incoming_string = string
me.call_back = call_back
return me
}
// Suppose its called when you have to update FirstViewController with new data.
func updatedSomething() {
//Executing block that is implemented/assigned by the FirstViewController.
self.call_back?("UPDATED DATA")
}
}
ソースビューコントローラから宛先ビューコントローラへのプッシュセグエを作成し、以下のような識別子名を付けることができます。
このようにdidselectRowAtから実行する必要があります。
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
performSegue(withIdentifier: "segue", sender: self)
}
そして下の関数から選択した項目の配列を渡すことができます。
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let index = CategorytableView.indexPathForSelectedRow
let indexNumber = index?.row
print(indexNumber!)
let VC = segue.destination as! AddTransactionVC
VC.val = CategoryData[indexNumber!] . //You can pass here entire array instead of array element.
}
そして、あなたは目的のviewcontrollerのviewdidloadの中の値をチェックし、それからデータベースにそれを保存する必要があります。
override func viewDidLoad{
if val != ""{
btnSelectCategory.setTitle(val, for: .normal)
}
}
私は代表者とセグエなしでそれを作ることを好みます。これは、カスタムinitを使って、またはオプションの値を設定することによって実行できます。
1.カスタムinit
class ViewControllerA: UIViewController {
func openViewControllerB() {
let viewController = ViewControllerB(string: "Blabla", completionClosure: { success in
print(success)
})
navigationController?.pushViewController(animated: true)
}
}
class ViewControllerB: UIViewController {
private let completionClosure: ((Bool) -> Void)
init(string: String, completionClosure: ((Bool) -> Void)) {
self.completionClosure = completionClosure
super.init(nibName: nil, bundle: nil)
title = string
}
func finishWork() {
completionClosure()
}
}
2.オプションの変数
class ViewControllerA: UIViewController {
func openViewControllerB() {
let viewController = ViewControllerB()
viewController.string = "Blabla"
viewController.completionClosure = { success in
print(success)
}
navigationController?.pushViewController(animated: true)
}
}
class ViewControllerB: UIViewController {
var string: String? {
didSet {
title = string
}
}
var completionClosure: ((Bool) -> Void)?
func finishWork() {
completionClosure?()
}
}
これを行うApple方法は、セグエを使用することです。 prepareForSegue()関数を使用する必要があります
素晴らしいチュートリアルがたくさんありますが、ここにその1つがあります。 https://www.iphonelife.com/content/unleash-your-inner-app-developer-part-21-passing-data-between-controllers
また、セグエの使用に関するAppleドキュメントを参照してください。 https://developer.Apple.com/library/ios/featuredarticles/ViewControllerPGforiPhoneOS/UsingSegues.html
もっと簡単な方法はこちらです。
グローバル変数を使うだけです。次のクラスに渡すために必要なオブジェクトまたは変数を宣言します。
たとえば、classA
とclassB
の2つのクラスがあります。
classA
には通常以下が含まれます。
#import "classA.h"
@interface classA()
@end
@implementation classA
-(void)viewDidLoad
{
...
}
-(void)didReceiveMemoryWarning
{
...
}
そしてclassB
は以下を含みます。
#import "classB.h"
@interface classB()
@end
@implementation classB
-(void)viewWillLoad
{
...
}
-(void)didReceiveMemoryWarning
{
...
}
今度は、2番目のクラスclassB
をclassA
にインポートします。
#import "classA.h"
#import "classB.h" //---import classB to classA.
@interface classA()
@end
@implementation classA
-(void)viewDidLoad
{
...
}
-(void)didReceiveMemoryWarning
{
...
}
これで、2番目のクラスclassB
に移動するためのブリッジができました。変数またはオブジェクトをグローバルとして宣言するために、次のように最初のクラスの.mファイルで宣言します。
classA.h
内
#import "classA.h"
#import "classB.h"
@interface classA()
@end
NSString *temp; //----declare any object/variable as global.
@implementation classA
-(void)viewDidLoad
{
...
temp=@"Hello";
...
}
-(void)didReceiveMemoryWarning
{
...
}
ここでオブジェクトtemp
は任意のクラスのグローバルオブジェクトまたはグローバル変数にアクセスするためのクラスNSString
のグローバルオブジェクトです。2番目のクラスのオブジェクトまたは変数を再宣言するだけです。例えば。下記のとおり:
classB.m
内
#import "classB.h"
@interface classB()
@end
extern NSString *temp; //----use `extern` keyword for using the global object/variable in classB that was declared in classA.
@implementation classB
-(void)viewDidLoad
{
...
LabeL.text=temp;
...
}
-(void)didReceiveMemoryWarning
{
...
}
これで、2番目のクラスから値にアクセスできるようになりました。単純な!..このメソッドは、任意の数のクラスに対して実行できます。
注意:
2番目のクラスの.hファイルを最初のクラスにインポートする必要があります。しかし、第1クラスの.hファイルを第2クラスにインポートする必要はありません。
橋があるならば、それは両側に行くことができるはずです。
これが役に立つと思います。私が同じ状況にあったとき、それは私を助けました。
IOS用のアプリケーションを作成するときは、常にMVCの概念に従う必要があります。 ViewControllerから別のViewControllerにデータを渡す場合が2つあります。
階層内に "A" ViewContollerがあり、あなたが next / viewcontrollerである "B"にデータを送りたいとき。この場合はSegueを使う必要があります。セグエの識別子を設定してから、「A」VCに次のコードを記述します。
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "A to B segue identifier" {
let bViewController = segue.destination as! UIDocumentBrowserViewController
bViewController.data = someData
}
}
モーダル(または埋め込み)としてA
を自分自身で開いたB
があるとき。これでB
ビューコントローラはその親について盲目になるはずです。そのため、データをA
に送り返す最良の方法はDelegation
を使用することです。 B
ビューコントローラとdelegate
プロパティにデリゲートプロトコルを作成します。そのためB
はデリゲートに報告(データを送り返す)します。 A
ビューコントローラでは、B
ビューコントローラのデリゲートプロトコルを実装し、prepare(forSegue:)
メソッドでself
をdelegate
ビューコントローラのB
プロパティとして設定します。
これはどのように正しく実装されるべきかです。それが役に立てば幸い