Segue and Unwind segue without UI element - swift

I have been looking for an answer for this on SO without success.
I have UIViewController A and B. They are NOT linked in Storyboard, and I want to perform a Segue from A to B, and upon clicking a button in B, to activate an unwindSegue to A.
So far I have :
1) The #IBAction in A to unwind back to
2) The function to prepare the Segue from A to B (In A)
3) The button to call the unwindSegue from B to A
What I'm missing is the logic of the function in 2.
More info about B:
Title: FilterView
Class: FilterViewController
Storyboard ID: FilterView
I tried creating a segue:
let segueToFilter = UIStoryboardSegue(identifier: "SegueToFilterView", source: self, destination: FilterViewController)
Thinking this might just get me what I need.
but I get this error:
Cannot convert value of type 'FilterViewController.Type' to expected argument type UIViewController
Help would be appreciated :)

I still do not understand how segues are causing problems to you, but if you really really don't want to use segues, you can try presenting and dismissing VCs programmatically.
The idea goes like this:
Create an instance of your VC
Pass data to it by setting some properties
Present!
In the VC, dismiss.
The actual code will be different if you embedded your VCs in a navigation controller. Instead of calling present and dismiss, you would call pushVC and popVC on the navigation controller.
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "FilterView") as FilterViewController
vc.someProperty = someData // this is where you pass data
vc.present(vc, animated: true, completion: nil)
To pass data back, you should use the delegate pattern by creating a FilterViewDelegate that the first VC conforms to. Look up on Google for more info.

Related

What is the simplest way to modify variable by using the Button for later use of that variable in another View Controller?

I have two view controllers. One VC with a button and a label and one VC only with a label. I created button action that can modify variable (declared in the class of that first viewcontroller) if the button is pressed and I can show that MODIFIED variable as the label in that same VC but my intent is to show that MODIFIED variable on another VC.
I want to use that button to go to the second VC where the modified variable should be displayed on the label when that VC is displayed. In first VC I declared:
var myVariable: String = "default"
In the button action:
{myVariable = "700"}
In second VC I declared:
let vc1: ViewController1 = ViewController1()
I assigned an action to the button in first VC (dragging to second VC to show the second VC) and in viewdidload of the second VC I try to acces the variable from first VC with:
label.text = vc1.myVariable and it accesses it but ...
...but the result is DECLARED/notMODIFIED state of variable. So if I show myVariable on same VC it is OK but on the second VC myVariable was not changed by the button action on the first VC.
I am trying to avoid delegation as it seems complicated..
What is the easiest way to accomplish task? Thank you guys.
To get updated value of first VC variable to second VC you can use NSNotificationCenter.
In first VC when you change value at that time you have to post notification with dictionary like following.
NotificationCenter.default.post(name: NSNotification.Name("ValueUpdate"), object: nil, userInfo: ["variable": myVariable])
For that you have to observe Notification in second VC
NotificationCenter.default.addObserver(self, selector: #selector(handleNotification(notification:)), name: Notification.Name("ValueUpdate"), object: nil)
And then you have to implement following method to get update in your second VC
#objc func handleNotification(notification: Notification) {
label.text = notification.userInfo?["variable"]
}
The SIMPLEST way to do this is to define a global variable. So you can access it from anywhere. It should be define outside of anything to be global.
#import UIKit
var myGlobalVariable = "Initial value"
class MyClass { ... }
then you can change or read it from anywhere like print(myGlobalVariable).
Note that although it is the simplest solution, it is also THE WORST solution too. Global functions and variables use cases are vary rare.
Inject your data via initializer while showing controller.
show(SecondViewController(name: name), sender: nil)
Check out this code.
#Eljer...
Although Delegation seems VERY hard at first its very easy.
You need to remember 3 steps only as a beginner ! I'll try to make it easy for you to use here.
Step 1 : Create tempVariable (of same type) on second View controller also (you can make with same name also doesnt matter)
Step 2 : Write this code:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let xyz = segue.destination as! abc
}
The xyz is the name you want to call it with.
The abc is the name of your second view controller where you made the other same type of variable.
What this will do is, it will make an object of EVERYTHING (that includes all variables and functions) that you have on your Second View controller available to you on your First view controller also.
To use / assign value to the label on second view controller what you need to do is...
just add :
xyz.yourVariableNameOnSecondViewController = variableNameOnFirstViewController
this will assign the value of your variable on First VC to the variable on Second VC also
Finally, to get the value updated. You need to get this updated value using viewDidLoad on your second view controller.
I hope this helps, if its still confusing ask me anything.
Gluck

Programmatically press back button for UIViewController with UITableView iOS swift

I have a UIViewController that implements UITableViewDelegate, UITableViewDataSource and that contains a UITableView as a member variable. When a user click on one of the rows of that table, the app performs a storyboard segue to open the detail view controller. That detail view controller of course has a button in the top left of the screen that is the "back" button to go back up to the UIViewController with the UIViewTable.
So, suppose that I want to programmatically "click" that back button. How exactly would I do that in swift? This is the most recent version of swift (swift 4?) in XCode 10.1.
UPDATE:
So here is how I solved this. As the answers below show, it is possible to use self.navigationController?.popViewController(animated: true) to just return to the previous view controller. What I discovered I also wanted to do, however, was to call a specific method in that view controller so that it executed a certain behavior once it got shown. It turns out that is also possible, but in my case it was a bit tricky, since that prior view controller was actually a UITabBarController. Therefore I had to get the ViewController that I was interested in from the UITabBarController. I did it like this:
let numvc = navigationController!.viewControllers.count
let tvc:UITabBarController = navigationController!.viewControllers[numvc-2] as! UITabBarController
let my_vc: MyCustomVC = tvc.viewControllers![0] as! MyCustomVC
my_vc.some_function()
Here of course MyCustomV is my custom view controller class and some_function() is the method I want to call on that class. Hope this helps someone.
When You run a segue you perform a "pushViewController" method to the next view, so if you want to go back to the previous view programmatically you just have to do is pop the last view like so:
self.navigationController?.popViewController(animated: true)
UPDATE
You just need the if statement if you have multiple segues from that viewController, if not, you can delete and just cast the next view as you wish and set the properties, let the autocomplete write the *prepare(for segue... * method for you, so You don't run into any problems
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "yourSegueName" {
let destinationVC = segue.destination as! CustomViewController
destinationVC.labelExample.text = "Some text I'm sending"
}
}
Are you sure you need to "click" the button?
If all you need is to dismiss details view controller, you can just call navigationController?.popViewController(animated: true)
Or if you want to deal directly with button, you can tell it to send its actions: backButton.sendActions(for: .touchUpInside)
Or if you absolutely need to show button clicking animation, then you will need something like this (you should play and choose suitable delay):
backButton.isHighlighted = true
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.3) {
backButton.isHighlighted = false
backButton.sendActions(for: .touchUpInside)
}

Performing segue with UINavigationController (without IBAction)

It's easier to show you a drawing and then explain.
Dashboard Storyboard
I have 2 separate UIViewControllers (i've included just one in the drawing, the other is irrelevant) embedded in container view called ContainerViewController.
Post Storyboard
NewPostViewController shows a UIButton that presents TextPostViewController. As you can see, all of them are embedded in UINavigationControllers. Now, once the completion block of the new post is being called, I have to present the ContainerViewController and it needs to handle it's own logic. The problem is that it's embedded in UINavigationController and once I present it, the UITaBbar is hidden.
I tried to do this:
self.performSegue(withIdentifier: "TextPostToNavContainerVC", sender: nil)
The transition is successful but I'm losing the UITabBar, even though in the DashboardViewController and the ContainerViewController I called:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
tabBarController?.tabBar.isHidden = false
}
What am I doing wrong or is there are better way to do that?
You should instantiate the tab bar controller. not the view controller.
Imagine you're putting a initial view controller ahead of your tab bar controller. Making your tab bar not being pushed
If I undestand it correctly.
You are doing this
Segue connect to a view controller
But you should actually do this Segue connected to a tab bar controller
You can try to add it as a child to control it's frame like this
let textPost = self.storyboard?.instantiateViewController(withIdentifier: "containerID") as! TextPostToNavContainerVC
textPost.view.frame = CGRect(x:20,y:0,width:self.view.frame.width,height:self.view.frame.height-50)
self.view.addSubview(nvc.view)
self.addChildViewController(textPost)
textPost.didMove(toParentViewController: self)

Opening window + view from an other view controller

I've got a ViewControllerOne. ViewControllerOne is connected via Ctrl-Drag (in storyboard) to a menu-button mBtn (which means I don't know how it is implemented programmatically).
Clicking on this mBtn, a ViewOne appears (present modal). This ViewOne is bound to ViewControllerOne. ViewOne has a button btnOne.
Clicking on btnOne I want ViewOne to be dismissed and ViewTwo to be shown. ViewTwo belongs to ViewControllerTwo and to WindowControllerTwo.
The WindowControllerTwo-ViewControllerTwo-binding is the standard case as created on a new project.
I have the following code in the IBAction for button btnOne in ViewControllerOne:
#IBAction func onbtnOnePressed(sender: AnyObject){
let m_WindowControllerTwo = NSStoryboard(name: NSStoryboard.Name(rawValue: "Main"), bundle: nil).instantiateController(withIdentifier: NSStoryboard.SceneIdentifier("WindowControllerTwo")) as! NSWindowController // I have no custom class for the window controller as I don't really know what I can use it for ...
let m_ViewTwo = WindowControllerTwo.contentViewController as! ViewControllerTwo // my custom class for ViewTwo
m_ViewTwo.attributeIWantToPassToThisView = self.x // an attribute I want to pass from view a to view b
m_WindowControllerTwo.contentViewController = m_ViewTwo // passing the attribute from a to b
m_WindowControllerTwo.showWindow(self) // this does not work
self.dismiss(nil) // see NOTE
}
This code actually does not work. On debugging it step by step, I'm seeing the window/view flickering but not appearing...
NOTE: I could connect the button btnOne with a ctrl-drag to ViewControllerTwo. This works. But then the current ViewOne does not get dismissed!
Question: What am I doing wrong here? In iOS swift this also works. I don't quite get the WindowController stuff, so I'll need your advice on this.
Instead of this: m_WindowControllerTwo.showWindow(self)
use:
let application = NSApplication.shared()
application.runModal(for: wordCountWindow) //this will present WindowControllerTwo modally.
then to close your present controller add this line: PresentWindowControllerName.close()

How do I segueing to the UIViewController I'm in?

I'm trying to go from one view, filled with data from an object, to the same view but filled with a different object via segue.
Using a segue is necessary as apposed to switching the object and refreshing the view because my users need to be able to go back to the past view controller when they hit the back button.
Example:
(ThisView, with thisObject populating the view) -> (ThisView, with thisOtherObject populating the view)
What I've tried:
presentView Controller: This didn't work because it is not the default segue I'm trying to achieve.
let next: NewClubProfile = storyboard?.instantiateViewControllerWithIdentifier("clubProfile") as! NewClubProfile
presentViewController(next, animated: true, completion: nil)
Using Navigation Controller: Can't figure out how to segue the new object to populate the other view
let next: NewClubProfile = storyboard?.instantiateViewControllerWithIdentifier("clubProfile") as! NewClubProfile
self.navigationController?.pushViewController(next, animated:true)
Should have taken longer to solve this myself before posting this but I couldn't find an answer that fit what I needed. Anyway, solved it, here's what worked for me.
let next: ViewController = storyboard?.instantiateViewControllerWithIdentifier("viewName") as! ViewController
next.object = newObject
self.navigationController?.pushViewController(next, animated:true)