I'm unable to pass data from one VC to another [duplicate] - ios

This question already has answers here:
Passing data with unwind segue
(3 answers)
Closed 5 years ago.
I'm passing the data from one VC back to the first VC. I'm using this code:
#IBAction func goBack(_ sender: Any) {
dismiss(animated: true, completion: nil)
print(self.entryField.text!)
performSegue(withIdentifier: "sendText", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let destVC = segue.destination as! ViewController
let printName = self.entryField.text!
print(self.entryField.text!)
destVC.nameToDisplay=printName
}
This is my code of the VC in which the data is.
The code of the VC in which I want to display my result.
var nameToDisplay = ""
override func viewWillAppear(_ animated: Bool) {
titleDisplay.text=nameToDisplay
}
I'm unable to pass the data, I tried printing the nameToDisplay but it gives empty string.

A reasonable pattern for passing back the value from second controller to the first one could be like this:
class FirstViewController: UIViewController {
//......
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let secondViewController = segue.destination as? SecondViewController {
secondViewController.nameHandler = { (name) -> Void in
titleDisplay.text=nameToDisplay //second controller will call back here to pass the name value when it's going back.
}
}
}
//......
}
class SecondViewController: UIViewController {
//......
var nameHandler:((_ name:String)->Void)? //a closure to call back name
#IBAction func goBack(_ sender: Any) {
if let name = self.entryField.text {
self.nameHandler?(name) //call back and pass the name to the first controller
}
dismiss(animated: true, completion: nil)
}
//......
}

You are looking for one to one communication between viewcontrollers. This can be achieved by different ways in iOS.
1- Delegation
2- blocks, closures.
The above solution is using block. I will tell you with delegates
class FirstVC: UIViewController, PassData {
func pushVC() {
let secondVC = SecondVC()
secondVC.delegate = self
self.navigationController?.pushViewController(secondVC, animated: true)
}
func passDataOnDismiss(data: String) {
print(data)
}}
protocol PassData: class {
func passDataOnDismiss(data: String)
}
class SecondVC: UIViewController {
weak var delegate: PassData?
#IBAction func didButtonPress() {
self.delegate?.passDataOnDismiss(data: "I am passing this string back to First VC")
self.navigationController?.popViewController(animated: true)
}
}

Related

I'm trying to use a protocol and delegate pattern which will pass the data in my array back to the parent view controller

I am new to Xcode and am trying to save an array from secondViewController into the View controller. I have a a series of view controllers embedded in a navigation controller so when I click 'back' on the navigation bar I want to keep the data that was collected in an array 'collectionArray' and save to 'collectionArray2' . Here is the protocol delegate method I've tried:
This is in my ViewController where I want the array saved to :
import UIKit
class ViewController: UIViewController {
var collectionArray2: [String] = []
override func viewDidLoad() {
super.viewDidLoad()
let controller = secondViewController()
controller.delegate = self
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let first = segue.destination as! secondViewController
first.collectionArray.append(contentsOf: collectionArray2)
}
}
extension ViewController: PopupDelegate {
func popupSelectedView(array: [String]) {
collectionArray2.append(contentsOf: array)
}
}
This is my secondViewController where I want to take 'collectionArray':
import UIKit
protocol PopupDelegate: class{
func popupSelectedView(array: [String])
}
class secondViewController: UIViewController {
var exerciseButton: String!
var collectionArray: [String] = []
weak var delegate: PopupDelegate?
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func Exercisess(_ sender: UIButton){
exerciseButton = sender.currentTitle
collectionArray.append(exerciseButton!)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
if self.isMovingFromParent {
delegate?.popupSelectedView(array: collectionArray)
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let second = segue.destination as! FinalViewController
second.cellLabelArray.append(contentsOf: collectionArray)
}
}
Thank you
your problem is that your are referring to a difference instance of secondViewController. Don't set your delegate in viewDidLoad but when you prepare the segue:
override func viewDidLoad() {
super.viewDidLoad()
//let controller = secondViewController() REMOVE THIS
//controller.delegate = self REMOVE THIS
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let first = segue.destination as! secondViewController
first.collectionArray.append(contentsOf: collectionArray2)
first.delegate = self // set the delate here
}
}
by the way, you should name all your classes starting with capital letters, so it should be SecondViewController

Swift delegate & protocols

For protocols and delegates to work, do the view controllers have to be adjacent in the stack?
The function called by my delegate was working fine until I inserted a new vc in between the vc sending the data and the vc receiving them. (If the answer is, "Of course, you idiot!" I'd actually be relieved as I'm really stumped.)
Presumably, you have something like this:
and your code runs along these lines:
protocol MyCustomDelegate: class {
func myFunc(_ value: Int)
}
class VC1: UIViewController, MyCustomDelegate {
func myFunc(_ value: Int) {
print("value: \(value)")
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let vc = segue.destination as? VC2 {
vc.delegate = self
}
}
}
class VC2: UIViewController {
weak var delegate: MyCustomDelegate?
func someAction(_ sender: Any?) -> Void {
delegate?.myFunc(1)
}
}
Now, you "insert a new VC":
So your code in VC1 is no longer segueing to VC2 ... and thus the delegate won't be set.
One approach, not necessarily the best, would be:
add a delegate var in VC1A, which is set by VC1
when navigating from VC1A to VC2, pass that delegate along
It could look something like this:
protocol MyCustomDelegate: class {
func myFunc(_ value: Int)
}
class VC1: UIViewController, MyCustomDelegate {
func myFunc(_ value: Int) {
print("value: \(value)")
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let vc = segue.destination as? VC1A {
vc.delegate = self
}
}
}
class VC1A: UIViewController {
weak var delegate: MyCustomDelegate?
func someAction(_ sender: Any?) -> Void {
delegate?.myFunc(1)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let vc = segue.destination as? VC1A {
vc.delegate = self.delegate
}
}
}
class VC2: UIViewController {
weak var delegate: MyCustomDelegate?
func someAction(_ sender: Any?) -> Void {
delegate?.myFunc(1)
}
}
If your app flow is simple enough, this would probably work... but, it's not ideal and is prone to problems as your code gets more complex.
You may want to re-think how your code is structured, and how you're trying to manage your data.

Form sheet is not getting detected when being swiped down

I have a parent view controller and a child view controller. I have a button that can show that child view controller. The button does these actions: self.performSegue(withIdentifier: "showComments", sender: self). I also have prepared the segue and set segue.destination.presentationController?.delegate = self. I have put UIAdaptivePresentationControllerDelegate into my view controller and tried putting this function:
public func presentationControllerDidDismiss(
_ presentationController: UIPresentationController)
{
print("dismissed")
}
When I try to drag down the form sheet it doesn't print anything telling me that something is wrong here. Is there a reason to why this is happening?
Edit: Here is my view controller:
import UIKit
class ViewController: UIViewController, UIAdaptivePresentationControllerDelegate {
func presentationControllerWillDismiss(_: UIPresentationController) {
print("yep2")
viewWillAppear(true)
}
#IBOutlet weak var commentImage: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
let vc = CommentingViewController()
vc.presentationController?.delegate = self
let tappp = UITapGestureRecognizer(target: self, action:#selector(ChannelVideoViewController.tapppFunction))
commentImage.addGestureRecognizer(tappp)
// Do any additional setup after loading the view.
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?)
{
if segue.destination is CommentingViewController {
if segue.identifier == "showComments" {
segue.destination.presentationController?.delegate = self
}
}
}
var isDismissed: Bool = true
#objc func tapppFunction(sender:UITapGestureRecognizer) {
isDismissed = true
self.performSegue(withIdentifier: "showComments", sender: self)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(true)
isDismissed = true
print("View Disappeared")
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
isDismissed = false
print("view appeared")
}
}
Edit 2: I'm not sure if this helps but I also have my child view controller in another storyboard.
Your issue is occurring probably because of the delegate is not getting set. Modify the prepare(for:,sender:) method to the following:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destination = segue.destination as? CommentingViewController {
destination.presentationController?.delegate = self
} else {
print("Not CommentingViewController")
segue.destination.presentationController?.delegate = self
}
}
In my original question I actually put a modified version of my view controller as it was pretty big and messy with other things. After looking at my prepare(for:,sender:) I realized that I was checking for the wrong view controller (Frakenstein kind of helped me find it). Here is what I'm talking about:
override func prepare(for segue: UIStoryboardSegue, sender: Any?)
{
if segue.destination is OtherViewController
{
if let vc = segue.destination as? OtherViewController {
if segue.identifier == "showOther" {
vc.other = self.other
}
} else if segue.destination is CommentingViewController {
if segue.identifier == "showComments" {
segue.destination.presentationController?.delegate = self
}
}
}
}
So it wasn't working too well. But it does work through storyboard references.

iOS Swift, with one common method in protocol and two implementations in two ViewControllers, how to decide which ViewControllers method is invoked?

I have a protocol
protocol HandleEmbedController: class {
func printMsg()
}
and 2 container views and 2 corresponding ViewControllers
class EnemyBaseVC: UIViewController, HandleEmbedController {
override func viewDidLoad() {
super.viewDidLoad()
}
var value1 = ""
func printMsg(){
print("printing some embedded message")
}
}
and
class EnemyBase2VC: UIViewController, HandleEmbedController {
func printMsg() {
print("enemy base 2 message")
}
override func viewDidLoad() {
super.viewDidLoad()
}
}
and both use the protocol HandleEmbedController and implement printMsg function.
In the main ViewController I have
class HomeBaseVC: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
var handleEmbedController:HandleEmbedController?
#IBAction func onclick(_ sender: UIButton) {
handleEmbedController?.printMsg()
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "embedseg"){
if let embed = segue.destination as? EnemyBaseVC {
self.handleEmbedController = embed
}
}
if (segue.identifier == "embedseg2"){
if let embed = segue.destination as? EnemyBase2VC {
self.handleEmbedController = embed
}
}
}
}
When button is clicked always EnemyBaseVC method is invoked and prints
printing some embedded message
Is there any way to decide which method is to be invoked?
UPDATE
If you have two container views, both segues will be triggered on load, and handleEmbedController will reference the one ViewController that is loaded last.
If you have some logic to decide which one should be referenced, then you can use it to decide which ViewController will be referenced, like so:
class HomeBaseVC: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
var handleEmbedController:HandleEmbedController?
// comes from your decision logic
var decisionMaker: Bool = false
#IBAction func onclick(_ sender: UIButton) {
handleEmbedController?.printMsg()
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "embedseg"),
let embed = segue.destination as? EnemyBaseVC,
decisionMaker {
self.handleEmbedController = embed
}
else if (segue.identifier == "embedseg2"),
let embed = segue.destination as? EnemyBase2VC,
!decisionMaker {
self.handleEmbedController = embed
}
}
}
be aware that this will set the handleEmbedController on load, if you need more complex behavior, you might as well handle the assignment of handleEmbedController elsewhere than in the segue.
Since this is a scenario where your base ViewController must communicate with more than one object, you can also use notifications instead of delegations. This way, you can decide which message to send when user taps the button. Your base ViewController would look something like this
class HomeBaseVC: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
// comes from your decision logic
var decisionMaker: Bool = true
#IBAction func onclick(_ sender: UIButton) {
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "BUTTON_WAS_TAPPED"), object: nil, userInfo: ["decision" : decisionMaker])
}
}
while the enemy ViewControllers would look like this (the second one would be the same except the decision value to handle)
class EnemyBaseVC: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(forName: NSNotification.Name(rawValue: "BUTTON_WAS_TAPPED"),
object: nil,
queue: nil) { notification in
if let userInfo = notification.userInfo,
let decision = userInfo["decision"] as? Bool,
decision {
self.printMsg()
}
}
}
var value1 = ""
private func printMsg(){
print("printing some embedded message")
}
}

Swift 1.2 - prepareForSegue targeting a View Controller embedded Navigation Controller

As in this question, I need to pass a value from a ViewController to another one, the second VC is embedded in navigation controller. I tried the answer to that question but value printed in console is always nil. Can't figure out what to do.
In First VC I have:
var dataToSend : String!
override func viewDidLoad() {
super.viewDidLoad()
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "toSecondVCSegue" {
println("prepareForSegue occurred test value is: *\(dataToSend)*")
let destinationNavigationController = segue.destinationViewController as! UINavigationController
let targetController = destinationNavigationController.topViewController as! SecondViewController
targetController.receivedTest = dataToSend
}
}
#IBAction func toSecondVCButtonTapped(sender: UIButton) {
performSegueWithIdentifier("toSecondVCSegue", sender: nil)
dataToSend = "passed"
}
in the second I have:
var receivedTest : String!
override func viewDidLoad() {
super.viewDidLoad()
println("in SecondViewController in viewDidLoad receivedTest is: *\(receivedTest)*")
}
override func viewWillAppear(animated: Bool) {
println("in SecondViewController in viewWillAppear receivedTest is: *\(receivedTest)*")
}
override func viewDidAppear(animated: Bool) {
println("in SecondViewController in viewDidAppear receivedTest is: *\(receivedTest)*")
}
I think, the reason is you set value to dataToSend variable after calling performSegueWithIdentifier and so it stays always nil
Try changing your code as :
#IBAction func toSecondVCButtonTapped(sender: UIButton) {
dataToSend = "passed"
performSegueWithIdentifier("toSecondVCSegue", sender: nil)
}
This may help!

Resources