I need to send some data back from secondView to First View by popView.
How can i send back the data by popViewControllerAnimated?
Thanks!
You can pass data back using delegate
Create protocol in ChildViewController
Create delegate variable in ChildViewController
Extend ChildViewController protocol in MainViewController
Give reference to ChildViewController of MainViewController when navigate
Define delegate Method in MainViewController
Then you can call delegate method from ChildViewController
Example
In ChildViewController: Write code below...
protocol ChildViewControllerDelegate
{
func childViewControllerResponse(parameter)
}
class ChildViewController:UIViewController
{
var delegate: ChildViewControllerDelegate?
....
}
In MainViewController
// extend `delegate`
class MainViewController:UIViewController,ChildViewControllerDelegate
{
// Define Delegate Method
func childViewControllerResponse(parameter)
{
.... // self.parameter = parameter
}
}
There are two options:
A) with Segue
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?)
{
let goNext = segue.destinationViewController as ChildViewController
goNext.delegate = self
}
B) without Segue
let goNext = storyboard?.instantiateViewControllerWithIdentifier("childView") as ChildViewController
goNext.delegate = self
self.navigationController?.pushViewController(goNext, animated: true)
Method Call
self.delegate?.childViewControllerResponse(parameter)
If you want to send data by popping, you'd do something like:
func goToFirstViewController() {
let a = self.navigationController.viewControllers[0] as A
a.data = "data"
self.navigationController.popToRootViewControllerAnimated(true)
}
Extending Dheeraj's answer in case your ViewController is not first VC in the stack, here is the solution:
func popViewController() {
guard let myVC = self.navigationController?.viewControllers.first({ $0 is MyViewController }) else { return }
myVC.data = "data"
self.navigationController?.popViewController(animated: true)
}
However, this solution will break if you have 2 or more than 2 MyViewController in the stack. So, use wisely.
Answer given here is a little complex, quite simple to just use UINavigationControllerDelegate
class FirstNavigationController: UIViewController {
var value: String?
}
class SecondNavigationController: UIViewController, UINavigationControllerDelegate {
func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) {
guard let vc = navigationController.topViewController as? FirstNavigationController else { return }
vc.value = "Hello again"
}
}
self.navigationController?.popViewController(animated: true)
let vc = self.navigationController?.viewControllers.last as! MainViewController
vc.textfield.text = "test"
this popviewcontroller solutions
Related
I have set a "Show as popover" segue between a UIView (A) and another UIView (B) (embed in a Navigation Controller) activated on a button's clic.
i am trying to pass datas back from (B) to (A) when i dismiss it (i want to keep the popover animation on both ways).
I have tried many methods i found mostly here, on Stackoverflow, but as of now i never successfully retrieved my data on (A).
I tried Delegates and protocols as well as other simpler methods. The last in date is the following one:
In (A), i just try to print the variable that should be storing the datas in ViewWillAppear :
class SearchBarsController: UIViewController, UITableViewDelegate, UITableViewDataSource, UISearchBarDelegate {
var testValue:String = ""
override func viewWillAppear(_ animated: Bool) {
print(testValue) // print is empty
super.viewWillAppear(animated)
}
}
In (B), i dismiss the popover and try to send the datas back on a button clic :
class SearchFilterViewController: UIViewController {
#IBAction func DismissPopoverOnClic(_ sender: Any) {
if let navController = presentingViewController as? UINavigationController {
let presenter = navController.topViewController as! SearchBarsController
presenter.testValue = "Test"
print("success") //never called
}
self.dismiss(animated: true, completion: nil)
}
}
on (B) i'd like to set up some filter that i'd use on (A) to present search results in a tableview. But actually the testValue's value is always blank.
oky so you can do it using unwind segue here is sample project :
sample projecct
process :
Add this method to SearchBarsController below viewWillAppear
#IBAction func unWindFromFilterViewController(_ sender: UIStoryboardSegue) {
}
Than go to Storyboard and go to SearchFilterViewController and then cntrl + Drag from DismissPopoverOnClic to top of the exit button then select unWindFromFilterViewController .
Than this the SearchFilterViewController write this method for passing data
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destVC = segue.destination as? ViewController {
destVC.testValue = "Test"
}
}
You will get your desired data back . thanks
When passing back data to a viewController, the most efficient way to implement it using delegate
protocol SearchFilterViewControllerDelegate {
func setTextValue(string : String)
}
class SearchFilterViewController: UIViewController {
var delegate : SearchFilterViewControllerDelegate?
#IBAction func DismissPopoverOnClic(_ sender: Any) {
self.dismiss(animated: true, completion: nil)
delegate?.setTextValue(string : "Test Value")
}
}
class SearchBarsController: UIViewController, UITableViewDelegate, UITableViewDataSource, UISearchBarDelegate {
var filterViewController : SearchFilterViewController?
func popup() {
// your pop up code and init filterViewController
filterViewController.delegate = self **//without this line, the delegate will be nil, no nothing will happen.**
}
}
extension SearchBarsController : SearchFilterViewControllerDelegate {
func setTextValue(string : String) {
print(string)
}
}
I have 2 UIViewControllers, ViewController, SecondViewController. I defined delegate function in VC, and using in Second VC. But delegate functions not calling in Second VC.
This is mu first VC code
import UIKit
//Step1:
protocol testDelegate {
func testFunction(string1: String, string2:String)
func math(a:Int, b:Int)
}
class ViewController: UIViewController {
//Step2:
var delegateVariable: testDelegate?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func moveToSecondVC(_ sender: Any) {
let nav = self.storyboard?.instantiateViewController(withIdentifier: "SVC") as! SecondViewController
//Step3:
delegateVariable?.testFunction(string1: "String1", string2: "String2")
delegateVariable?.math(a:30, b:10)
self.navigationController?.pushViewController(nav, animated: true)
}
}
My second VC code
import UIKit
//Step4:
class SecondViewController: UIViewController , testDelegate {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
//Step5:
let svc = ViewController()
svc.delegateVariable = self
}
#IBAction func btn(_ sender: Any) {
//Step5:
let svc = ViewController()
svc.delegateVariable = self
}
//Step6:
func testFunction(string1: String, string2: String) {
print(string1+string2)
}
func math(a:Int, b:Int) {
print(a+b)
print(a-b)
print(a*b)
}
}
Here i'm just passing small amount of data for practice, but can any one please suggest some high level delegate example tutorial links for me.
This is why nothing is happening...
let svc = ViewController()
svc.delegateVariable = self
You are creating a NEW ViewController, not using the one that is actually in use.
It does not look like you are using the delegate pattern properly. Your ViewController should not be calling code on other view controllers.
SecondViewController should "do stuff" and then let ViewController know what it has done.
For the Math function you could just use a new class (not a view controller) and create and use this as needed. You do not need a ViewController for this.
An example of using a delegate might be something like:
protocol CreateProfileDelegate: class {
func didCreateProfile(profile: Profile?)
func didCancelCreateProfile()
}
class ViewController: UIViewController {
func showCreateProfile() {
let vc = CreateProfileViewController()
vc.delegate = self
present(vc, animated: true)
}
}
extension ViewController: CreateProfileDelegate {
func didCreateProfile(profile: Profile?) {
// show the profile?
}
func didCancelCreateProfile() {
// show an alert maybe?
}
}
This way the SecondViewController (CreateProfileViewController) basically tells the first that something has happened so that it can react to it.
in SecondViewController you are setting....
let svc = ViewController()
svc.delegateVariable = self
That just create an object of ViewController() class and then you set the delegate. So when the obj. of the scope is finished then the memory of the object will be increased automatically.
The flow should like below....
Create an object of the Viewcontroller in SecondViewController and set the delegate
let vc = self.storyboard?.instantiateViewController(withIdentifier: "ViewController") as! ViewController
vc.delegateVariable = self
Then push the view controller in to the navigation stack.
self.navigationController?.pushViewController(svc, animated: true)
Implement the delegate method of testDelegate in SecondViewController
func testFunction(string1: String, string2: String) {
print(string1+string2)
}
func math(a:Int, b:Int) {
}
EDIT
The final code of the SecondViewController Will be...
import UIKit
class SecondViewController: UIViewController , testDelegate {
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func btn(_ sender: Any) {
let vc = self.storyboard?.instantiateViewController(withIdentifier: "ViewController") as! ViewController
vc.delegateVariable = self
self.navigationController?.pushViewController(svc, animated: true)
}
//MARK:- TestDelegate Methods
func testFunction(string1: String, string2: String) {
print(string1+string2)
}
func math(a:Int, b:Int) {
print(a+b)
print(a-b)
print(a*b)
}
}
I try to use delegate to reset my ViewControllerA (HomePage) property "type" value when I logout.
But I set breakpoint and my delegate function work success.
When I login again, and print my property "type" in ViewWillAppear. It's also cache old value before I logout.
Please tell me what's wrong with me.
Thanks.
class ViewControllerA: UIViewController, CustomDelegate {
enum Type: Int {
case book = 0
case pen
}
var tmpType: Type?
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
printBBLog("tmpType: \(tmpType)") //before I logout the value is "pen",and I login again the value is "pen".
}
func clearType() {
printBBLog("clear")
self.tmpType = nil
printBBLog("\(self.tmpType)")
}
#objc func bookBtnClicked(sender: UIButton) {
self.tmpType = .book
}
#objc func penBtnClicked(sender: UIButton) {
self.tmpType = .pen
}
}
class ViewControllerB: UIViewController {
var delegate: CustomDelegate?
func doLogout() {
let vc = ViewControllerA()
self.delegate = vc
self.delegate?.clearType()
}
}
You are creating a new instance of your ViewControllerA. since you are using the UITabBarController you can access you ViewControllerA from your ViewControllerB and assign the delegate. after that you will get your desired result. for reference please check below code.
class ViewControllerB: UIViewController {
var delegate: CustomDelegate?
func doLogout() {
let viewControllers = self.tabBarController?.viewControllers
if let vc1 = viewControllers[0] as? ViewControllerA {
self.delegate = vc1
self.delegate?.clearType()
}
}
}
if you are using UINavigationController inside the UITabBarcontroller then use:
if let vc1 = ((self.tabBarController?.viewControllers?[0] as? UINavigationController)?.viewControllers[0] as? ViewControllerA)
I have two UIViewController, when I click a button, it goes from the first view controller to the second one. And before that, I animated a UIView to move to another place. After dismissing the second View Controller, I want to move the UIView in the first view controller back to where it originally was. However, when I call a function from the second View Controller to animate the UIview in the first view controller after dismissing the second one, It could not get the UIView's properties, and cannot do anything with it. I think because the first UIViewController is not loaded yet. Is that the problem? And How should I solve this?
There are two solutions you can either use swift closures
class FirstViewController: UIViewController {
#IBAction func start(_ sender: Any) {
guard let secondController = self.storyboard?.instantiateViewController(withIdentifier: "SecondController") as? SecondController else { return }
secondController.callbackClosure = { [weak self] in
print("Do your stuff")
}
self.navigationController?.pushViewController(secondController, animated: true)
}
}
//----------------------------
class SecondController: UIViewController {
var callbackClosure: ((Void) -> Void)?
override func viewWillDisappear(_ animated: Bool) {
callbackClosure?()
}
}
or you can use protocols
class FirstViewController: UIViewController {
#IBAction func start(_ sender: Any) {
guard let secondController = self.storyboard?.instantiateViewController(withIdentifier: "SecondController") as? SecondController else { return }
secondController.delegate = self
self.navigationController?.pushViewController(secondController, animated: true)
}
}
extension ViewController : ViewControllerSecDelegate {
func didBackButtonPressed(){
print("Do your stuff")
}
}
//--------------------------
protocol SecondControllerDelegate : NSObjectProtocol {
func didBackButtonPressed()
}
class SecondController: UIViewController {
weak var delegate: SecondControllerDelegate?
override func viewWillDisappear(_ animated: Bool) {
delegate?.didBackButtonPressed()
}
}
You can try to use a closure. Something like this:
class FirstViewController: UIViewController {
#IBOutlet weak var nextControllerButton: UIButton!
private let animatableView: UIView = UIView()
private func methodsForSomeAnimation() {
/*
perform some animation with 'animatableView'
*/
}
#IBAction func nextControllerButtonAction() {
// you can choose any other way to initialize controller :)
let storyboard = UIStoryboard(name: "Main", bundle: nil)
guard let secondController = storyboard.instantiateViewController(withIdentifier: "SecondViewController") as? SecondViewController else { return }
secondController.callbackClosure = { [weak self] in
self?.methodsForSomeAnimation()
}
present(secondController, animated: true, completion: nil)
}
}
class SecondViewController: UIViewController {
#IBOutlet weak var dismissButton: UIButton!
var callbackClosure: ((Void) -> Void)?
#IBAction func dismissButtonAction() {
callbackClosure?()
dismiss(animated: true, completion: nil)
/*
or you call 'callbackClosure' in dismiss completion
dismiss(animated: true) { [weak self] in
self?.callbackClosure?()
}
*/
}
}
When you present your second view controller you can pass an instance of the first view controller.
The second VC could hold an instance of the first VC like such:
weak var firstViewController: NameOfController?
then when your presenting the second VC make sure you set the value so it's not nil like so:
firstViewController = self
After you've done this you'll be able to access that viewControllers functions.
iOS 11.x Swift 4.0
In calling VC you put this code ...
private struct Constants {
static let ScannerViewController = "Scan VC"
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == Constants.ScannerViewController {
let svc = destination as? ScannerViewController
svc?.firstViewController = self
}
}
Where you have named the segue in my case "Scan VC", this is what it looks like in Xcode panel.
Now in scan VC we got this just under the class declaration
weak var firstViewController: HiddingViewController?
Now later in your code, when your ready to return I simply set my concerned variables in my firstViewController like this ...
self.firstViewController?.globalUUID = code
Which I have setup in the HiddingViewController like this ...
var globalUUID: String? {
didSet {
startScanning()
}
}
So basically when I close the scanning VC I set the variable globalUUID which in term starts the scanning method here.
When you are saying it could not get the UIView's properties it's because you put it as private ? Why you don't replace your UIView in the first controller when it disappears before to go to your secondViewController. I think it's a case where you have to clean up your view controller state before to go further to your second view controller.
Check IOS lifecycle methods : viewWillDisappear or viewDidDisappear through Apple documentation and just do your animation in one of these methods.
Very simple solution actually... Just put your animation in the viewDidAppear method. This method is called every time the view loads.
class firstViewController: UIViewController {
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
// insert animation here to run when FirstViewController appears...
}
}
I've got two View Controllers. Main and Temporary one. The second one performs an action on the different screen (is called by pushViewController) and then I'm popping (popViewController) and would like to present the returned value which is String.
I've tried using protocol but it's nil.
Here is my code:
SecondVC.swift:
protocol ValueDelegate {
func append(_ text: String)
}
class SecondViewController: UIViewController{
var delegate: ValueDelegate!
...
...
private func function(){
if let delegate = self.delegate{
delegate.append(value.stringValue)
}
navigateBack()
}
private func navigateBack(){
if let navigation = self.navigationController{
navigation.popViewController(aniamted: true)
}
}
MainVC.swift:
class MainViewController: UIViewController, ValueDelegate {
var secondVC = SecondViewController()
...
func append(_ value: String) {
textField.text?.append(barcode)
}
...
override func viewDidLoad(){
super.viewDidLoad()
self.secondVC.delegate = self
}
}
Use these links to understand exactly how to use Protocols in swift:
Passing data between two ViewControllers (delegate) - Swift
Passing Data between View Controllers
You have to implement below line of code in first view controller :-
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showSecondViewController" {
let secondViewController = segue.destination as! SecondViewController
secondViewController.delegate = self
}
}
I've tried using protocol but it's nil.
Because you never set it to anything. It was your job, when you pushed the SecondViewController, to set its valueDelegate to the MainViewController. But you didn't.
What you did do was set the valueDelegate of another SecondViewController to the MainViewController:
var secondVC = SecondViewController()
self.secondVC.delegate = self
That was silly, because secondVC is a different, newly made instance of SecondViewController having nothing at all to do with your real interface. In particular, it is not the SecondViewController instance that gets pushed. But that is the instance you need to set the delegate of.