passing data between view controllers without changing views - ios

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.

Related

How to set Struct Variable data from one view controller And Getting same value from Another view controller

Im new in swift. I have create a struct class with one variable. Im trying to set value of that struct variable from my first view controller which is working perfectly but when im trying to get that same value from second view controller its give me nil value.
below is my some codding
//Struct class
import Foundation
import UIKit
struct CompanyData {
var companyName : String?
mutating func setData(name : String)
{
companyName = name
}
}
// FIRST VIEW CONTROLLER
import UIKit
class SelfCompanyNameView: UIViewController {
#IBOutlet weak var companyNameTxt: TextfieldDesign!
var company = CompanyData()
var comName = ""
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func compnyBtnPress(_ sender: Any)
{
if companyNameTxt.text?.count == 0
{
Alert.showAlert(on: self, with: "Required", message: "Please enter your company name")
}
else
{
comName = companyNameTxt.text!
company.setData(name: comName)
print("\(comName)===\(company.companyName!)")
let vc = storyboard?.instantiateViewController(withIdentifier: "SelfAddressView") as! SelfAddressView
navigationController?.pushViewController(vc, animated: true)
}
}
}
//SECOND VIEW CONTROLLER
import UIKit
class SelfAddressView: UIViewController {
var company = CompanyData()
override func viewDidLoad() {
super.viewDidLoad()
print(company.companyName)
}
}
You need to pass your model to secondView Controller like following code
// FIRST VIEW CONTROLLER
let vc = storyboard?.instantiateViewController(withIdentifier: "SelfAddressView") as! SelfAddressView
vc.company = company
navigationController?.pushViewController(vc, animated: true)
//SECOND VIEW CONTROLLER
import UIKit
class SelfAddressView: UIViewController {
var company: CompanyData!
override func viewDidLoad() {
super.viewDidLoad()
print(company.companyName)
}
}
You need to declare a static variable as a shared instance of your struct. And use that while setting and getting your value.
E.G.
struct CompanyData {
static let shared = CompanyData()
var companyName : String?
mutating func setData(name : String)
{
companyName = name
}
}
While setting the value, use as:
company.shared.setData(name: comName)
And while getting the value, use as:
print(company.shared.companyName)
I would rather use Protocols and delegators. Here is a great tutorial will help you understand the concept Sean Allen Swift Delegate Protocol Pattern Tutorial - iOS Communication Patterns Part 1

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

Passing data to a specific View Controller without entering the controller Swift

I am making a app with many Views. When I am navigating from one View to another I want the data to be sent to a third view without entering the view. I know that it is a way to do this by using this code:
let nextView = self.storyboard?.instantiateViewControllerWithIdentifier("view3") as lastViewController
nextView.data = 1103
But if I do it this way and don't move to the view the data won't be saved. How can I save the data without entering it? Do I need to open it?
In the first view:
var chosenItem:Int = 0
#IBAction func buttonClicked(sender: AnyObject) {
if sender.tag == 0 {
chosenItem = 1
} else if sender.tag == 1 {
chosenItem = 2
} else if sender.tag == 2 {
chosenItem = 3
}
}
This is how my code looks now:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "toView2" {
let nextView = segue.destinationViewController as questionAgeViewController
nextView.firstAnswer = chosenItem
}
}
In the next view I send both the first item and the second. And as you understand this is not the best way to get the information. Is there a better?
I think you might be looking for a global variable. Use this code to make the variables:
struct globalVars {
static var firstVar = "Your text"
static var secondVar = Some number
static var thirdVar = Some array
}
In a other view you can access the information like this:
let globalText = globalVars.firstVar
println(globalText)
To edit the variables just use this code:
globalVars.firstVar = "The new text"
Create a singleton class and add properties into it, as per your requirement.
Now you can set its properties from your firstViewController and access the same from SecondViewController.
class Singleton {
class var sharedInstance: Singleton {
struct Static {
static var instance: Singleton?
static var token: dispatch_once_t = 0
}
dispatch_once(&Static.token) {
Static.instance = Singleton()
}
return Static.instance!
}
}
Singleton tutorials for Swift
http://thatthinginswift.com/singletons/
http://code.martinrue.com/posts/the-singleton-pattern-in-swift
http://www.raywenderlich.com/86477/introducing-ios-design-patterns-in-swift-part-1

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

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)

Resources