Swift 1.2 - prepareForSegue targeting a View Controller embedded Navigation Controller - ios

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!

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

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.

Delegate method is not called swift ios?

We have two controller - ControllerA and ControllerB. Controller A contains the normal button and text field(mail id). When we enter the mailid and tap the button. we will present the ViewControllerB and we have an option called to change the email and click back. We are using a delegate to pass the viewControllerB value to ViewController. But delegate function is not called.
ViewControllerB :
protocol countryViewControllerDelegate{
func passMailId(code: String)
}
var delegate: countryControllerDelegate?
#IBAction func createNewFolder(_ sender: Any?) {
delegate?.countryCode(code: emailText.text)
self.dismiss(animated: true, completion: nil)
}
ViewControllerA :
var instance = ViewControllerB()
override func viewDidLoad() {
instance.delegate = self
}
func showCoutryPicker(){
self.performSegue(withIdentifier: "DropDown", sender: self)
}
extension ViewControllerA:countryViewControllerDelegate{
func countryCode(code: String) {
print(code)
}
}
Is any other way to fix this?
Your segue instance is different than the 1 here
var instance = ViewControllerB()
So you should either present
self.present(instance,animated:true,completion:nil)
OR
inside prepareForSegue
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "DropDown" {
let des = segue.destination as! ViewControllerB
des.delegate = self
}
}
Simply use prepare(for segue: ) check the code below,
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if(segue.identifier == "DropDown"){
let vc = segue.destination as! ViewControllerB
vc.delegate = self
}
}
The problem you had is that you are creating an instance from ViewControllerB
var instance = ViewControllerB()
And segue on the other hand, it wont work because it would be considered as a new instance rather than the segue destination.
You can use either delegate or instance in the below code. It will be useful for anyone:
ViewController :
class ViewController: UIViewController {
#IBOutlet weak var myTextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
myTextField.text = "Hello World"
// Do any additional setup after loading the view, typically from a nib.
}
// Without segue
#IBAction func passData(_ sender: Any) {
let sb = storyboard?.instantiateViewController(withIdentifier: "viewcontroller2") as! ViewController2
sb.passText = "Hello World"
//set self to Delegate
sb.delegate = self
//set self to Instance
sb.instance = self
present(sb, animated: true, completion: nil)
}
// With segue
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let sb = segue.destination as! ViewController2
sb.passTextSegue = "Hello World with segue"
//set self to Delegate
sb.delegate = self
//set self to Instance
sb.instance = self
}
}
extension ViewController : ViewController2Delegate{
func passValue(Str: String) {
print(Str)
}
}
ViewController2 :
protocol ViewController2Delegate : class {
func passValue(Str:String)
}
class ViewController2: UIViewController {
//Create instance for Delegate
weak var delegate : ViewController2Delegate?
//Create instance for ViewController
var instance: ViewController?
override func viewDidLoad() {
super.viewDidLoad()
myTextField.text = passText
myTextFieldSegue.text = passTextSegue
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBOutlet weak var myTextField: UITextField!
var passText: String?
#IBOutlet weak var myTextFieldSegue: UITextField!
var passTextSegue: String?
#IBAction func manage(_ sender: UIButton) {
//Pass value using Delegate
delegate?.passValue(Str: "Happy Coding~")
//Pass value using Instance
instance?.myTextField.text = "Happy Coding~ :)"
dismiss(animated: true, completion: nil)
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
}

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

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

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

Resources