iOS
ビューコントローラ間でのデータの受け渡し
サーチ…
Seguesを使用する(データを転送する)
セグを使用して、現在のビューコントローラから次の新しいビューコントローラ(以前のビューコントローラではない)にデータを渡すには、まず関連するストーリーボードに識別子を持つセグを作成します。現在のView ControllerのprepareForSegue
メソッドをオーバーライドします。メソッド内で、その識別子によって作成したセグをチェックします。宛先ビューコントローラをキャストし、ダウンキャストビューコントローラのプロパティを設定して、データを渡します。
segueの識別子を設定する:
Seguesは、プログラマチックに、またはストーリーボードに設定されたボタンアクションイベントを使用して、Ctrl +ドラッグ先のビューコントローラにドラッグして実行できます。必要に応じて、ビューコントローラのセグの識別子を使用してプログラム的にセグを呼び出すことができます:
目標-C
- (void)showDetail {
[self performSegueWithIdentifier:@"showDetailingSegue" sender:self];
}
迅速
func showDetail() {
self.performSegue(withIdentifier: "showDetailingSegue", sender: self)
}
segueペイロードは、 prepareForSegue
メソッドのオーバーライドされたバージョンで構成できます。宛先ビューコントローラがロードされる前に、必要なプロパティを設定できます。
目標-C
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if([segue.identifier isEqualToString:@"showDetailingSegue"]){
DetailViewController *controller = (DetailViewController *)segue.destinationViewController;
controller.isDetailingEnabled = YES;
}
}
迅速
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showDetailingSegue" {
let controller = segue.destinationViewController as! DetailViewController
controller.isDetailingEnabled = true
}
}
DetailViewController
は2番目のView Controllerの名前で、 isDetailingEnabled
はそのView Controllerのパブリック変数です。
このパターンを拡張するために、 DetailViewController
パブリックメソッドを擬似イニシャライザとして扱い、必要な変数を初期化するのに役立てることができます。これは、 DetailViewController
に設定する必要がある変数を、ソースコードを読み込むことなく、自己文書化します。それはデフォルトを置くのにも便利な場所です。
目標-C
- (void)initVC:(BOOL *)isDetailingEnabled {
self.isDetailingEnabled = isDetailingEnabled
}
迅速
func initVC(isDetailingEnabled: Bool) {
self.isDetailingEnabled = isDetailingEnabled
}
デリゲートパターンを使用する(データを返す)
現在のビューコントローラから以前のビューコントローラにデータを戻すには、デリゲートパターンを使用します。
この例では、Interface BuilderでsegueをshowSecondViewController
、segue識別子をshowSecondViewController
設定していることを前提としています。アウトレットとアクションは、次のコードの名前にも接続する必要があります。
ファーストビューコントローラ
First View Controllerのコードは次のとおりです。
迅速
class FirstViewController: UIViewController, DataEnteredDelegate {
@IBOutlet weak var label: UILabel!
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showSecondViewController", let secondViewController = segue.destinationViewController as? SecondViewController {
secondViewController.delegate = self
}
}
// required method of our custom DataEnteredDelegate protocol
func userDidEnterInformation(info: String) {
label.text = info
navigationController?.popViewControllerAnimated(true)
}
}
目標-C
@interface FirstViewController : UIViewController <DataEnteredDelegate>
@property (weak, nonatomic) IBOutlet UILabel *label;
@end
@implementation FirstViewController
- (void)viewDidLoad {
[super viewDidLoad];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
SecondViewController *secondViewController = segue.destinationViewController;
secondViewController.delegate = self;
}
-(void)userDidEnterInformation:(NSString *)info {
_label.text = info
[self.navigationController popViewControllerAnimated:YES];
}
@end
私たちのカスタムDataEnteredDelegate
プロトコルの使用に注意してください。
2番目のビューコントローラとプロトコル
2番目のView Controllerのコードは次のとおりです。
迅速
// 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?
@IBOutlet weak var textField: UITextField!
@IBAction func sendTextBackButton(sender: AnyObject) {
// call this method on whichever class implements our delegate protocol (the first view controller)
delegate?.userDidEnterInformation(textField.text ?? "")
}
}
目標-C
@protocol DataEnteredDelegate <NSObject>
-(void)userDidEnterInformation:(NSString *)info;
@end
@interface SecondViewController : UIViewController
@property (nonatomic) id <DataEnteredDelegate> delegate;
@property (weak, nonatomic) IBOutlet UITextField *textField;
@end
@implementation SecondViewController
- (void)viewDidLoad {
[super viewDidLoad];
}
- (IBAction) sendTextBackButton:(id)sender{
[_delegate userDidEnterInformation:textField.text];
}
@end
protocol
はView Controllerクラスの外にあることに注意してください。
セグをするために巻き戻しを使用してデータを後方に渡す
現在のビューコントローラからデスティネーションビューコントローラにデータを "転送"できるようにするsegueとは対照的に、
(VC1) → (VC2)
"巻き戻し"を使用すると、反対のことができます。送り先または現在のビューコントローラーからそのプレゼンテーションビューコントローラーにデータを渡します。
(VC1) < - (VC2)
注 :アンワインドを使用すると、データを最初に渡し、その後に現在のビューコントローラ(VC2)が割り当て解除されることに注意してください。
それを行う方法は次のとおりです:
まず、データを渡すビューコントローラであるプレゼンテーションビューコントローラ(VC1)に次の宣言を追加する必要があります。
@IBAction func unwindToPresentingViewController(segue:UIStoryboardSegue)
重要なのは、接頭辞unwind
を使うことです。これは、Xcodeに " unwind
"メソッドであることを知らせて、ストーリーボードでもそのオプションを使用できるようにします。
その後、メソッドを実装する必要があります。実際のセグエーとほとんど同じです。
@IBAction func unwindToPresentingViewController(segue:UIStoryboardSegue)
{
if segue.identifier == "YourCustomIdentifer"
{
if let VC2 = segue.sourceViewController as? VC2
{
// Your custom code in here to access VC2 class member
}
今度は、巻き戻し呼び出しを呼び出す2つのオプションがあります。
- :あなたは、「ハードコード」を呼び出すことができます
self.performSegueWithIdentifier("YourCustomIdentifier", sender: self)
いつでも必要になりますあなたのためのアンワインドを行いますperformSegueWithIdentifier
。 -
storyboard
を使用してアンワインドメソッドを「終了」オブジェクトにリンクすることができます。巻き戻しメソッドを呼び出すボタンをctrl +ドラッグして「終了」オブジェクトにドラッグします。
あなたは、あなたのカスタム巻き戻し方法を選択するオプションを持っています:
クロージャを使用してデータを渡す(データを返す)
UIViewController
クラスのさまざまな部分で実装を分割するデリゲートパターンを使用する代わりに、 closures
を使用してデータを前後に渡すことさえできます。あなたがUIStoryboardSegue
を使用していると仮定することによって、 prepareForSegue
メソッドでは、新しいコントローラを簡単にワンステップでセットアップできます
final class DestinationViewController: UIViewController {
var onCompletion: ((success: Bool) -> ())?
@IBAction func someButtonTapped(sender: AnyObject?) {
onCompletion?(success: true)
}
}
final class MyViewController: UIViewController {
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
guard let destinationController = segue.destinationViewController as? DestinationViewController else { return }
destinationController.onCompletion = { success in
// this will be executed when `someButtonTapped(_:)` will be called
print(success)
}
}
}
これは使用の例であり、SwiftのObjective-Cブロックの構文は、コードを読みやすくするのはそれほど簡単ではありません
データを渡すコールバッククロージャ(ブロック)の使用
このトピックはiOS開発の古典的な問題であり、その解決策はすでに示した他の例のようにさまざまです。この例では、別の日常的な使用方法をもう1つ表示します:このページのdelegate pattern
例をコールバックclosure
適用してclosure
を使用してデータを渡す方法
このメソッドは、 delegate pattern
よりも優れています。これは、2つの異なる場所(このページのデリゲートの例、 prepareForSegue
、 userDidEnterInformation
)で設定コードを分割するのではなく、( prepareForSegue
でのみ表示します)
2番目のビューコントローラから開始
コールバックの使い方を理解してから、それを書くことができます。これは、コールバックを使用する場所であるため、2番目のビューコントローラから始める理由です:新しいテキスト入力を取得したとき、 コールバックのパラメータをメディア最初のViewControllerにデータを渡すには、コールバックのパラメータを使用していることに気付きました。これは非常に重要です。初心者(私がそうだったように)は常にこれを見落とし、コールバッククロージャを正しく書くために
この場合、コールバックは1つのパラメータしか取らないことがわかります。テキストとその型はString
。最初のビューコントローラからデータを取り込む必要があるため、それを宣言してプロパティにしましょう
私はちょうどすべてのdelegate
部分をコメントし、比較のためにそれを保つ
class SecondViewController: UIViewController {
//weak var delegate: DataEnteredDelegate? = nil
var callback: ((String?)->())?
@IBOutlet weak var textField: UITextField!
@IBAction func sendTextBackButton(sender: AnyObject) {
//delegate?.userDidEnterInformation(textField.text!)
callback?(input.text)
self.navigationController?.popViewControllerAnimated(true)
}
}
最初のビューコントローラを終了する
あなたがしなければならないことは、コールバッククロージャを渡すことだけです。完了しました。クロージャは、すでに2番目のビューコントローラ
delegate pattern
と比較してコードをどのように短くするか見てみましょう
//no more DataEnteredDelegate
class FirstViewController: UIViewController {
@IBOutlet weak var label: UILabel!
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showSecondViewController" {
let secondViewController = segue.destinationViewController as! SecondViewController
//secondViewController.delegate = self
secondViewController.callback = { text in self.label.text = text }
}
}
// required method of our custom DataEnteredDelegate protocol
//func userDidEnterInformation(info: String) {
// label.text = info
//}
}
最後に、あなたの誰かが、最初のビューコントローラーから2番目のビューコントローラーから2番目のビューコントローラーから直接戻ってくることなく、1つの方法でのみデータ(この場合はクロージャー)を渡すだけであることを見て混乱します。コミュニケーションツールと考えていますか?多分あなたは本当にそれを実行し、あなた自身でそれを証明すべきです、私はそれがパラメータだと言うでしょう、それはデータを戻すコールバッククロージャのパラメータです!
プロパティ(データの受け渡し)を割り当てることにより、
次のView Controllerのプロパティをプッシュまたはプレゼンテーションする前に割り当てることによって、データを直接渡すことができます。
class FirstViewController: UIViewController {
func openSecondViewController() {
// Here we initialize SecondViewController and set the id property to 492
let secondViewController = SecondViewController()
secondViewController.id = 492
// Once it was assign we now push or present the view controller
present(secondViewController, animated: true, completion: nil)
}
}
class SecondViewController: UIViewController {
var id: Int?
override func viewDidLoad() {
super.viewDidLoad()
// Here we unwrapped the id and will get the data from the previous view controller.
if let id = id {
print("Id was set: \(id)")
}
}
}