How to pass data back to the first view controller in Swift? - ios

I have two view controllers and I have this code to navigate between the two views
ViewController:
func goToSecondViewController() {
let aa = self.storyboard.instantiateViewControllerWithIdentifier("SecondViewController") as SecondViewController
self.navigationController.pushViewController(aa, animated: true)
}
/
SecondViewController:
func goToFirstViewController() {
self.navigationController.popToRootViewControllerAnimated(true)
}
How to send some data to the first view before popping it ?

If A is a view controller with the following declaration:
class A : UIViewController {
var data: String!
}
Then, whenever you have an instance of A, you can just set the data property directly:
let a = A() // assuming you've defined the init method
a.data = "hello"
Edit
If you want to send data before popping, you'd do something like:
func goToFirstViewController() {
let a = self.navigationController.viewControllers.first as! A
a.data = "data"
self.navigationController.popToRootViewControllerAnimated(true)
}
(haven't compiled the above)

Related

How to move from one NS View controller to another while closing the previous view controller in OS X swift?

I am on NSView Controller B where I came from A using
**let vc = RegisterViewController.registerViewController()
vc.title = "Sign up"
self.presentViewControllerAsModalWindow(vc)**
Now from B, I want to go to C while closing A as my B is opened in separate window.
I have wrote an extension for this, you can close any view controller using it:
extension NSApplication{
static func closeWindow(withVCType vcType: NSViewController.Type)
{
// Find the window of desired view controller by comparing class name
if let windowOfA = shared.windows.first(where: {
$0.contentViewController?.className == vcType.className()
}){
// Close it
windowOfA.close()
}
}
}
So in order to close let say view controller A, call it like this:
NSApplication.closeWindow(withVCType: A.self)
Another Thing that worked for was this solution.
if let window = NSApplication.shared.mainWindow {
let VC = ViewController.VC()
window.contentViewController = VC
if let windowController = window.windowController as? homeWindow {
}
}
self.view.window?.close()
Where the function VC() is.
class func VC() -> ViewController {
return NSStoryboard(name: NSStoryboard.Name(rawValue: "Main"), bundle: nil).instantiateController(withIdentifier: NSStoryboard.SceneIdentifier(rawValue: "VC")) as! ViewController
}

passing data between view controllers without changing views

I want to pass data between two view controllers, but don't want the view to change when the users presses my save data button.
The users needs to fill in multiple data fields, and when finish can press another button to go to the second view controller.
I found many tutorials how to pass data using segue, but they all change view as soon as the 'save button is pressed'.
Any one can explain to me how to alter the code?
#Phillip Mills: here is how I used your code. (what am I doing wrong?)
code:
//////// declaring classes on FirstViewController (trying it first on only one ViewController)
class FakeVC1 {
func userInput() {
DataModel.shared.username = outbj14u.text
}
class FakeVC2 {
func viewAppears() {
if let name = DataModel.shared.username {
outbj14p.text = name
print("I have nothing to say")
}
}
}
class DataModel {
static let shared = DataModel()
var username: String?
}
////till here
//// here is where i call the functions
override func viewDidAppear(_ animated: Bool) {
FakeVC1().userInput()
FakeVC2().viewAppears()
if let xbj14p = UserDefaults.standard.object(forKey: "outbj14p") as? String
{
outbj14p.text = xbj14p
}
if let xbj14u = UserDefaults.standard.object(forKey: "outbj14u") as? String
{
outbj14u.text = xbj14u
}
////
#Phillip Mills: Below is what I have know. I think I got the code on the FirstViewController right, but the code on the Second View controller must be wrong. I don't get any errors, but the text field on the SecondViewController remains unchanged after putting input on in the FirstViewController
//// Code on the FirstViewController
class DataModel {
static let shared = DataModel()
var username: String?
}
#IBAction func savebj14p(_ sender: Any) {
outbj14p.text = inbj14p.text
DataModel.shared.username = outbj14p.text
UserDefaults.standard.set(inbj14p.text, forKey: "namebj14p")
}
//and on the SecondViewController
#IBOutlet weak var bj14u: UILabel! // connected to a label
//and
class DataModel {
static let shared = DataModel()
var username: String?
}
override func viewDidAppear(_ animated: Bool) {
if let name = DataModel.shared.username {
bj14u.text = name
}
}
In your case, don't pass data.
Create a shared object to act as your data model. When users fill in the fields, update the data model.
When the user moves to the second controller/view, that controller uses the data model object to show what it needs to.
class FakeVC1 {
func userInput() {
DataModel.shared.username = "Me"
}
}
class FakeVC2 {
func viewAppears() {
if let name = DataModel.shared.username {
print(name)
} else {
print("I have nothing to say")
}
}
}
class DataModel {
static let shared = DataModel()
var username: String?
}
FakeVC1().userInput()
FakeVC2().viewAppears()
If you need to pass value to another viewcontroller without changing the view , you can user NSNotificationCenter class
Refer this link for more details
NSNotificationCenter addObserver in Swift
what i will recommend is to use a global variable or array, you will have the info in all view controllers and you will be able to call it in your new view controller.

Swift 3 : Back to last ViewController with sending data [duplicate]

This question already has answers here:
Passing data between view controllers
(45 answers)
Closed 5 years ago.
I'm trying to go back to my las viewController with sending data, but it doesn't work.
When I just use popViewController, I can go back to the page, but I can't move my datas from B to A.
Here is my code :
func goToLastViewController() {
let vc = self.navigationController?.viewControllers[4] as! OnaylarimTableViewController
vc.onayCode.userId = taskInfo.userId
vc.onayCode.systemCode = taskInfo.systemCode
self.navigationController?.popToViewController(vc, animated: true)
}
To pass data from Child to parent Controller, you have to pass data using Delegate pattern.
Steps to implement delegation pattern, Suppose A is Parent viewController and B is Child viewController.
Create protocol, and create delegate variable in B
Extend protocol in A
pass reference to B of A when Push or Present viewcontroller
Define delegate Method in A, receive action.
After that, According to your condition you can call delegate method from B.
You should do it using delegate protocol
class MyClass: NSUserNotificationCenterDelegate
The implementation will be like following:
func userDidSomeAction() {
//implementation
}
And ofcourse you have to implement delegete in your parent class like
childView.delegate = self
Check this for more information
https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Protocols.html
You have to send back to last ViewController with 2 options.
1. Unwind segue. (With use of storyboard)
You can refer this link.
2. Use of delegate/protocol.
You can refer this link.
Also this link will be useful for you.
You can use Coordinator Pattern
For example, I have 2 screens. The first displays information about the user, and from there, he goes to the screen for selecting his city. Information about the changed city should be displayed on the first screen.
final class CitiesViewController: UITableViewController {
// MARK: - Output -
var onCitySelected: ((City) -> Void)?
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
onCitySelected?(cities[indexPath.row])
}
...
}
UserEditViewController:
final class UserEditViewController: UIViewController, UpdateableWithUser {
// MARK: - Input -
var user: User? { didSet { updateView() } }
#IBOutlet private weak var userLabel: UILabel?
private func updateView() {
userLabel?.text = "User: \(user?.name ?? ""), \n"
+ "City: \(user?.city?.name ?? "")"
}
}
And Coordinator:
protocol UpdateableWithUser: class {
var user: User? { get set }
}
final class UserEditCoordinator {
// MARK: - Properties
private var user: User { didSet { updateInterfaces() } }
private weak var navigationController: UINavigationController?
// MARK: - Init
init(user: User, navigationController: UINavigationController) {
self.user = user
self.navigationController = navigationController
}
func start() {
showUserEditScreen()
}
// MARK: - Private implementation
private func showUserEditScreen() {
let controller = UIStoryboard.makeUserEditController()
controller.user = user
controller.onSelectCity = { [weak self] in
self?.showCitiesScreen()
}
navigationController?.pushViewController(controller, animated: false)
}
private func showCitiesScreen() {
let controller = UIStoryboard.makeCitiesController()
controller.onCitySelected = { [weak self] city in
self?.user.city = city
_ = self?.navigationController?.popViewController(animated: true)
}
navigationController?.pushViewController(controller, animated: true)
}
private func updateInterfaces() {
navigationController?.viewControllers.forEach {
($0 as? UpdateableWithUser)?.user = user
}
}
}
Then we just need to start coordinator:
coordinator = UserEditCoordinator(user: user, navigationController: navigationController)
coordinator.start()

Using prepareForSegue to pass data to ViewController later in app

I was wondering, when passing data using prepareForSegue, can you pass data to a View Controller later in the app? For example on the first ViewController I have the user enter their name. It's not until the very end, so a few views later, do I need to display their name. Is there a way to pass their name without having to go to the end view right away?
Use a Coordinator.
It's really easy to decouple your ViewControllers:
instead of using segues give every ViewController a delegate
create a coordinator object (this object knows your screen flow, not your screens)
the coordinator creates the ViewControllers (it can use UIStoryboard instantiateViewController(withIdentifier:) so ViewController A does not have to know that ViewController B exists
instead of calling performSegue you just call your delegate and pass in the data
Benefits
Simple to use
Easy to reorder screens in a flow
Highly decoupled (easier testing)
Very nice for A/B testing
Scales a lot (you can have multiple coordinators, one for each flow)
Sample
Let's say you have 3 VCs, the first one asks for your name, the second for your age and the third displays the data. It would make no sense that AgeViewController knew that NameViewController existed, later on you may want to change their order or even merge them.
Name View Controller
protocol NameViewControllerDelegate: class {
func didInput(name: String)
}
class NameViewController: UIViewController {
weak var delegate: NameViewControllerDelegate?
#IBOutlet var nameTextField: UITextField!
//Unimportant stuff ommited
#IBAction func submitName(sender: Any) {
guard let name = nameTextField.text else {
// Do something, it's up to you what
return
}
delegate?.didInput(name: name)
}
}
Age View Controller
protocol AgeViewControllerDelegate: class {
func didInput(age: Int)
}
class AgeViewController: UIViewController {
weak var delegate: AgeViewControllerDelegate?
#IBOutlet var ageTextField: UITextField!
//Unimportant stuff ommited
#IBAction func submitAge(sender: Any) {
guard let ageString = ageTextField.text,
let age = Int(ageString) else {
// Do something, it's up to you what
return
}
delegate?.didInput(age: age)
}
}
Displayer View Controller
class DisplayerViewController: UIViewController {
var age: Int?
var name: String?
}
Coordinator
class Coordinator {
var age: Int?
var name: String?
var navigationController: UINavigationController
init(navigationController: UINavigationController) {
self.navigationController = navigationController
}
fileprivate lazy var storyboard: UIStoryboard = {
return UIStoryboard(name: "MyStoryboard", bundle: nil)
}()
//This works if you name your screns after their classes
fileprivate func viewController<T: UIViewController>(withType type: T.Type) -> T {
return storyboard.instantiateViewController(withIdentifier: String(describing: type(of: type))) as! T
}
func start() -> UIViewController {
let viewController = self.viewController(withType: NameViewController.self)
viewController.delegate = self
navigationController.viewControllers = [viewController]
return viewController
}
}
Coordinator + Name View Controller Delegate
extension Coordinator: NameViewControllerDelegate {
func didInput(name: String){
self.name = name
let viewController = self.viewController(withType: AgeViewController.self)
viewController.delegate = self
navigationController.pushViewController(viewController, animated: true)
}
}
Coordinator + Age View Controller Delegate
extension Coordinator: AgeViewControllerDelegate {
func didInput(age: Int) {
self.age = age
let viewController = self.viewController(withType: DisplayerViewController.self)
viewController.age = age
viewController.name = name
navigationController.pushViewController(viewController, animated: true)
}
}
Not really. You can pass view by view the item but it's not a proper way of doing things.
I suggest you to have a Static Manager or this kind of stuff to store the information globally in your app to retrieve it later
All the solution are pretty good. Possible you can try the below model also
1. DataModel class
1.1 Should be singleton class
1.2 Declare value
Step 1 : ViewCOntroller-one
1 Create the Sharedinstance of singleton class
1.1 Assign the value
Step 3 :ViewController-two
1 Create the Sharedinstance of singleton class
1.1 Get the value

Retrieve var value from another class

i have those two classes and i want to retrieve a value from one to another:
class SearchUserViewController:UIViewController{
var selectedUser: String!
#IBAction func btn_accept(sender: AnyObject) {
selectedUser = "Norolimba"
self.dismissViewControllerAnimated(true, completion: nil)
}
}
I'm saving the value to "selectedUser" var, then i want to check the value from this class:
class CalendarViewController: UIViewController {
override func viewDidAppear(animated: Bool) {
let vc : SearchUserViewController! = self.storyboard.instantiateViewControllerWithIdentifier("searchView") as SearchUserViewController
println("\(vc.selectedUser)")
if vc.selectedUser == nil {
self.requestData("team")
}else{
self.requestData("user")
}
}
}
But when i println the value "vc.selectedUser" the value is nil. So what can i do here to catch it from the other class and don't get a nil value?
searchView is here:
Hope you can help me.
Thanks
When you use instantiateViewControllerWithIdentifier(), you're not accessing the view controller that was being displayed on the screen, nor are you accessing the controller that has potentially been automatically instantiated by Interface Builder.
What you're doing is instantiating (hence the name) a new instance of that controller. So the instance variable selectedUser of that new instance is going to be nil.
What you should do is probably provide a callback to your SearchUserViewController when you display it, so that it can notify the view that presented it when a user is picked.
Alternatively, you can use the parentViewController property of UIViewController in cases where you (the view controller) are being presented modally to access the view controller that presented you. So in your SearchUserViewController, when it's being dismissed, it can access self.parentViewController, which should be a CalendarViewController, and call a method or set a property.
(But for the modal controller to assume who its parent is, is a bit of a code smell. I recommend using a callback or delegate of some sort.)
Edit: An example of using the completion callback:
class CalendarViewController : UIViewController {
public func displayUserSelectionController() {
let suvc : SearchUserViewController = ... (how are you displaying it?) ...
self.presentViewController(suvc, animated:true, completion: {
// The SUVC is hiding
if let user = suvc.selectedUser {
self.requestData("team")
} else {
self.requestData("user")
}
})
}
...
}

Resources