【问题标题】:Pass data back to previous viewcontroller将数据传回前一个视图控制器
【发布时间】:2013-10-21 00:45:55
【问题描述】:

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

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

我正在使用this answer 的“传回数据”部分。我的不同之处:第 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

【问题讨论】:

    标签: iphone ios objective-c uiviewcontroller


    【解决方案1】:

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

    如果您是 Objective C 的新手,请查看What is 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;
    

    ViewControllerB.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
    }
    

    已编辑:

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

    【讨论】:

    • Erhan,也许你再快速看一下我当前的代码。我没有“综合”,因为我不相信 ARC 需要这样做。或者我正在使用情节提要的“initWithNibName”,甚至是视图控制器的推送。除此之外代码是相同的,但我的不起作用。你知道为什么吗?这确实是个问题。
    • 你可以合成也可以不合成。如果你做不到,那不是问题。你必须使用 self.objecct 或 _objecct。如果你真的按照我的回答,它会帮助你。请好好阅读。
    • 您是否尝试过此代码。它甚至不编译。光是看我就想到了。 ViewControllerA 没有委托,即在 viewControllerB 中。所以我认为你试图设置一些不存在的东西。也许如果您调整我的解决方案,那么答案与我直接相关?谢谢。
    • 是的,我在我的项目中使用过。真的,我代表传递数据的纯粹示例。如果你看不懂这个例子,看谷歌:我如何在 ios 中使用委托:
    • 这个答案是错误的,因为它在 viewcontrollerA 中需要这个:@property (nonatomic, weak) id delegate;
    【解决方案2】:

    我会这样做。

    @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

    【讨论】:

    • 不应该在另一个视图控制器上捕获强引用。使用弱委托。此外,此代码未显示如何将数据从ViewControllerB 传递回ViewControllerA。甚至没有任何接近 OP 的问题。
    【解决方案3】:

    编辑: 使用上面@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;
        }
    

    【讨论】:

    • @TimWhiting 是的。这不是万无一失的解决方法。开发人员必须处理不同 ViewController 的所有情况。我会建议你去使用 Erhan 的解决方案。当我写这个答案时(大约 17 个月前),我是新手现在我有将近 2 年的经验,我不喜欢这种方法。我总是使用代表。有时也会通知。如果您有任何其他问题,请告诉我。
    【解决方案4】:

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

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

    阅读文档here。

    【讨论】:

      【解决方案5】:

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

      关于双向传递数据的完整答案是here。我解释委托模式的答案是here。

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

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

      在 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 类之外。

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

      【讨论】:

      【解决方案6】:

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

      在我的情况下发送回字符串。 在 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)
      }
      

      【讨论】:

      • 非常有帮助和直接!第一次尝试本身就可以工作。谢谢!
      • 我也想用这个——速度很快。但我也担心闭包中的内存泄漏问题。要解决关闭中的泄漏,您不能直接使用"self"。您必须首先创建一个弱自我/无主自我,然后将其转换为强自我,然后再在闭包中使用它。
      【解决方案7】:

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

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

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

      【讨论】:

      • 这肯定有效。但是 NSNotification 只是一个美化的事件总线单例。你可以将任何东西传递到任何地方。如果您想构建一个可扩展的系统,这将产生比以后解决的问题更多的问题。如果它们是视图控制器,我会坚持使用委托、关闭或展开 segue。
      【解决方案8】:

      有协议就有闭包。使用闭包,我们需要通过使用弱自我(或无主自我)来避免内存泄漏。使用协议,每个视图控制器都会有一个您想要“监控”的视图控制器,最终需要实现数十个委托。这里我在 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 视图控制器中,假设您通过情节提要中的 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
      • 处理数据输出就像prepare(for:sender:)中的数据输入一样

      【讨论】:

        【解决方案9】:
        //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.
        }
        
        }
        

        【讨论】:

          猜你喜欢
          • 2012-02-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-03-31
          • 1970-01-01
          相关资源
          最近更新 更多