การส่งผ่านข้อมูลระหว่างตัวควบคุมมุมมอง


1372

ฉันยังใหม่กับ iOS และ Objective-C และกระบวนทัศน์ MVC ทั้งหมดและฉันติดอยู่กับสิ่งต่อไปนี้:

ฉันมีมุมมองที่ทำหน้าที่เป็นแบบฟอร์มการป้อนข้อมูลและฉันต้องการให้ตัวเลือกแก่ผู้ใช้ในการเลือกผลิตภัณฑ์หลายรายการ ผลิตภัณฑ์มีการระบุไว้ในมุมมองอื่นด้วยUITableViewControllerและฉันได้เปิดใช้งานการเลือกหลายรายการ

คำถามของฉันคือฉันจะถ่ายโอนข้อมูลจากมุมมองหนึ่งไปยังมุมมองอื่นได้อย่างไร ฉันจะเลือกสิ่งที่อยู่UITableViewในอาร์เรย์ แต่ฉันจะส่งกลับไปยังมุมมองฟอร์มการป้อนข้อมูลก่อนหน้าได้อย่างไรเพื่อให้สามารถบันทึกพร้อมกับข้อมูลอื่น ๆ ไปยัง Core Data เมื่อส่งแบบฟอร์ม

ฉันได้ท่องไปรอบ ๆ และเห็นบางคนประกาศอาร์เรย์ในตัวแทนแอพ ฉันอ่านบางอย่างเกี่ยวกับ Singletons แต่ไม่เข้าใจว่าสิ่งเหล่านี้คืออะไรและฉันอ่านบางอย่างเกี่ยวกับการสร้างแบบจำลองข้อมูล

อะไรจะเป็นวิธีที่ถูกต้องในการทำสิ่งนี้และฉันจะทำอย่างไร

คำตอบ:


1683

คำถามนี้ดูเหมือนว่าจะเป็นที่นิยมมากที่นี่ใน stackoverflow ดังนั้นฉันคิดว่าฉันจะลองและให้คำตอบที่ดีกว่าเพื่อช่วยให้ผู้คนที่เริ่มต้นในโลกของ iOS เช่นฉัน

ฉันหวังว่าคำตอบนี้ชัดเจนเพียงพอสำหรับคนที่จะเข้าใจและฉันไม่ได้พลาดอะไรเลย

ส่งต่อข้อมูล

การส่งต่อข้อมูลไปยังตัวควบคุมมุมมองจากตัวควบคุมมุมมองอื่น คุณจะใช้วิธีนี้หากคุณต้องการส่งวัตถุ / ค่าจากคอนโทรลเลอร์มุมมองหนึ่งไปยังคอนโทรลเลอร์มุมมองอื่นที่คุณอาจใช้ในการนำทางสแต็ก

สำหรับตัวอย่างนี้เราจะได้ViewControllerAและViewControllerB

ในการส่งBOOLค่าจากViewControllerAไปยังViewControllerBเราจะทำสิ่งต่อไปนี้

  1. ในการViewControllerB.hสร้างคุณสมบัติสำหรับBOOL

    @property (nonatomic, assign) BOOL isSomethingEnabled;
  2. ในViewControllerAคุณต้องการที่จะบอกได้ว่ามันเกี่ยวกับViewControllerBเพื่อใช้

    #import "ViewControllerB.h"

    จากนั้นที่ที่คุณต้องการโหลดมุมมองเช่น didSelectRowAtIndexหรือบางอย่างที่IBActionคุณจำเป็นต้องตั้งค่าคุณสมบัติViewControllerBก่อนที่คุณจะผลักดันมันลงในกอง nav

    ViewControllerB *viewControllerB = [[ViewControllerB alloc] initWithNib:@"ViewControllerB" bundle:nil];
    viewControllerB.isSomethingEnabled = YES;
    [self pushViewController:viewControllerB animated:YES];
    

    นี้จะตั้งisSomethingEnabledอยู่ในViewControllerBที่จะคุ้มค่าBOOLYES

การส่งต่อข้อมูลโดยใช้ Segues

หากคุณใช้สตอรีบอร์ดคุณมักจะใช้งานเซกเมนต์และต้องการโพรซีเดอร์นี้เพื่อส่งต่อข้อมูล สิ่งนี้คล้ายกับด้านบน แต่แทนที่จะส่งผ่านข้อมูลก่อนที่คุณจะกดตัวควบคุมมุมมองคุณใช้วิธีการที่เรียกว่า

-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender

ดังนั้นการส่งผ่านBOOLจากViewControllerAไปยังViewControllerBเราจะทำดังต่อไปนี้:

  1. ในการViewControllerB.hสร้างคุณสมบัติสำหรับBOOL

    @property (nonatomic, assign) BOOL isSomethingEnabled;
  2. ในViewControllerAคุณต้องการที่จะบอกได้ว่ามันเกี่ยวกับViewControllerBเพื่อใช้

    #import "ViewControllerB.h"
  3. สร้างส่วนต่อจากViewControllerAไปยังViewControllerBบนกระดานเรื่องราวและให้ตัวระบุในตัวอย่างนี้เราจะเรียกมันว่า"showDetailSegue"

  4. ต่อไปเราต้องเพิ่มวิธีการ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ที่จะคุ้มค่าBOOLYES

ผ่านข้อมูลกลับ

ในการส่งข้อมูลกลับจากViewControllerBไปยังViewControllerAคุณจำเป็นต้องใช้โปรโตคอลและผู้ได้รับมอบหมายหรือบล็อกหลังสามารถใช้เป็นกลไกคู่ที่หลวมสำหรับการโทรกลับ

การทำเช่นนี้เราจะทำให้ผู้ร่วมประชุมของViewControllerA ViewControllerBสิ่งนี้อนุญาตให้ViewControllerBส่งข้อความกลับเพื่อViewControllerAให้เราสามารถส่งข้อมูลกลับ

สำหรับการViewControllerAที่จะเป็นตัวแทนของViewControllerBมันต้องเป็นไปตามViewControllerBโปรโตคอล 's ซึ่งเราต้องระบุ นี่บอกViewControllerAวิธีที่มันต้องใช้

  1. ในViewControllerB.hด้านล่าง#importแต่เหนือ@interfaceคุณระบุโปรโตคอล

    @class ViewControllerB;
    
    @protocol ViewControllerBDelegate <NSObject>
    - (void)addItemViewController:(ViewControllerB *)controller didFinishEnteringItem:(NSString *)item;
    @end
    
  2. ยังคงอยู่ในViewControllerB.hคุณจะต้องตั้งค่าdelegateคุณสมบัติและสังเคราะห์มาViewControllerB.m

    @property (nonatomic, weak) id <ViewControllerBDelegate> delegate;
  3. ในที่ViewControllerBเราเรียกข้อความในdelegateเมื่อเราปรากฏตัวควบคุมมุมมอง

    NSString *itemToPassBack = @"Pass this value back to ViewControllerA";
    [self.delegate addItemViewController:self didFinishEnteringItem:itemToPassBack];
    
  4. ViewControllerBนั่นมันสำหรับ ตอนนี้ในViewControllerA.hบอกViewControllerAให้นำเข้าViewControllerBและสอดคล้องกับโปรโตคอลของมัน

    #import "ViewControllerB.h"
    
    @interface ViewControllerA : UIViewController <ViewControllerBDelegate>
    
  5. ในViewControllerA.mการใช้วิธีการต่อไปนี้จากโปรโตคอลของเรา

    - (void)addItemViewController:(ViewControllerB *)controller didFinishEnteringItem:(NSString *)item
    {
        NSLog(@"This was returned from ViewControllerB %@",item);
    }
    
  6. ก่อนที่จะผลักดันviewControllerBไปที่กองนำทางเราต้องบอก ViewControllerBว่าViewControllerAเป็นตัวแทนของมิฉะนั้นเราจะได้รับข้อผิดพลาด

    ViewControllerB *viewControllerB = [[ViewControllerB alloc] initWithNib:@"ViewControllerB" bundle:nil];
    viewControllerB.delegate = self
    [[self navigationController] pushViewController:viewControllerB animated:YES];
    

อ้างอิง

  1. การใช้การมอบหมายเพื่อสื่อสารกับตัวควบคุมมุมมองอื่น ๆในคู่มือการเขียนโปรแกรมดูคอนโทรลเลอร์
  2. รูปแบบของผู้ได้รับมอบหมาย

ศูนย์การแจ้งเตือนของ NSN เป็นอีกวิธีหนึ่งในการส่งผ่านข้อมูล

// 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 หรือคลาสอื่น ๆ )

บล็อกเป็นฟังก์ชั่นที่ไม่ระบุชื่อ

ตัวอย่างนี้ส่งผ่านข้อมูลจากคอนโทรลเลอร์ Bไปยังคอนโทรลเลอร์ A

กำหนดบล็อก

@property void(^selectedVoucherBlock)(NSString *); // in ContollerA.h

เพิ่มตัวจัดการบล็อก (ฟัง) ที่คุณต้องการค่า (ตัวอย่างเช่นคุณต้องการการตอบสนอง API ของคุณใน ControllerA หรือคุณต้องการข้อมูล ContorllerB บน A)

// 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];
}

อีกตัวอย่างการทำงานสำหรับบล็อก


24
เราจำเป็นต้องใส่@class ViewControllerB;นิยาม @protocol ด้านบนด้วยหรือไม่ โดยไม่ได้ฉันได้รับข้อผิดพลาด "ประเภทที่คาดหวัง" ใน ViewControllerB ในบรรทัด: - (void)addItemViewController:(ViewControllerB *)controller didFinishEnteringItem:(NSString *)item; ภายใน@protocolประกาศ
alan-p

4
มันใช้งานได้ดี ตามที่ alan-p บอกไว้อย่าลืมที่จะเขียน @class ViewControllerB; เหนือโปรโตคอลมิฉะนั้นคุณจะได้รับข้อผิดพลาด "คาดว่าจะเป็นประเภท"
Andrew Davis

6
คุณไม่ต้องการผู้ได้รับมอบหมายสำหรับการส่งกลับเพียงแค่ใช้การผ่อนคลาย
malhal

4
เมื่อฉันใส่ "viewControllerB.delegate = self;" ใน ViewControllerB ฉันได้รับข้อผิดพลาด การกำหนดให้ 'id <ViewControllerBDel> จากประเภทที่เข้ากันไม่ได้' ViewControllerB * const __strong 'ฉันไม่แน่ใจว่าฉันทำอะไรผิด ใครช่วยได้บ้าง นอกจากนี้ฉันต้องเปลี่ยน: initWithNib -> initWithNibName
uplearnedu.com

4
ถ้าคุณกำลังใช้NavigationControllerคุณต้องใช้ [self.navigationController pushViewController:viewController animated:YES];แทน[self pushViewController:viewControllerB animated:YES];
Nazir

192

รวดเร็ว

มีคำอธิบายมากมายที่นี่และรอบ ๆ StackOverflow แต่ถ้าคุณเป็นมือใหม่ที่พยายามจะทำงานขั้นพื้นฐานให้ลองดูบทแนะนำของ YouTube นี้

ส่งผ่านข้อมูลไปยังมุมมองถัดไปของตัวควบคุม

ต่อไปนี้เป็นตัวอย่างที่ใช้วิดีโอ แนวคิดคือการส่งสตริงจากฟิลด์ข้อความในตัวควบคุมมุมมองที่หนึ่งไปยังป้ายชื่อในตัวควบคุมมุมมองที่สอง

ป้อนคำอธิบายรูปภาพที่นี่

สร้างเค้าโครงกระดานเรื่องราวในเครื่องมือสร้างส่วนติดต่อ ในการสร้างเซกคิวคุณเพียงControlคลิกที่ปุ่มและลากไปยังตัวควบคุมมุมมองที่สอง

ตัวควบคุมมุมมองแรก

รหัสสำหรับตัวควบคุมมุมมองแรกคือ

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!
    }

}

ตัวควบคุมมุมมองที่สอง

และรหัสสำหรับตัวควบคุมมุมมองที่สองคือ

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
    }

}

อย่าลืม

  • ร้านเบ็ดขึ้นสำหรับและUITextFieldUILabel
  • ตั้งค่าตัวควบคุมมุมมองที่หนึ่งและที่สองเป็นไฟล์ Swift ที่เหมาะสมใน IB

การส่งข้อมูลกลับไปยัง View Controller ก่อนหน้า

เพื่อส่งผ่านข้อมูลกลับมาจากการควบคุมมุมมองที่สองไปยังตัวควบคุมมุมมองแรกคุณจะใช้โปรโตคอลและตัวแทน วิดีโอนี้เป็นกระบวนการที่ชัดเจนมากแม้ว่า:

ต่อไปนี้เป็นตัวอย่างที่ใช้วิดีโอ (โดยมีการดัดแปลงเล็กน้อย)

ป้อนคำอธิบายรูปภาพที่นี่

สร้างเค้าโครงกระดานเรื่องราวในเครื่องมือสร้างส่วนติดต่อ อีกครั้งในการทำต่อคุณเพียงControlลากจากปุ่มไปยังตัวควบคุมมุมมองที่สอง showSecondViewControllerตั้งค่าตัวระบุทำต่อไป นอกจากนี้อย่าลืมติดต่อร้านค้าและการกระทำโดยใช้ชื่อในรหัสต่อไปนี้

ตัวควบคุมมุมมองแรก

รหัสสำหรับตัวควบคุมมุมมองแรกคือ

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โปรโตคอลที่กำหนดเองของเรา

ตัวควบคุมมุมมองและโปรโตคอลที่สอง

รหัสสำหรับตัวควบคุมมุมมองที่สองคือ

import UIKit

// protocol used for sending data back
protocol DataEnteredDelegate: AnyObject {
    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

แค่นั้นแหละ. เรียกใช้แอพตอนนี้คุณควรจะสามารถส่งข้อมูลกลับจากคอนโทรลเลอร์มุมมองที่สองไปยังแอพแรกได้


ได้รับการอัปเดต Swift ล่าสุดแล้วนี่เป็นรูปแบบทั่วไปที่จะนำไปใช้หรือไม่
piofusco

4
การอัปเดต Swift ส่วนใหญ่ทั้งหมดที่ฉันเคยเห็นมีการเปลี่ยนแปลงทางไวยากรณ์เล็กน้อยค่อนข้างไม่เปลี่ยนแปลงว่าข้อมูลถูกส่งผ่านระหว่างตัวควบคุมมุมมองอย่างไร หากฉันเรียนรู้การเปลี่ยนแปลงที่สำคัญเช่นนั้นฉันจะอัปเดตคำตอบของฉัน
Suragch

2
offtopic - iOS มีวิธีที่น่าเกลียดในการส่งพารามิเตอร์ไปยังตัวควบคุมมุมมองใหม่ไม่น่าเชื่อคุณต้องตั้งค่าพารามิเตอร์ไม่ได้อยู่ในสถานที่เมื่อคุณทำการโทร แต่ในอีกอันหนึ่ง Android มีแนวทางที่ดีกว่าในเรื่องนี้ - เมื่อคุณเริ่มต้นกิจกรรมคุณสามารถส่งผ่านข้อมูลใด ๆ (เกือบจะ) ผ่านการเริ่มต้นเจตนา ง่าย. ไม่จำเป็นต้องหล่อหรืออะไรเลย การส่งค่าส่งคืนกลับไปยังผู้โทรเป็นสิ่งที่สำคัญเช่นกันไม่จำเป็นต้องมอบหมาย แน่นอนว่ามันเป็นไปได้ที่จะใช้วิธีการที่น่าเกลียดด้วยเช่นกันไม่มีปัญหา))
Mixaz

1
@ Himanshu ก่อนได้รับการอ้างอิงถึงตัวควบคุมมุมมองที่สอง จากนั้นอัพเดตตัวแปรสาธารณะที่มีอยู่
Suragch

8
@น้ำผึ้ง. ฉันคิดว่าคำว่า "ผู้แทน" สับสน ให้ฉันใช้คำว่า "คนงาน" "ผู้ปฏิบัติงาน" (ตัวควบคุมมุมมองแรก) ทำทุกอย่างที่ "หัวหน้า" (ตัวควบคุมมุมมองที่สอง) บอกให้ทำ "เจ้านาย" ไม่รู้ว่าใครเป็น "คนงาน" จะเป็นใคร มันอาจเป็นใครก็ได้ ดังนั้นในตัวควบคุมมุมมองแรก (คลาส "ผู้ปฏิบัติงาน") มันบอกว่าฉันจะเป็น "คนงาน" ของคุณ คุณบอกฉันว่าจะเขียนอะไรในฉลากและฉันจะทำเพื่อคุณ ดังนั้นsecondViewController.delegate = selfหมายความว่า "ฉันตกลงที่จะเป็นพนักงานของเจ้านาย" ดูคำตอบนี้สำหรับตัวอย่างอื่นและคำอธิบายเพิ่มเติม
Suragch

136

M ใน MVC สำหรับ "Model" และในกระบวนทัศน์ MVC บทบาทของคลาสโมเดลคือการจัดการข้อมูลของโปรแกรม โมเดลตรงข้ามกับมุมมอง - วิวรู้วิธีแสดงข้อมูล แต่ไม่รู้อะไรเกี่ยวกับข้อมูลในขณะที่โมเดลรู้ทุกอย่างเกี่ยวกับวิธีทำงานกับข้อมูล แต่ไม่มีอะไรเกี่ยวกับวิธีแสดงมัน โมเดลอาจมีความซับซ้อน แต่ไม่จำเป็นต้องเป็น - โมเดลสำหรับแอปของคุณอาจเรียบง่ายเหมือนอาร์เรย์ของสตริงหรือพจนานุกรม

บทบาทของคอนโทรลเลอร์คือการเป็นสื่อกลางระหว่างมุมมองและโมเดล ดังนั้นพวกเขาต้องการการอ้างอิงถึงหนึ่งหรือมากกว่าหนึ่งวัตถุมุมมองและหนึ่งหรือมากกว่าหนึ่งรูปแบบวัตถุ สมมติว่าแบบจำลองของคุณเป็นอาร์เรย์ของพจนานุกรมโดยแต่ละพจนานุกรมแสดงถึงหนึ่งแถวในตารางของคุณ มุมมองรูทสำหรับแอปของคุณแสดงตารางนั้นและมันอาจจะรับผิดชอบในการโหลดอาร์เรย์จากไฟล์ เมื่อผู้ใช้ตัดสินใจที่จะเพิ่มแถวใหม่ในตารางพวกเขาแตะปุ่มบางปุ่มและตัวควบคุมของคุณสร้างพจนานุกรมใหม่ (ไม่แน่นอน) และเพิ่มลงในอาร์เรย์ เพื่อที่จะกรอกข้อมูลลงในแถวควบคุมจะสร้างตัวควบคุมมุมมองรายละเอียดและให้พจนานุกรมใหม่ ตัวควบคุมมุมมองรายละเอียดเติมในพจนานุกรมและส่งคืน พจนานุกรมเป็นส่วนหนึ่งของโมเดลอยู่แล้วดังนั้นจึงไม่มีอะไรเกิดขึ้น


95

มีหลายวิธีที่สามารถรับข้อมูลไปยังคลาสอื่นใน iOS ตัวอย่างเช่น -

  1. การกำหนดค่าเริ่มต้นโดยตรงหลังจากการจัดสรรคลาสอื่น
  2. การมอบหมาย - สำหรับการส่งข้อมูลกลับ
  3. การแจ้งเตือน - สำหรับการกระจายข้อมูลไปยังหลาย ๆ คลาสพร้อมกัน
  4. กำลังบันทึกในNSUserDefaults- สำหรับการเข้าถึงในภายหลัง
  5. ชั้นเรียนเดี่ยว
  6. ฐานข้อมูลและกลไกการจัดเก็บอื่น ๆ เช่น plist เป็นต้น

แต่สำหรับสถานการณ์ง่าย ๆ ในการส่งผ่านค่าไปยังคลาสอื่นซึ่งการจัดสรรเสร็จสิ้นในคลาสปัจจุบันวิธีที่นิยมที่สุดและเป็นที่ต้องการคือการตั้งค่าโดยตรงหลังจากการจัดสรร สามารถทำได้ดังนี้: -

เราสามารถเข้าใจได้โดยใช้คอนโทรลเลอร์สองตัวคือController1 และ Controller2

สมมติว่าใน Controller1 คลาสที่คุณต้องการสร้างวัตถุ Controller2 และผลักดันมันด้วยค่าสตริงที่ถูกส่งผ่าน สิ่งนี้สามารถทำได้ดังนี้: -

- (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.

หวังว่านี่จะช่วยได้


84

หลังจากการวิจัยเพิ่มเติมดูเหมือนว่าโปรโตคอลและผู้ได้รับมอบหมายเป็นวิธีการที่ถูกต้อง / Apple ต้องการทำเช่นนี้

ฉันลงเอยด้วยการใช้ตัวอย่างนี้

การแชร์ข้อมูลระหว่างตัวควบคุมมุมมองและวัตถุอื่น ๆ @ iPhone Dev SDK

ทำงานได้ดีและอนุญาตให้ฉันส่งสตริงและอาร์เรย์ไปข้างหน้าและย้อนกลับระหว่างมุมมองของฉัน

ขอบคุณสำหรับความช่วยเหลือของคุณ


3
อย่าใช้โปรโตคอลและผู้ได้รับมอบหมายเพียงแค่ใช้การผ่อนคลาย
malhal

1
@malhal ถ้าคุณไม่ใช้สตอรี่บอร์ดล่ะ
Evan R

ฉันเกลียดโปรโตคอลและผู้แทนที่ไร้ประโยชน์เช่นกัน @malhal
DawnSong

@EvanR คุณสามารถสร้างและดำเนินการแยกย่อยในรหัส มันเหมือนกันหมด
DawnSong

1
โดยพื้นฐานแล้วการควบคุมคุณภาพทั้งหมดในหน้านี้คือ "จากวันก่อนที่จะดูคอนเทนเนอร์" คุณจะไม่ต้องกังวลกับโปรโตคอลหรือผู้ได้รับมอบหมายเป็นล้าน ๆ ปีเลย ทุกสิ่งเล็ก ๆ น้อย ๆ ที่คุณทำบนหน้าจอใด ๆ คือมุมมองคอนเทนเนอร์ดังนั้นคำถามจะไม่มีอยู่อีกต่อไป - คุณมีการอ้างอิงทั้งหมด "ขึ้นและลง" จากมุมมองคอนเทนเนอร์ทั้งหมดแล้ว
Fattie

66

ฉันพบรุ่นที่ง่ายที่สุดและสง่างามที่สุดด้วยบล็อกการผ่าน มาควบคุมชื่อ view view ที่รอข้อมูลที่ส่งคืนเป็น "A" และ return view controller เป็น "B" ในตัวอย่างนี้เราต้องการได้รับ 2 ค่า: แรกของ Type1 และที่สองของ Type2

สมมติว่าเราใช้สตอรีบอร์ดตัวควบคุมแรกจะตั้งค่าบล็อกการโทรกลับเช่นระหว่างการเตรียมเซกเมนต์:

- (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);

สิ่งหนึ่งที่ต้องจำคือการใช้บล็อกมักจะต้องจัดการที่แข็งแกร่งและการอ้างอิงที่อ่อนแอเช่นที่อธิบายไว้ที่นี่


ทำไมค่าไม่เป็นพารามิเตอร์สำหรับบล็อกการโทรกลับแทนที่จะเป็นคุณสมบัติแยกต่างหาก
Timuçin

56

มีข้อมูลที่ดีในหลายคำตอบที่ให้ แต่ก็ไม่มีใครตอบคำถามได้อย่างเต็มที่

คำถามถามเกี่ยวกับการส่งผ่านข้อมูลระหว่างตัวควบคุมมุมมอง ตัวอย่างเฉพาะที่ให้ถามเกี่ยวกับการส่งข้อมูลระหว่างมุมมอง แต่ด้วยความแปลกใหม่ที่ระบุด้วยตนเองกับ iOS โปสเตอร์ต้นฉบับอาจหมายถึงระหว่าง viewControllers ไม่ใช่ระหว่างมุมมอง (โดยไม่มีส่วนเกี่ยวข้องใด ๆ จาก ViewControllers) ดูเหมือนว่าคำตอบทั้งหมดมุ่งเน้นไปที่ตัวควบคุมมุมมองสองตัว แต่ถ้าแอปวิวัฒนาการต้องมีตัวควบคุมมุมมองมากกว่าสองตัวในการแลกเปลี่ยนข้อมูล

โปสเตอร์เดิมยังถามเกี่ยวกับSingletonsและการใช้งานของAppDelegate คำถามเหล่านี้ต้องตอบ

เพื่อช่วยให้คนอื่นดูคำถามนี้ใครต้องการคำตอบแบบเต็มฉันจะพยายามให้

สถานการณ์จำลองแอปพลิเคชัน

แทนที่จะมีการถกเถียงอย่างเป็นนามธรรมและเป็นนามธรรมมันช่วยให้มีแอปพลิเคชั่นที่เป็นรูปธรรมในใจ เพื่อช่วยกำหนดสถานการณ์สองมุมมองตัวควบคุมและสถานการณ์มากกว่าสองมุมมองตัวควบคุมฉันจะกำหนดสองสถานการณ์แอปพลิเคชันที่เป็นรูปธรรม

สถานการณ์ที่หนึ่ง: ผู้ควบคุมมุมมองสูงสุดสองคนต้องแชร์ข้อมูล ดูแผนภาพหนึ่ง

แผนภาพของปัญหาเดิม

มีตัวควบคุมมุมมองสองตัวในแอปพลิเคชัน มี ViewControllerA (Data Entry Form) และ View Controller B (รายการผลิตภัณฑ์) รายการที่เลือกในรายการผลิตภัณฑ์จะต้องตรงกับรายการที่แสดงในกล่องข้อความในแบบฟอร์มการป้อนข้อมูล ในสถานการณ์สมมตินี้ ViewControllerA และ ViewControllerB ต้องสื่อสารโดยตรงกับแต่ละอื่น ๆ และไม่มีตัวควบคุมมุมมองอื่น ๆ

สถานการณ์ที่สอง : ตัวควบคุมมุมมองมากกว่าสองตัวต้องใช้ข้อมูลเดียวกันร่วมกัน ดูแผนภาพสอง

แผนภาพใบสมัครสินค้าคงคลังที่บ้าน

มีตัวควบคุมมุมมองสี่ตัวในแอปพลิเคชัน มันเป็นโปรแกรมที่ใช้แท็บสำหรับการจัดการสินค้าคงคลังที่บ้าน ตัวควบคุมมุมมองสามตัวแสดงมุมมองที่กรองแตกต่างกันของข้อมูลเดียวกัน:

  • ViewControllerA - สินค้าฟุ่มเฟือย
  • ViewControllerB - รายการที่ไม่มีประกัน
  • ViewControllerC - สินค้าคงคลังบ้านทั้งหมด
  • ViewControllerD - เพิ่มฟอร์มรายการใหม่

ทุกครั้งที่มีการสร้างหรือแก้ไขแต่ละรายการรายการนั้นจะต้องซิงโครไนซ์กับตัวควบคุมมุมมองอื่น ๆ ตัวอย่างเช่นถ้าเราเพิ่มเรือใน ViewControllerD แต่มันยังไม่ได้รับการประกันเรือจะต้องปรากฏขึ้นเมื่อผู้ใช้ไปที่ ViewControllerA (สินค้าหรูหรา) และ ViewControllerC (สินค้าคงคลังบ้านทั้งหมด) แต่ไม่ใช่เมื่อผู้ใช้ไปที่ ViewControllerB (รายการที่ไม่มีประกัน) เราจำเป็นต้องคำนึงถึงไม่เพียง แต่เพิ่มรายการใหม่ แต่ยังลบรายการ (ซึ่งอาจได้รับอนุญาตจากตัวควบคุมมุมมองทั้งสี่) หรือแก้ไขรายการที่มีอยู่ (ซึ่งอาจได้รับอนุญาตจาก "เพิ่มแบบฟอร์มรายการใหม่" โดยเปลี่ยนรูปแบบเดิม สำหรับการแก้ไข)

เนื่องจากตัวควบคุมมุมมองทั้งหมดจำเป็นต้องใช้ข้อมูลเดียวกันตัวควบคุมมุมมองทั้งสี่จึงต้องอยู่ในการซิงโครไนซ์ดังนั้นจึงจำเป็นต้องมีการสื่อสารบางอย่างกับตัวควบคุมมุมมองอื่นทั้งหมดเมื่อใดก็ตามที่คอนโทรลเลอร์มุมมองเดี่ยวเปลี่ยนแปลงข้อมูลพื้นฐาน มันควรจะค่อนข้างชัดเจนว่าเราไม่ต้องการให้แต่ละคอนโทรลเลอร์มุมมองสื่อสารโดยตรงกับตัวควบคุมมุมมองอื่นในสถานการณ์นี้ ในกรณีที่ไม่ชัดเจนพิจารณาว่าเรามีตัวควบคุมมุมมองที่แตกต่างกัน 20 ตัว (ไม่ใช่แค่ 4) มันยากแค่ไหนและเกิดข้อผิดพลาดได้ง่ายเพียงใดเมื่อต้องแจ้งตัวควบคุมมุมมอง 19 ตัวอื่น ๆ เมื่อใดก็ตามที่ตัวควบคุมมุมมองตัวหนึ่งทำการเปลี่ยนแปลง

การแก้ปัญหา: ผู้ได้รับมอบหมายและรูปแบบการสังเกตการณ์และซิงเกิล

ในสถานการณ์สมมติที่หนึ่งเรามีโซลูชันที่ทำงานได้หลายอย่างตามที่ได้รับคำตอบอื่น ๆ

  • segues
  • ผู้ได้รับมอบหมาย
  • การตั้งค่าคุณสมบัติบนตัวควบคุมมุมมองโดยตรง
  • NSUserDefaults (อันที่จริงเป็นตัวเลือกที่แย่)

ในสถานการณ์ที่สองเรามีวิธีแก้ไขปัญหาอื่น ๆ ที่ทำงานได้:

  • รูปแบบการสังเกตการณ์
  • singletons

เดี่ยวเป็นตัวอย่างของชั้นเรียนที่อินสแตนซ์เป็นเพียงตัวอย่างในการดำรงอยู่ในช่วงชีวิตของมัน ซิงเกิลได้ชื่อมาจากข้อเท็จจริงที่ว่ามันเป็นอินสแตนซ์เดียว โดยปกตินักพัฒนาที่ใช้ซิงเกิลตันจะมีวิธีการเรียนพิเศษสำหรับการเข้าถึงพวกเขา

+ (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;
}

ตอนนี้เราเข้าใจว่าซิงเกิลตันคืออะไรมาพูดคุยกันว่าซิงเกิลนั้นเข้ากับรูปแบบการสังเกตการณ์อย่างไร รูปแบบผู้สังเกตการณ์ใช้สำหรับวัตถุหนึ่งเพื่อตอบสนองต่อการเปลี่ยนแปลงโดยวัตถุอื่น ในสถานการณ์ที่สองเรามีตัวควบคุมมุมมองที่แตกต่างกันสี่ตัวซึ่งทุกคนต้องการทราบเกี่ยวกับการเปลี่ยนแปลงข้อมูลพื้นฐาน "ข้อมูลพื้นฐาน" ควรเป็นของอินสแตนซ์เดี่ยวหนึ่งตัว "รู้เกี่ยวกับการเปลี่ยนแปลง" สามารถทำได้โดยการสังเกตการเปลี่ยนแปลงที่เกิดขึ้นกับซิงเกิล

แอปพลิเคชันสินค้าคงคลังที่บ้านจะมีอินสแตนซ์เดียวของคลาสที่ออกแบบมาเพื่อจัดการรายการของสินค้าคงคลัง ผู้จัดการจะจัดการสะสมสิ่งของในครัวเรือน ต่อไปนี้เป็นคำจำกัดความของคลาสสำหรับตัวจัดการข้อมูล:

#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

เมื่อคอลเลกชันของรายการสินค้าคงคลังที่บ้านมีการเปลี่ยนแปลงตัวควบคุมมุมมองจะต้องตระหนักถึงการเปลี่ยนแปลงนี้ คำจำกัดความของคลาสข้างต้นไม่ได้ทำให้ชัดเจนว่าจะเกิดอะไรขึ้น เราต้องทำตามรูปแบบผู้สังเกตการณ์ ตัวควบคุมมุมมองจะต้องสังเกตอย่างเป็นทางการของ SharedManager มีสองวิธีในการสังเกตวัตถุอื่น:

  • คีย์ - ค่า - การสังเกต (KVO)
  • NSNotificationCenter

ในสถานการณ์ที่สองเราไม่มีคุณสมบัติเดียวของ HouseholdInventoryManager ซึ่งสามารถสังเกตได้โดยใช้ KVO เนื่องจากเราไม่มีคุณสมบัติเดียวที่สังเกตได้ง่ายรูปแบบผู้สังเกตการณ์ในกรณีนี้จึงต้องใช้งานโดยใช้ NSNotificationCenter ตัวควบคุมมุมมองสี่ตัวแต่ละตัวจะสมัครรับการแจ้งเตือนและ SharedManager จะส่งการแจ้งเตือนไปยังศูนย์การแจ้งเตือนตามความเหมาะสม ผู้จัดการสินค้าคงคลังไม่จำเป็นต้องรู้อะไรเกี่ยวกับตัวควบคุมมุมมองหรืออินสแตนซ์ของคลาสอื่น ๆ ที่อาจสนใจที่จะรู้ว่าเมื่อใดที่การรวบรวมรายการสินค้าคงคลังเปลี่ยนแปลง NSNotificationCenter ดูแลรายละเอียดการใช้งานเหล่านี้ ตัวควบคุมมุมมองเพียงสมัครรับการแจ้งเตือนและผู้จัดการข้อมูลเพียงแค่โพสต์การแจ้งเตือน

โปรแกรมเมอร์มือใหม่หลายคนใช้ประโยชน์จากความจริงที่ว่ามีตัวแทนผู้สมัครหนึ่งคนเสมอในช่วงชีวิตของแอปพลิเคชันซึ่งสามารถเข้าถึงได้ทั่วโลก โปรแกรมเมอร์เริ่มต้นใช้ข้อเท็จจริงนี้เพื่อจัดเก็บวัตถุและฟังก์ชันการทำงานลงใน appDelegate เพื่อความสะดวกในการเข้าถึงจากที่อื่น ๆ ในแอปพลิเคชัน เพียงเพราะ AppDelegate เป็นซิงเกิลตันไม่ได้หมายความว่าควรแทนที่ซิงเกิลตันอื่นทั้งหมด นี่เป็นวิธีปฏิบัติที่ไม่ดีเนื่องจากมีภาระมากเกินไปในหนึ่งชั้นเรียนซึ่งเป็นการทำลายแนวปฏิบัติที่ดีของวัตถุ แต่ละชั้นเรียนควรมีบทบาทที่ชัดเจนซึ่งอธิบายได้ง่าย ๆ โดยมักจะใช้ชื่อของชั้นเรียน

เมื่อใดก็ตามที่ตัวแทนผู้ได้รับมอบหมายของคุณเริ่มที่จะป่องให้เริ่มลบการทำงานลงในซิงเกิล ตัวอย่างเช่น Core Data Stack ไม่ควรถูกทิ้งไว้ใน AppDelegate แต่ควรใส่ไว้ในคลาสของตัวเองนั่นคือคลาส coreDataManager

อ้างอิง


41

OP ไม่ได้กล่าวถึงตัวควบคุมมุมมอง แต่มีคำตอบมากมายที่ฉันต้องการพูดสอดด้วยคุณสมบัติใหม่ของ LLVM ที่อนุญาตให้ทำสิ่งนี้ได้ง่ายขึ้นเมื่อต้องการส่งข้อมูลจากคอนโทรลเลอร์มุมมองหนึ่งไปอีกอันหนึ่งแล้ว รับผลกลับมา

การแบ่งสตอรีบอร์ด, บล็อก ARC และ LLVM ทำให้ฉันง่ายขึ้นกว่าที่เคย คำตอบบางส่วนที่กล่าวถึงกระดานเรื่องราวและส่วนย่อยที่กล่าวมาแล้ว แต่ยังต้องพึ่งพาการมอบหมายงาน การกำหนดผู้รับมอบสิทธิ์ทำงานได้อย่างแน่นอน แต่บางคนอาจพบว่าผ่านตัวชี้หรือบล็อกรหัสได้ง่ายขึ้น

ด้วย UINavigators และส่วนย่อยมีวิธีง่ายๆในการส่งข้อมูลไปยังตัวควบคุมย่อยและรับข้อมูลกลับมา ARC ทำให้การส่งผ่านพอยน์เตอร์ไปยังสิ่งต่าง ๆ ที่ได้รับจาก NSObjects ง่ายดังนั้นหากคุณต้องการให้ตัวควบคุมย่อยเพิ่ม / เปลี่ยนแปลง / แก้ไขข้อมูลบางอย่างให้คุณให้ส่งตัวชี้ไปยังอินสแตนซ์ที่ไม่แน่นอน บล็อกทำให้การกระทำผ่านทำได้ง่ายดังนั้นถ้าคุณต้องการให้ตัวควบคุมย่อยเรียกใช้การกระทำบนตัวควบคุมระดับที่สูงขึ้นให้ส่งผ่านบล็อก คุณกำหนดบล็อกเพื่อยอมรับอาร์กิวเมนต์จำนวนเท่าใดก็ได้ที่เหมาะสมกับคุณ นอกจากนี้คุณยังสามารถออกแบบ API เพื่อใช้หลายบล็อกได้หากเหมาะสมกับสิ่งที่ดีกว่า

นี่คือตัวอย่างเล็กน้อยของกาวต่อเนื่อง แรกคือตรงไปตรงมาแสดงหนึ่งพารามิเตอร์ผ่านสำหรับการป้อนข้อมูลที่สองสำหรับการส่งออก

// 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];
}

ตัวอย่างที่สองนี้แสดงให้เห็นผ่านการบล็อกการเรียกกลับสำหรับอาร์กิวเมนต์ที่สอง ฉันชอบใช้บล็อกเพราะมันช่วยให้รายละเอียดที่เกี่ยวข้องอยู่ใกล้กันในแหล่งที่มา - แหล่งที่มาระดับที่สูงขึ้น

// 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];
    }];
}

41

การส่งข้อมูลกลับจาก ViewController 2 (ปลายทาง) ไปยัง viewController 1 (ที่มา) เป็นสิ่งที่น่าสนใจมากขึ้น สมมติว่าคุณใช้กระดานเรื่องราวนั่นคือวิธีที่ฉันค้นพบ:

  • ตัวแทน
  • การแจ้งเตือน
  • ค่าเริ่มต้นของผู้ใช้
  • ซิงเกิล

เหล่านี้ถูกกล่าวถึงที่นี่แล้ว

ฉันพบว่ามีวิธีเพิ่มเติม:

- ใช้บล็อกการโทรกลับ:

ใช้ในprepareForSegueวิธีการใน VC1

NextViewController *destinationVC = (NextViewController *) segue.destinationViewController;
[destinationVC setDidFinishUsingBlockCallback:^(NextViewController *destinationVC)
{
    self.blockLabel.text = destination.blockTextField.text;
}];

- การใช้กระดานเรื่องราวคลายออก (ออก)

ใช้วิธีการที่มีอาร์กิวเมนต์ UIStoryboardSegue ใน VC 1 เช่นนี้

-(IBAction)UnWindDone:(UIStoryboardSegue *)segue { }

ในกระดานเรื่องราวขอให้ปุ่ม "ย้อนกลับ" ไปที่ปุ่มออกสีเขียว (คลายกลับ) ของ vc ตอนนี้คุณมีเซกเมนต์ที่ "ย้อนกลับ" เพื่อให้คุณสามารถใช้คุณสมบัติปลายทางViewControllerใน prepareForSegue ของ VC2 และเปลี่ยนคุณสมบัติของ VC1 ก่อนที่จะย้อนกลับ

  • อีกทางเลือกหนึ่งของการใช้กระดานเรื่องราวยกเลิกการปลด (ออก) - คุณสามารถใช้วิธีที่คุณเขียนใน VC1

    -(IBAction)UnWindDone:(UIStoryboardSegue *)segue {
        NextViewController *nextViewController = segue.sourceViewController;
        self.unwindLabel.text = nextViewController.unwindPropertyPass;
    } 

    และใน prepareForSegue ของ VC1 คุณสามารถเปลี่ยนคุณสมบัติที่คุณต้องการแชร์ได้

ในตัวเลือกการผ่อนคลายทั้งสองคุณสามารถตั้งค่าคุณสมบัติแท็กของปุ่มและตรวจสอบได้ใน PreparForSegue

หวังว่าฉันจะเพิ่มบางสิ่งในการสนทนา

:) ไชโย


40

มีหลายวิธีในการแบ่งปันข้อมูล

  1. NSUserDefaultsคุณสามารถแบ่งปันข้อมูลโดยใช้ ตั้งค่าที่คุณต้องการแบ่งปันด้วยความเคารพต่อคีย์ที่คุณเลือกและรับค่าที่NSUserDefaultเกี่ยวข้องกับคีย์นั้นในตัวควบคุมมุมมองถัดไป

    [[NSUserDefaults standardUserDefaults] setValue:value forKey:key]
    [[NSUserDefaults standardUserDefaults] objectForKey:key]
  2. คุณสามารถสร้างอสังหาริมทรัพย์viewcontrollerAได้ สร้างวัตถุviewcontrollerAในviewcontrollerBและกำหนดค่าที่ต้องการให้กับทรัพย์สินนั้น

  3. คุณยังสามารถสร้างผู้รับมอบสิทธิ์ที่กำหนดเองสำหรับสิ่งนี้


30
วัตถุประสงค์ทั่วไปของ NSUserDefaults คือการจัดเก็บค่ากำหนดของผู้ใช้ที่ยังคงมีอยู่ระหว่างการดำเนินการของแอพดังนั้นสิ่งใดก็ตามที่เก็บไว้ที่นี่จะอยู่ที่นี่เว้นแต่จะถูกลบออกอย่างชัดเจน เป็นความคิดที่ดีที่จะใช้สิ่งนี้เพื่อส่งผ่านข้อมูลระหว่างตัวควบคุมมุมมอง (หรือวัตถุอื่น ๆ ) ในแอป
JoséGonzález

30

หากคุณต้องการส่งผ่านข้อมูลจากคอนโทรลเลอร์หนึ่งไปยังอีกลองรหัสนี้

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];
 }

29

นี่เป็นคำตอบที่เก่ามากและเป็นรูปแบบต่อต้านโปรดใช้ผู้ได้รับมอบหมาย อย่าใช้วิธีการนี้ !!

1.@property (nonatomic,assign)สร้างอินสแตนซ์แรกดูควบคุมในครั้งที่สองดูควบคุมและทำให้ทรัพย์สินของ

2.กำหนดSecondviewControllerอินสแตนซ์ของตัวควบคุมมุมมองนี้

2.เมื่อคุณทำการเลือกเสร็จแล้วให้คัดลอกอาร์เรย์ไปยังมุมมองแรกของตัวควบคุมเมื่อคุณยกเลิกการโหลด SecondView, FirstView จะเก็บข้อมูล Array

หวังว่าสิ่งนี้จะช่วย


2
ฉันไม่เชื่อว่านี่เป็นวิธีที่ถูกต้องในการสร้างลิงก์ที่มีรอยยับมากระหว่างตัวควบคุมมุมมอง ไม่เกาะติดกับ MVC จริงๆ
ราคาแมตต์

1
หากคุณต้องการที่จะปฏิบัติตามอย่างเคร่งครัด MVC ใช้NSNotificationCenterวิธีการที่สามารถเรียกจาก ViewControllerA เพื่อ ViewControllerB ตรวจสอบนี้มันอาจจะช่วยให้ยู
kaar3k

28

ฉันค้นหาโซลูชันนี้มาเป็นเวลานานฉันพบว่า Atlast ก่อนอื่นให้ประกาศวัตถุทั้งหมดในไฟล์ 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และวัตถุ ตอนนี้คุณสามารถเติมหน่วยความจำนั้นก่อนที่จะผลักดันสิ่งนี้ViewController

ไปที่ SecondViewController.h ของคุณแล้วเขียนสองวิธี

-(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 ];

ดูแลการสะกดผิด


24

นี่ไม่ใช่วิธีที่จะทำคุณควรใช้ผู้รับมอบสิทธิ์ฉันจะถือว่าเรามีตัวควบคุมมุมมองสองตัว ViewController1 และ ViewController2 และสิ่งที่ตรวจสอบนี้อยู่ในตัวแรกและเมื่อสถานะเปลี่ยนไปคุณต้องทำบางสิ่งใน ViewController2 เพื่อ บรรลุเป้าหมายด้วยวิธีที่เหมาะสมคุณควรทำสิ่งต่อไปนี้:

เพิ่มไฟล์ใหม่ให้กับโครงการของคุณ (Objective-C Protocol) ไฟล์ -> ใหม่ตอนนี้ตั้งชื่อ 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; 

ตอนนี้ถ้าคุณกำลังสร้าง ViewController1 ภายใน ViewController2 หลังจากเหตุการณ์บางอย่างคุณควรทำเช่นนี้โดยใช้ไฟล์ 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

โปรดบอกฉันว่ามีอะไรที่ไม่ชัดเจนถ้าฉันไม่เข้าใจคำถามของคุณอย่างถูกต้อง


23

หากคุณต้องการส่งข้อมูลจาก viewController หนึ่งไปยังอีกนี่คือวิธีการ:

สมมติว่าเรามี viewControllers: 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 โดยไม่ต้องตั้งค่าตัวแทนใด ๆ ;)


1
ฉันลองใช้รหัส ur ในโครงการของฉัน แต่ไม่สามารถรับค่าใน viewcontrollerB คุณบอกฉันได้ไหมว่าปัญหาคืออะไร
X-Coder

1
@Ajitthala คุณสามารถวางรหัสในคำถามใหม่ได้หรือไม่ ฉันจะพยายามแก้ปัญหาของคุณ :)
Aniruddh Joshi

1
ผิดไหมที่จะไม่ใช้วิธีการเริ่มต้นและทำสิ่งที่ต้องการ vcB.string = @ "asdf" จาก viewcontroller A
khanh.tran.vinh

1
@ khanh.tran.vinh ขึ้นอยู่กับว่าคุณใช้ ARC หรือไม่
Aniruddh Joshi

21

ฉันรู้ว่านี่เป็นวิชาที่ถูกตี แต่สำหรับผู้ที่ต้องการตอบคำถามนี้ด้วย SWIFT slant และต้องการตัวอย่างที่ไม่มีกระดูกนี่คือวิธีการส่งข้อมูลของฉันแบบไปที่ถ้าคุณกำลังใช้เซกคิวเพื่อไปไหนมาไหน

มันคล้ายกับข้างต้น แต่ไม่มีปุ่มฉลากและเช่น เพียงแค่ส่งข้อมูลจากมุมมองหนึ่งไปยังอีกมุมมองหนึ่ง

ตั้งค่ากระดานเรื่องราว

มีสามส่วน

  1. ผู้ส่ง
  2. The Segue
  3. ผู้รับ

นี่เป็นเลย์เอาต์มุมมองที่ง่ายมากโดยมีส่วนต่อขยายระหว่างกัน


เลย์เอาต์ดูง่ายมาก  หมายเหตุ: ไม่มีตัวควบคุมทิศทาง


นี่คือการตั้งค่าสำหรับผู้ส่ง


ผู้ส่ง


นี่คือการตั้งค่าสำหรับผู้รับ


ผู้รับ


สุดท้ายการตั้งค่าสำหรับการทำต่อ


ตัวบ่งชี้ Segue


ตัวควบคุมมุมมอง

เรากำลังทำให้ปุ่มนี้ง่ายไม่มีปุ่มไม่ใช่การกระทำเราเพียงแค่เคลื่อนย้ายข้อมูลจากผู้ส่งไปยังผู้รับเมื่อแอพพลิเคชั่นโหลดแล้วส่งออกค่าที่ส่งไปยังคอนโซล

หน้านี้ใช้ค่าการโหลดครั้งแรกและส่งผ่านไป

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

    }

}

หน้านี้จะส่งค่าของตัวแปรไปยังคอนโซลเมื่อโหลด ณ จุดนี้ภาพยนตร์เรื่องโปรดของเราควรอยู่ในตัวแปรนั้น

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)")

    }   
}

นั่นคือวิธีที่คุณสามารถเล่นงานหากคุณต้องการใช้งานแยกย่อยและคุณไม่มีเพจภายใต้ตัวควบคุมการนำทาง

เมื่อรันแล้วควรสลับไปที่มุมมองตัวรับโดยอัตโนมัติและส่งค่าจากผู้ส่งไปยังผู้รับโดยแสดงค่าในคอนโซล

Ghost Busters เป็นเกมคลาสสิค


19

ในกรณีของฉันฉันใช้คลาสเดี่ยวซึ่งสามารถทำงานเป็นวัตถุระดับโลกที่อนุญาตให้เข้าถึงข้อมูลจากเกือบทุกที่ในแอป สิ่งแรกคือการสร้างชั้นเดียว โปรดดูที่หน้า " อะไรคือวัตถุประสงค์ของ C-singleton ของฉันควรมีลักษณะอย่างไร " และสิ่งที่ฉันทำเพื่อให้วัตถุที่สามารถเข้าถึงได้ทั่วโลกนั้นเป็นเพียงการนำเข้ามันappName_Prefix.pchซึ่งใช้สำหรับการนำคำสั่งการนำเข้ามาใช้ในทุกชั้นเรียน ในการเข้าถึงวัตถุนี้และใช้ฉันเพียงใช้วิธีการเรียนการสอนเพื่อส่งคืนอินสแตนซ์ที่ใช้ร่วมกันซึ่งมีตัวแปรของตัวเอง


นี่คือคำตอบที่ถูกต้อง เพียงใช้ซิงเกิลตันเป็น "แบบจำลอง" โปรดทราบว่าเป็นคาเลบกล่าวว่า"รูปแบบของแอปของคุณอาจจะเป็นง่ายๆเป็นอาร์เรย์ของสตริง" มันสำคัญที่จะทราบว่าการทำเดี่ยวในสวิฟท์เป็นที่น่ารำคาญจริงๆ (ง่ายดังนั้นก็ไม่ได้มีมูลค่าการกล่าวขวัญที่นี่ -. เพียง google) สำหรับการเขียนโปรแกรมใหม่ก็คุ้มค่าความเข้าใจว่าการทำเดี่ยวที่ใช้ในการเจ็บจริงในตูด อย่างไรก็ตามซิงเกิลนั้นเป็นศูนย์กลางของการเขียนโปรแกรม iOS ทุกอย่างที่แอปเปิ้ลทำคือซิงเกิล นั่นเป็นเหตุผลว่าทำไม Apple จึงสร้าง trtvial (ใน Swift) เพื่อให้ได้ซิงเกิลที่ถูกต้อง
Fattie

1
อย่างไรก็ตามโปรดทราบว่าทุกวันนี้ (2016+) "ทุกอย่างเป็นมุมมองคอนเทนเนอร์ใน iOS" ทุกสิ่งที่คุณทำบนหน้าจอคุณทำมุมมองคอนเทนเนอร์เล็กน้อย มันค่อนข้างง่ายที่จะได้รับการอ้างอิงมุมมองคอนเทนเนอร์ "ขึ้นและลง" (แม้ว่า Apple จะทำให้ง่ายขึ้นในอนาคต) และคุณทำเช่นนี้กับมุมมองคอนเทนเนอร์เกือบทุกครั้ง ดังนั้นถ้าคุณทำเช่นนั้น - คุณมีคำตอบ; ไม่จำเป็นต้องเป็นคนโสด แนะนำมุมมองคอนเทนเนอร์ ... stackoverflow.com/a/23403979/294884
Fattie

19

สวิฟท์ 5

ดีคำตอบแมตต์ไพรซ์เป็นอย่างดีดีสำหรับการส่งผ่านข้อมูล แต่ฉันจะเขียนมันในรุ่นสวิฟท์ล่าสุดเพราะผมเชื่อว่าเขียนโปรแกรมใหม่พบว่ามันออกจากความท้าทายเนื่องจากไวยากรณ์ใหม่และวิธีการ / กรอบเช่นโพสต์ต้นฉบับอยู่ใน Objective-C

มีหลายตัวเลือกสำหรับการส่งผ่านข้อมูลระหว่างตัวควบคุมมุมมอง

  1. การใช้ตัวควบคุมทิศทาง
  2. ใช้ Segue
  3. ใช้ตัวแทน
  4. ใช้สังเกตการณ์การแจ้งเตือน
  5. ใช้บล็อก

ฉันจะเขียนตรรกะของเขาใหม่ใน Swift ด้วย iOS Framework ล่าสุด


การส่งข้อมูลผ่านตัวควบคุมการนำทางพุช : จาก 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 Pass Data ในขณะที่กดผ่าน 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)
    }
}

การส่งผ่านข้อมูลผ่านเซกเมนต์ : จาก ViewControllerA ไปยัง ViewControllerB

ขั้นตอนที่ 1สร้าง Segue จาก ViewControllerA ไปยัง ViewControllerB และให้ Identifier = showDetailSegue ใน Storyboard ดังที่แสดงด้านล่าง

ป้อนคำอธิบายรูปภาพที่นี่

ขั้นตอนที่ 2ใน ViewControllerB ประกาศชื่อisSomethingEnabled ที่ทำงานได้และพิมพ์ค่า

ขั้นตอนที่ 3ในการส่งผ่าน ViewControllerA คือค่าของ SomethingEnabled ขณะผ่าน Segue

ดังนั้นนี่คือรหัสที่สมบูรณ์สำหรับ:

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ประกาศ Protocol ViewControllerBDelegateในไฟล์ ViewControllerB แต่อยู่นอกคลาส

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ส่งข้อมูลสำหรับผู้ได้รับมอบหมายภายในวิธีการ viewDidLoad ของ ViewControllerB

delegate?.addItemViewController(self, didFinishEnteringItem: "Data for ViewControllerA")

ขั้นตอน 4.ยืนยัน ViewControllerBDelegate ใน ViewControllerA

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 permissionCompletionBlock: ((Bool) -> ())? = {_ ใน}

ขั้นตอนที่ 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 ของฉันโปรดแจ้งให้เราทราบหากคุณมีคำถามใด ๆ เกี่ยวกับเรื่องนี้


18

การส่งผ่านข้อมูลระหว่าง FirstViewController ไปยัง SecondViewController ดังต่อไปนี้

ตัวอย่างเช่น:

ค่า FirstViewController สตริงเป็น

StrFirstValue = @"first";

เพื่อให้เราสามารถส่งผ่านค่านี้ในชั้นสองโดยใช้ขั้นตอนด้านล่าง

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 จากวิธีที่เรานำทางไปยังมุมมองที่สองโปรดเขียนโค้ดด้านล่างในวิธีการนั้น

SecondViewController *secondView= [[SecondViewController alloc]     
initWithNibName:@"SecondViewController " bundle:[NSBundle MainBundle]];

[secondView setStrSecondValue:StrFirstValue];

[self.navigationController pushViewController:secondView animated:YES ];

หลังจากอยู่ใน SecondViewController คุณจะส่งข้อมูลกลับไปยัง FirstViewController ได้อย่างไร
bruno

18

ขณะนี้ฉันมีส่วนร่วมในการแก้ปัญหาโอเพ่นซอร์สกับปัญหานี้ผ่านโครงการที่เรียกว่า 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 ใหม่และข้อมูลส่งผ่านคุณสร้างความตั้งใจใหม่และเพิ่มข้อมูลลงในพจนานุกรม (ที่บันทึกไว้ InstanceState) จากนั้นเพียงกำหนดเจตนาปัจจุบันของโรงงาน:

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: method ใหม่ช่วยให้คุณเข้าถึงข้อมูลที่คุณส่งผ่าน

-(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];
}

หวังว่าบางท่านจะพบว่าโซลูชันนี้มีประโยชน์ / น่าสนใจ


จากนั้นวัตถุคอนโทรลเลอร์ทั้งหมดสามารถรับ / ตั้งค่าพจนานุกรมที่ลงทะเบียนทั้งหมดในขอบเขตใด ๆ ลงคะแนนนี้
Itachi

15

สร้างคุณสมบัติต่อไปview controller .hและกำหนด getter และ setter

เพิ่มpropertyใน NextVC.h ใน nextVC

@property (strong, nonatomic) NSString *indexNumber;

เพิ่ม

@synthesize indexNumber; ใน NextVC.m

และสุดท้าย

NextVC *vc=[[NextVC alloc]init];

vc.indexNumber=@"123";

[self.navigationController vc animated:YES];

11

มีหลายวิธีในการทำเช่นนี้และสิ่งสำคัญคือการเลือกวิธีการที่เหมาะสม อาจเป็นหนึ่งในการตัดสินใจทางสถาปัตยกรรมที่ยิ่งใหญ่ที่สุดขึ้นอยู่กับวิธีการแบ่งปันหรือเข้าถึงรหัสรุ่นทั่วทั้งแอป

ผมเขียนบล็อกโพสต์เกี่ยวกับเรื่องนี้ในขณะที่กลับมาร่วมกันรหัสรุ่น นี่เป็นบทสรุปโดยย่อ:

ข้อมูลที่แชร์

วิธีหนึ่งคือการใช้พอยน์เตอร์ร่วมกับวัตถุจำลองระหว่างตัวควบคุมมุมมอง

  • บังคับให้มีการวนซ้ำของตัวควบคุมมุมมอง (ในตัวควบคุมการนำทางหรือ Tab Bar) เพื่อตั้งค่าข้อมูล
  • ตั้งค่าข้อมูลใน prepareForSegue (ถ้ากระดานเรื่องราว) หรือ init (ถ้าเป็นโปรแกรม)

เนื่องจากการเตรียมพร้อมสำหรับการต่อเวลาเป็นเรื่องธรรมดาที่สุดที่นี่เป็นตัวอย่าง:

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
    var next = segue.destinationViewController as NextViewController
    next.dataSource = dataSource
}

การเข้าถึงอิสระ

อีกวิธีหนึ่งคือการจัดการหน้าจอที่เต็มไปด้วยข้อมูลในแต่ละครั้งและแทนที่จะเชื่อมต่อตัวควบคุมมุมมองกับแต่ละคู่อื่น ๆ แต่ละตัวควบคุมมุมมองกับแหล่งข้อมูลเดียวที่พวกเขาสามารถเข้าถึงได้อย่างอิสระ

วิธีที่พบมากที่สุดที่ฉันเคยเห็นสิ่งนี้ทำคืออินสแตนซ์เดี่ยว ดังนั้นหากวัตถุเดี่ยวของคุณคือDataAccessคุณสามารถทำต่อไปนี้ในวิธีการ viewDidLoad ของ UIViewController:

func viewDidLoad() {
    super.viewDidLoad()
    var data = dataAccess.requestData()
}

มีเครื่องมือเพิ่มเติมที่ช่วยส่งผ่านข้อมูล:

  • การสังเกตมูลค่าคีย์
  • NSNotification
  • ข้อมูลหลัก
  • NSFetchedResultsController
  • แหล่งข้อมูล

ข้อมูลหลัก

สิ่งที่ดีเกี่ยวกับ Core Data ก็คือมันมีความสัมพันธ์แบบผกผัน ดังนั้นหากคุณต้องการให้วัตถุ NotesViewController กับ Notes คุณสามารถทำได้เพราะจะมีความสัมพันธ์แบบผกผันกับสิ่งอื่นเช่นสมุดบันทึก หากคุณต้องการข้อมูลบนโน้ตบุ๊กใน NotesViewController คุณสามารถเดินกลับกราฟวัตถุโดยทำสิ่งต่อไปนี้:

let notebookName = note.notebook.name

อ่านเพิ่มเติมเกี่ยวกับเรื่องนี้ในบล็อกโพสต์ของฉัน: รหัสรุ่นที่ใช้ร่วมกัน


10

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;

10

การมอบหมายเป็นวิธีแก้ไขปัญหาเดียวในการดำเนินการดังกล่าวเมื่อคุณใช้ไฟล์. xib อย่างไรก็ตามคำตอบทั้งหมดที่อธิบายไว้ข้างต้นใช้storyboardสำหรับไฟล์. xibs ที่คุณต้องใช้การมอบหมาย นั่นเป็นทางออกเดียวที่คุณทำได้

อีกวิธีคือใช้รูปแบบคลาสซิงเกิลเริ่มต้นหนึ่งครั้งและใช้ในแอพทั้งหมดของคุณ


10

ถ้าคุณต้องการส่งผ่านข้อมูลจาก ViewControlerOne ไปยัง ViewControllerTwo ลองเหล่านี้ ..

ทำสิ่งเหล่านี้ใน ViewControlerOne.h

 @property (nonatomic, strong) NSString *str1;

ทำสิ่งเหล่านี้ใน ViewControllerTwo.h

 @property (nonatomic, strong) NSString *str2;

สังเคราะห์ str2 ใน ViewControllerTwo.m

@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);
}

10

คุณสามารถบันทึกข้อมูลในตัวแทนแอพเพื่อเข้าถึงข้อมูลผ่านคอนโทรลเลอร์ต่างๆในแอปพลิเคชันของคุณ สิ่งที่คุณต้องทำคือสร้างอินสแตนซ์ที่แชร์ของตัวแทนแอพ

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

ตัวอย่างเช่น

หากคุณประกาศNSArray object *arrayXYZแล้วคุณสามารถเข้าถึงได้ในตัวควบคุมมุมมองใด ๆ โดยappDelegate.arrayXYZ


มันเป็นวิธีการที่เหมาะสำหรับแฮ็คฮ็อต
Hai Feng Kao

9

หากคุณต้องการส่งข้อมูลจาก viewController หนึ่งไปยังอีกนี่คือวิธีการ:

สมมติว่าเรามี viewControllers: 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

ด้วยวิธีนี้เราสามารถส่งผ่านข้อมูลจาก viewcontroller หนึ่งไปยังคอนโทรลเลอร์ view อื่น ...


8

ฉันชอบแนวคิดของ Model object และ Mock objects ที่อิงกับ NSProxy เพื่อคอมมิทหรือทิ้งข้อมูลถ้าผู้ใช้ที่เลือกสามารถยกเลิกได้

ง่ายต่อการส่งผ่านข้อมูลเนื่องจากเป็นวัตถุเดี่ยวหรือสองวัตถุและถ้าคุณสมมติว่าตัวควบคุม UINavigationController คุณสามารถเก็บการอ้างอิงถึงโมเดลภายในและตัวควบคุมมุมมองแบบพุชทั้งหมดสามารถเข้าถึงได้โดยตรงจากตัวควบคุมการนำทาง


8

ฉันเคยเห็นผู้คนจำนวนมากมายุ่งเกี่ยวกับเรื่องนี้โดยใช้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 บรรทัดภายในเมธอดและคุณเสร็จสิ้นแล้ว


6

มีคำตอบมากมายสำหรับคำถามนี้ที่เสนอวิธีการที่หลากหลายในการทำการสื่อสารผ่านคอนโทรลเลอร์มุมมองที่ใช้งานได้จริง แต่ฉันไม่เห็นว่าที่ใดที่กล่าวถึงอันที่จริงแล้วใช้งานได้ดีและที่ควรหลีกเลี่ยง

ในทางปฏิบัติในความคิดของฉันแนะนำเพียงไม่กี่โซลูชั่น:

  • ในการส่งต่อข้อมูล:
    • แทนที่prepare(for:sender:)วิธีการUIViewControllerเมื่อใช้กระดานเรื่องราวและส่วนย่อย
    • ส่งผ่านข้อมูลผ่าน initializer หรือผ่านคุณสมบัติเมื่อดำเนินการเปลี่ยนมุมมองตัวควบคุมรหัสจน
  • เพื่อส่งข้อมูลย้อนหลัง
    • อัปเดตสถานะแอปที่แชร์ (ซึ่งคุณสามารถส่งต่อระหว่างตัวควบคุมมุมมองด้วยวิธีใดวิธีหนึ่งข้างต้น)
    • ใช้การมอบหมาย
    • ใช้การต่อเนื่องที่ผ่อนคลาย

แนวทางแก้ไขฉันไม่แนะนำให้ใช้:

  • อ้างอิงตัวควบคุมก่อนหน้าโดยตรงแทนการใช้การมอบหมาย
  • การแชร์ข้อมูลผ่านซิงเกิล
  • การส่งผ่านข้อมูลผ่านตัวแทนแอป
  • การแบ่งปันข้อมูลผ่านค่าเริ่มต้นของผู้ใช้
  • การส่งผ่านข้อมูลผ่านการแจ้งเตือน

วิธีแก้ปัญหาเหล่านี้แม้ว่าจะทำงานในระยะสั้น แต่แนะนำการพึ่งพามากเกินไปที่จะทำให้เกิดปัญหากับสถาปัตยกรรมของแอพและสร้างปัญหาเพิ่มเติมในภายหลัง

สำหรับผู้ที่สนใจฉันเขียนบางบทความที่เน้นประเด็นเหล่านี้ในเชิงลึกและเน้นข้อเสียต่าง ๆ :

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.