Unwind method doesn't work when using default backward button - ios

I have three view controllers like below
I wrote the unwind method in viewcontroller1, and try to receive some data from viewcontroller2 and viewcontroller3 when they unwind to viewcontroller1.
#IBAction func unwindToViewController1(segue: UIStoryboardSegue) {
print("1")
main_content = (segue.source as! MainContentViewController).main_content
}
#IBAction func unwindToViewController2(segue: UIStoryboardSegue) {
print("2")
detailed_content = (segue.source as! SupplementContentViewController).supplement
}
And set the exit unwind segue for both controller 2 and 3 already.
But why the unwindToViewController methods never get called correctly? I think they should be called when I click the button automatically created by the system.

I solve this problem by using delegate instead of unwind. Delegate pattenr is a more explicit way to solve this problem.
By creating a protocol called myDelegate
protocol myDelegate {
func updateMaincontent(main_content : String)
func updateSupplement(supplement: String)
}
And create a delegate instance inside the second view controller
var delegate: myDelegate?
Then in the first view controller, make the class extend myDelegate and set this delegate of second view controller to self in the func prepare(for segue: UIStoryboardSegue, sender: Any?) method
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destinationViewController = segue.destination as? MainContentViewController {
destinationViewController.main_content = self.main_content
destinationViewController.delegate = self
}
else if let destinationViewController = segue.destination as? SupplementContentViewController {
destinationViewController.supplement = self.detailed_content
destinationViewController.delegate = self
}
}
Finally, go back to second view controller, and set the value of delegate to what you want in viewWillDisappear method.
func viewWillDisappear(_ animated: Bool) {
self.main_content = contentTextView.text
delegate?.updateMaincontent(main_content: contentTextView.text)
}

Related

Segue and delegates in UIKit

I have 2 Controllers: MainVC and SideMenuVC.
I wanted to modify MainVC using SideMenuVC, so created delegate of SideMenuVC ( as well, there's Emdebed segue "name..." to "Side Menu View Controller" on storyboard because MainViewController has subView, which contains ContainerView - this container is our SideMenuVC. And this delegate works as he should.
However, due to logic in the app, I also need to send data from MAINVC to SIDEMENUVC.
So i did the same - created another delegate of second VC... But I turned out, MainViewControllerDelegate is not responding in SideMenuViewController. And i'm absolutely clueless...
Yes, i do implement necessary protocols in both classes, in extension!
Code of both VCs below, screens of storyboard in the attachment
MainViewController + MainViewControllerDelegate
protocol MainViewControllerDelegate{
func isImageLoaded(_ isLoaded:Bool)
}
class MainViewController: UIViewController {
/* ... */
var delegate: MainViewControllerDelegate?
var sideMenuViewController: SideMenuViewController?
private var isSideMenuPresented:Bool = false
private var isImageLoaded:Bool = false
override func viewDidLoad() {
super.viewDidLoad()
self.isImageLoaded = false
self.setupUI()
self.delegate?.isImageLoaded(false)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "MainVC_SideMenuVC_Segue")
{
if let controller = segue.destination as? SideMenuViewController
{
self.sideMenuViewController = controller
self.sideMenuViewController?.delegate = self
}
}
}
/* ... */
//I'm using PHPicker, and when new image is selected, i want to send "true" via delegate
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
dismiss(animated: true)
if let itemProvider = results.first?.itemProvider, itemProvider.canLoadObject(ofClass: UIImage.self){
let previousImage = self.presentedImage.image
itemProvider.loadObject(ofClass: UIImage.self){ [weak self] image, error in
DispatchQueue.main.async {
guard let self = self, let image = image as? UIImage, self.presentedImage.image == previousImage else {
return
}
self.presentedImage.image = image
self.isImageLoaded = true;
self.delegate?.isImageLoaded(true)
}
}
}
}
SideMenuViewController + SideMenuViewControllerDelegate
protocol SideMenuViewControllerDelegate{
func hideSideMenu()
func performAction(_ type:OperationType)
}
class SideMenuViewController: UIViewController {
/*...*/
var delegate: SideMenuViewControllerDelegate?
var mainViewController: MainViewController?
private var menuData: [ExpandingCellModel] = []
private var isImageLoaded: Bool = false
override func viewDidLoad() {
super.viewDidLoad()
// self.mainViewController?.delegate = self
menuData = setupData()
setupUI()
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let controller = segue.destination as? MainViewController {
controller.delegate = self
}
}
}
/* ... */
Here is what I think is happening.
There is segue happening from MainVC to SideMenuVC but there is no segue actually happening between SideMenuVC to MainVC in my opinion.
Happening is keyword because there is an EmbedSegue from MainVC to SideMenuVC but where is the segue from SideMenuVC to MainVC ? You did some connection in storyboard but nothing is happening in my opinion.
That is why in override func prepare is being called as planned in MainViewControllerDelegate and the delegate is getting set but it is not getting set in SideMenuViewController since override func prepare doesn't get called as no segue happens.
What you can do instead which might work is set both the delegates inside prepare in MainViewControllerDelegate
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "MainVC_SideMenuVC_Segue")
{
if let sideMenuVC = segue.destination as? SideMenuViewController
{
sideMenuVC.delegate = self
// assign MainViewControllerDelegate here
self.delegate = sideMenuVC
}
}
}
Check now if data is sent back to main view controller also.
If your issue is still not yet solved, please have a look and try this small example I set for you in github to show passing data between mainVC and embeddedVC and it might give you some hints.
You can see the result of this in action here: https://youtu.be/J7C7SEC04_E

how pass data between view controller and TableViewController when using dismiss page that opened with performSegue in swift 4?

I used this code here to pass data from first view controller to the second view controller
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let vc = segue.destination as? secondViewController {
vc.showPageType = self.checkEdit
}
But the problem is that in the second view controller I have text field that when user fill that text field and push the button submit the secondViewController will be dismiss with this method
dismiss(animated: false, completion: nil)
and now I can't use perform segue method to pass textfield text to the first view controller how can I do that in swift4?
Add to your secondViewController source code file:
protocol SecondViewControllerDelegate {
func submitButtonPushedWithText(_ text: String)
}
Add to class secondViewController property:
var delegate: SecondViewControllerDelegate?
Then conform your first controller to SecondViewControllerDelegate and implement method submitButtonPushedWithText(:):
class FirstViewController: UIViewController, SecondViewControllerDelegate {
func submitButtonPushedWithText(_ text: String) {
// use text from textField of second controller
}
}
Also setup delegate property of second controller before presenting:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let vc = segue.destination as? secondViewController {
vc.showPageType = self.checkEdit
// setup delegate
vc.delegate = self
}
Now you can call method submitButtonPushedWithText(_ text: String) in your Second controller just before calling dismiss(animated: false, completion: nil):
func submitButtonPushed() {
delegate?.submitButtonPushedWithText(textField.text!)
dismiss(animated: false, completion: nil)
}

does Prepare For Segue work when data send by second view controller

i'm new to ios development
few days back when i was learning how to send data from one VC to anotherVC then i used
override func prepare(for segue: UIStoryboardSegue, sender: self) {
if segue.identifire == "segue1" {
let data = segue.destinetion as! secondViewController
data.labelName = labelFirst.text
}
and now when in receiving data from secondVC then i also using almost same code
as i saw on web
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "segue2" {
let secondVC = segue.destination as! secondViewController
secondVC.delegate = self
}
}
so plz anyone can explain real quick why does it look same
or what's difference
To send data back from secondVC to firstVC rather use delegates. Based on the line "secondVC.delegate = self" that you wrote, you already read an article on this and might just need some additional info.
In secondVC put the following code outside of the class scope. for example above
class secondVC: UIViewController {}
The code to place there is
protocol secondVCDelegate {
func didFinishTask(returnData: String)
}
Put the following line inside of the class scope of secondVC
var delegate: secondVCDelegate?
Then in firstVC inherit the secondVC delegate by adding secondVCDelegate to the class. (e.g: class firstVC: UIViewController, secondVCDelegate {})
Then add the function to your firstVC
func didFinishTask(returnData: String) {
//Do something here with returnData
print(returnData)
}
Hope this helps!

Pass data before popViewController without segue and storyboard

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.

Pass data backward from detailViewController to masterViewController

I am trying to pass data back from the second viewController.
I can do that without NavigationController. But now I need to use NavigationController. Then my code does work as before. The data wont pass.
Here is the simple code:
In first viewController
class ViewController: UIViewController, backfromSecond {
#IBOutlet weak var text: UILabel!
var string : String?
override func viewDidLoad() {
super.viewDidLoad()
self.string = "Start here"
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
self.text.text = self.string
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destinationViewController = segue.destination as? secondViewController{
destinationViewController.delegate = self
}
}
func back(text: String) {
self.string = text
print(text)
}
}
And Second viewController:
protocol backfromSecond {
func back(text: String)
}
class secondViewController: UIViewController {
var string : String = "nothing here"
var delegate : backfromSecond?
override func viewDidLoad() {
super.viewDidLoad()
delegate?.back(text: string)
// Do any additional setup after loading the view.
}
}
What is wrong here?
Suppose A & B are two controllers and you first navigated from A to B with some data. And now you want to POP from B to A with some data.
Unwind Segues is the best and recommended way to do this.
Here are the steps.
Open A.m
define following method
#IBAction func unwindSegueFromBtoA(segue: UIStoryNoardSegue) {
}
open storyboard
Select B ViewController and click on ViewController outlet. press control key and drag to 'Exit' outlet and leave mouse here. In below image, selected icon is ViewController outlet and the last one with Exit sign is Exit Outlet.
You will see 'unwindSegueFromBtoA' method in a popup . Select this method .
Now you will see a segue in your view controler hierarchy in left side. You will see your created segue near StoryBoard Entry Piont in following Image.
Select this and set an identifier to it. (suggest to set the same name as method - unwindSegueFromBtoA)
Open B.m . Now, wherever you want to pop to A. use
self.performSegueWithIdentifier("unwindSegueFromBtoA", sender: dataToSend)
Now when you will pop to 'A', 'unwindSegueFromBtoA' method will be called. In unwindSegueFromBtoA of 'A' you can access any object of 'B'.
That's it..!
I think your problem is in the prepare for segue method. If the view controller is on a navigation stack i think your code should be something like
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destinationViewController = segue.destination as? UINavigationController).topViewController as! secondViewController{
destinationViewController.delegate = self
}
}
You can use unwind segues to pass data back.
Here's a tutorial
https://spin.atomicobject.com/2014/10/25/ios-unwind-segues/
This works me well.
1st VC
class ViewController: UIViewController, backfromSecond {
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func Passingfrom1stVCTo2ndVC(_ sender: AnyObject) {
if let vc = self.storyboard?.instantiateViewController(withIdentifier: "ViewController3") as? ViewController3{
vc.dataFrom1StVC = "message send from 1st VC"
vc.delegate = self
self.navigationController?.pushViewController(vc, animated: true)
}
}
func back(text: String) {
print("data\(text)")
}
}
2nd VC.
protocol backfromSecond: class {
func back(text: String)
}
class ViewController3: UIViewController {
var dataFrom1StVC : String? = nil
week var delegate : backfromSecond?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func DataSendFrom2ndVCTo1stVC(_ sender: AnyObject) {
self.delegate?.back(text: "Message Send From 2nd vc to 1st VC")
self.navigationController?.popViewController(animated: true)
}
}
I hope it will work you. If any problem then ask me i will help you.

Resources