61

我正在尝试将数据传递回以前的 viewController。

有谁知道如何将数据从 ViewController B 传递回 ViewController A?所以我想要一个字符串“从”BIDAddTypeOfDealViewController 到 BIDDCCreateViewController。用户编辑 viewController B,我希望将编辑后的数据返回到 ViewController A 中,然后在其中使用它。

我正在使用此答案的“传回数据”部分。我的不同之处:第 3 点和第 6 点只提到了何时弹出视图,所以我将该代码放在 viewWillDisappear 中。我认为这是正确的?同样在第 6 点上,我没有使用 nib 进行初始化,因为它是旧的。我正在使用故事板。我没有添加最后一行,因为我不相信我必须推动它。在我的故事板上按下一个按钮已经让我前进了。

我认为问题可能出现在 BIDDCCreateViewController 中,我有方法但我无法运行它。要运行一个方法,它应该去 [self 方法]。我无法做到这一点。嗯,这正是我的猜测。

它编译并运行良好,只是没有记录任何内容,所以我不知道它是否有效。

更新:我无法执行“sendDataToA”方法。

#import <UIKit/UIKit.h>
#import "BIDAddTypeOfDealViewController.h"

 @interface BIDDCCreateViewController : UIViewController
 @property (strong, nonatomic) NSString *placeId;
- (IBAction)gotoBViewController:(id)sender;
@end


#import "BIDDCCreateViewController.h"
#import "BIDAddTypeOfDealViewController.h"

@implementation BIDDCCreateViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    NSLog(@"SUCCESSFULLY PASSED PLACE ID: %@", self.placeId);
}

-(void)sendDataToA:(NSString *)myStringData
{

    NSLog(@"Inside sendDataToA");
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Your string Data Showing" message:myStringData delegate:self cancelButtonTitle:@"Ok " otherButtonTitles:nil];
    [alert show];
}

- (IBAction)gotoBViewController:(id)sender {
    NSLog(@"pressed");
    BIDAddTypeOfDealViewController *bidAddType = [[BIDAddTypeOfDealViewController alloc]init];
    bidAddType.delegate = self;

}
@end


@protocol senddataProtocol <NSObject>
-(void)sendDataToA:(NSString *)myStringData;
@end

#import <UIKit/UIKit.h>
@interface BIDAddTypeOfDealViewController : UIViewController <UITextFieldDelegate>//Using this delegate for data a user inputs
@property(nonatomic,assign)id delegate;
//other textfield outlets not relevant
- (IBAction)chooseDiscountDeal:(id)sender;
@end

#import "BIDAddTypeOfDealViewController.h"

@interface BIDAddTypeOfDealViewController ()

@end

@implementation BIDAddTypeOfDealViewController
@synthesize delegate;

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        // Custom initialization
    }
    return self;
}

- (void)viewDidLoad
{
    [super viewDidLoad];
}

-(void)viewWillDisappear:(BOOL)animated
{
    [delegate sendDataToA:@"Apple"];
}
@end
4

9 回答 9

100

您可以使用委托。因此,在您的 ViewController B 中,您需要创建一个将数据发送回 ViewController A 的协议。您的 ViewController A 将成为 ViewController B 的委托。

如果您不熟悉目标 C,请查看什么是 Delegate

在 ViewControllerB.h 中创建协议:

#import <UIKit/UIKit.h>

@protocol senddataProtocol <NSObject>

-(void)sendDataToA:(NSArray *)array; //I am thinking my data is NSArray, you can use another object for store your information. 

@end

@interface ViewControllerB : UIViewController

@property(nonatomic,assign)id delegate;

视图控制器B.m

@synthesize delegate;
-(void)viewWillDisappear:(BOOL)animated
{
     [delegate sendDataToA:yourdata];

}

在你的 ViewControllerA :当你去 ViewControllerB

ViewControllerA *acontollerobject=[[ViewControllerA alloc] initWithNibName:@"ViewControllerA" bundle:nil];
acontollerobject.delegate=self; // protocol listener
[self.navigationController pushViewController:acontollerobject animated:YES];

并定义您的功能:

-(void)sendDataToA:(NSArray *)array
{
   // data will come here inside of ViewControllerA
}

编辑:

您可以查看此示例:如何将数据传递回以前的视图控制器:教程链接

于 2013-10-13T09:31:53.697 回答
64

比协议/委托更短更简单的方法是创建一个闭包:

在我的情况下发送一个字符串。在 ViewControllerA 中:

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if let viewControllerB = segue.destination as? ViewControllerB {
        viewControllerB.callback = { message in
            //Do what you want in here!
        }
    }
}

在 ViewControllerB 中:

var callback : ((String) -> Void)?

@IBAction func done(sender: AnyObject) {
    callback?("Hi")
    self.dismiss(animated: true, completion: nil)
}
于 2016-03-02T22:31:48.410 回答
49

Swift:使用委托模式发回数据

我涵盖双向传递数据的完整答案在这里。我解释委托模式的答案在这里

要将数据从第二个视图控制器传递回第一个视图控制器,您需要使用协议和委托。该视频非常清楚地介绍了该过程:

以下是基于视频的示例(稍作修改)。

在此处输入图像描述

在 Interface Builder 中创建故事板布局。同样,要进行转场,您只需Control从按钮拖动到第二个视图控制器。将 segue 标识符设置为showSecondViewController. 此外,不要忘记使用以下代码中的名称连接插座和操作。

第一个视图控制器

第一个视图控制器的代码是

import UIKit

class FirstViewController: UIViewController, DataEnteredDelegate {
    
    @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
        }
    }
    
    func userDidEnterInformation(info: String) {
        label.text = info
    }
}

注意我们自定义DataEnteredDelegate协议的使用。

第二视图控制器和协议

第二个视图控制器的代码是

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: UIButton) {
        
        // call this method on whichever class implements our delegate protocol
        delegate?.userDidEnterInformation(textField.text!)
        
        // go back to the previous view controller
        self.navigationController?.popViewControllerAnimated(true)
    }
}

请注意,它protocol位于 View Controller 类之外。

而已。现在运行应用程序,您应该能够将数据从第二个视图控制器发送回第一个视图控制器。

于 2015-10-20T06:28:42.463 回答
2

编辑: 使用上面@Erhan 的解决方案。不是这个。这不是一个好的解决方案。

这会有所帮助。把它写在你的 ViewControllerB 中。

    // Get array of current navigation stack
    NSArray *arrayViewControllers = [self.navigationController viewControllers];

    // Get previous viewController object from it
    YOUR_VIEW_CONTROLLER_NAME *objViewController = (YOUR_VIEW_CONTROLLER_NAME *)[arrayViewControllers objectAtIndex:arrayViewControllers.count-2];

    // For safety this check is needed. whether it the class that you want or not.
    if ([objViewController isKindOfClass:[YOUR_VIEW_CONTROLLER_NAME class]])
    {
        // Access properties of YOUR_VIEW_CONTROLLER_NAME here
        objViewController.yourProperty = YOUR_VALUE;
    }
于 2013-10-13T09:53:06.127 回答
2

正如 Erhan Demirci 回答的那样,您可以使用代表。当您想将数据传递给单个视图控制器时,委托很有帮助。

NSNotificationCenter是另一种在视图控制器/对象之间传输数据的便捷方式。这对于在应用程序中广播数据非常有帮助。

在此处阅读文档。

于 2014-01-29T15:50:22.083 回答
1

自定义委托是移动数据的最佳选择,但您也可以尝试这样做。

您可以使用 NSUserDefaults 将数据移动到您想要的任何位置。

斯威夫特 3 代码

UserDefaults.standard.set(<Value>, forKey: <Key>) 
// To set data

UserDefaults.standard.object(forKey: <Key>) 
// To get data

你也可以使用 NSNotification 来移动数据。

NotificationCenter.default.post(name: Notification.Name(rawValue: "refresh"), object: myDict) 

NotificationCenter.default.addObserver(self, selector: #selector(refreshList(_:)), name: NSNotification.Name(rawValue: "refresh"), object: nil)
于 2017-02-28T12:36:49.360 回答
0

有协议就有闭包。使用闭包,我们需要通过使用弱自我(或无主自我)来避免内存泄漏。使用协议,每个 viewController 都会有一个您想要“监控”的视图控制器,最终需要实现数十个委托。在这里,我在 Swift 中有另一个简单的解决方案:

在新文件或现有文件(例如:)中UIViewController+Extensions.swift,创建此协议:

protocol ViewControllerBackDelegate: class {
    func back(from viewController: UIViewController)
}

在 LEVEL-2 viewController 中,当按下 Back 时,您需要一个回调:

class LevelTwoViewController: UIViewController {
    // making this a weak variable so that it won't create a strong reference cycle
    weak var delegate: ViewControllerBackDelegate? = nil

    override func willMove(toParentViewController parent: UIViewController?) {
        super.willMove(toParentViewController: parent)
        if (parent == nil) {
            delegate?.back(from: self)
        }
    }
}

由于delegate是可选的,您可以将此代码添加到视图控制器的基类中。我会添加到它需要的地方。

在您的 LEVEL-1 viewController 中,假设您通过 Storyboard 中的 segue 调用 LEVEL-2:

class LevelOneViewController: UIViewController {
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if (segue.identifier == "Go to Level 2") {
            if let vc = segue.destination as? LevelTwoViewController {
                vc.selectedItems = self.selectedItems // passing data-in
                vc.delegate = self
            }
        }
        // repeat `if` for another sub-level view controller
    }
}

extension LevelOneViewController: ViewControllerBackDelegate {    
    func back(from viewController: UIViewController) {
        if let vc = viewController as? LevelTwoViewController {
            self.selectedItems = vc.selectedItems
            // call update if necessary
        }
        // repeat `if` for another sub-level view controller
    }
}
  • 只需要一个协议。
  • 每个第一级 viewController 只有一个扩展。
  • 如果需要返回更多/更少的数据,则无需修改子级 viewController
  • 像处理数据输入一样处理数据输出prepare(for:sender:)
于 2018-09-13T16:50:47.333 回答
0
//FirstViewController

import UIKit

class FirstViewController: UIViewController {

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.
}
override func viewWillAppear(_ animated: Bool) {
     
}

@IBAction func pushToSecond(_ sender: Any) {
    if let vc = storyboard?.instantiateViewController(withIdentifier: "SecondViewController")as? SecondViewController {
         vc.callBack = { (id: String,name: String,age: Int) in
            print(id,name,age)
        }
        self.navigationController?.pushViewController(vc, animated: true)
    }
}

}

// //第二视图控制器

import UIKit

class SecondViewController: UIViewController {

var callBack: ((_ id: String, _ name: String, _ age: Int)-> Void)?

@IBAction func BackToFirstWitData(_ sender: Any) {
    
    callBack?("1","Test",22)
    self.navigationController?.popViewController(animated: true)
    
}
override func viewDidLoad() {
    super.viewDidLoad()
    
    // Do any additional setup after loading the view.
}

}
于 2021-10-29T11:52:38.853 回答
-3

这是我将如何做到的。

@interface ViewControllerA:UIViewController
@property(strong, nonatomic) ViewControllerB * recieverB;
@end

@implementation ViewControllerA
//implement class
- (void)prepareForSegue:(UIStoryboardSegue *) sender:(id)sender
{
segue.destinationViewController.recieverA = self;
}
-(void)viewDidLoad
{
//stop strong refrence cycle
self.viewControllerB = nil;
}
@end

B类

@interface ViewControllerB:UIViewController
@property(strong, nonatomic, getter = parentClass) ViewControllerB * recieverA;
@end

@implementation ViewControllerB
//implement class
- (void)viewWillDisappear:(BOOL)animated
{
parentClass.recieverB = self;
//now class A will have an instance on class b
}
@end

我没有把#import

于 2013-10-13T09:47:47.677 回答