I'm working on my first app and I've got the UI sketched out, but before I move forward, I've hit a stumbling block out of the gate that's not in the tutorials I've been studying.
Two issues I have:
1) Passing data between TableViewControllers
2) Conditional Segues
The app uses CoreData and has a context with 3 managed objects in the model: FocusArea, Equipment, & Activity.
A portion of my app will have 3 TableViews that display fetched results from FocusArea, Equipment, & Activity. The navigation through them will be as follows:
Step 1 -> Step 2 -> Step 3 -> save selections
focusAreaTVC -> equipmentTVC -> activityTVC -> saveVC
equipmentTVC -> focusAreaTVC -> activityTVC -> saveVC
activityTVC -> focusAreaTVC -> equipmentTVC -> saveVC
When an item/items is/are selected in Step 1, a "next" button will advance to Step 2, display options available for rows selected in Step 1. Once the user makes selections in Step 2, the remaining available selections will be displayed in Step 3. I envision the final selections on the Save screen to be saved as an array or a dictionary.
Issues:
1) View Controllers: Rather than use 9 different ViewControllers, I plan to use performSegueWithIdentifier so I only need to set up 3 view controllers for the objects and figure out the logic to accomplish the transitions.
Would I create a global variable in AppDelegate for the TVC at each step and put logic in a switch statement for each VC within performSequeWithIdentifier?
example:
// AppDelegate variables start all nil, updated at each step, reset to nil on save
var step1VC = "equipmentVCUsed"
var step2VC = "focusAreaTVCUsed"
var step3VC = nil
// VC switch statement (contained in each TableViewController)
var segueIdentifier: String
switch segueName {
case step1TVC = "focusAreaTVC":
segueName = "equipmentTVC"
case step1TVC = "equipmentTVC:
segueName = "focusAreaTVC"
case step1TVC = "activityTVC":
segueName = "focusAreaTVC"
case step2TVC = "equipmentTVC":
segueName = "activityTVC"
case step2TVC = "focusAreaTVC", step1VC = "equipmentVC":
segueName = "activityTVC"
case step2TVC = "focusAreaTVC", step1VC = "activityVC":
segueName = "equipmentTVC"
case step3VC != nil,
segueName = "saveVC"
step1TVC = nil
step2TVC = nil
default:
println("Something so the the compiler doesn't yell at me")
}
2) I plan to make selections in Step 1 and pass them to Step 2's VC, further refine there, pass to Step 3. Since they're all working off the same context, can I just pass the selections from 1 TVC to another or do I need to create a list that the next VC will retrieve items from?
How would I get the information from Step 1 to Step 3? Would I have to save the selections in an Array between segues and refine them with fetches in each VC?
Why not just let the segues be triggered by taps on a table view cell? In other words, make it like this: User selects option from first table view -> User then sees second table view and selects from the appropriate set of options -> User then sees third table view and selects from the appropriate set of options. You can always put a back button at the top so the user can go back and change his/her selections.
Your users might want, and expect, your app to behave like this.
Anyway, to answer your questions:
1) I suggest that you create a UIViewController superclass from which your subclasses inherit, then define a property in that class's .h file that can be set to an object that contains whatever data each view controller needs to pass to the next view controller. Then, use prepareForSegue to pass along the data that the next view controller will need to have.
2) You can pass the selections from 1 TVC to another (see #1)
Edit: If you need any clarification, or if you feel I am misunderstanding your question, please feel free to leave a comment. I got less than 5 hours of sleep last night.
Related
This question already has answers here:
Passing data between view controllers
(45 answers)
Closed 3 years ago.
I am currently working on an app and have problems with posting events.
I collect data in a sequence of several views (7 screens) and would like to store the data finally at once (or show all the data in the final view).
These data are, for example, location info, user info, event image, comments, event category, ...
I know how to store the data in the database/storage (firebase) if I collect the data in one or two views.
But in my use case I have seven views and I could not find any elegant method.
What's the best way to do that with Xcode 10?
You can use struct as below code. Make all required variable for all screen in this struct (like string, image etc..). And you can access this from any ViewController.
struct InputDetails {
static var details: InputDetails = InputDetails()
var city: String = ""
var lat: String = ""
var long: String = ""
}
Now to add value in this
InputDetails.details.city = textfiels.text
Now to access first screen value in last screen
print(InputDetails.details.city)
And once your API call or above struct usage is over, make sure to reset all details like below.
InputDetails.details = InputDetails()
There are several ways for passing data between View Controllers. For example you could use an instance property or a segue or the delegation method.
I recommend you study this article which paints a complete picture of the different methods and how to apply them:
How To: Pass Data Between View Controllers In Swift
Edit:
Upon examining the picture in your question I figured that using a segue would be the most appropriate solution here. As it seems from the picture you enter data in one View Controller, pass that onto the second View Controller and finally you upload all the data to Firebase.
I assume that you use storyboards (if not then consult the link above for other methods.) In this example below you will pass a string from one VC to another.
Step 1:
Add a segue between two view controllers. Storyboard -> press ctrl and click on VC one and drag your mouse -> you will see a blue arrow, drag that to VC two and release -> select manual segue: show -> click on the segue and give it an identifier
Step 2:
In VC two, make a string variable:
class SecondViewController: UIViewController {
var stringToPass: String = ""
override func viewDidLoad() {
super.viewDidLoad()
print(stringToPass)
}
Step 3:
In VC one, enter the following:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let vc = segue.destination as? SecondViewController {
vc.stringToPass = "This is the string we pass between two VC"
}
}
Step 4:
Then whenever you want to go to the SecondViewController perform the segue like this:
performSegue(withIdentifier: "identifierYouEntered", sender: self)
I have been tasked with creating a flow in an iOS application where a user can add multiple steps, where the number of steps are undefined, each step acting as a ViewController within a navigation stack and the user can add multiple steps (VCs) while moving backwards in the navigation stack to edit, while moving back forwards to an existing step and not losing any inputted data.
Example: User creates Step 1, User creates step 2, user creates Step 3, user moves back to Step 2, User moves back to Step 1, Edits Info, moves forward to Step 2, moves forward to Step 3, etc.
So far I'm thinking of creating a sort of counter to keep track of what Step the user is on in addition to an array of Classes that hold the data that builds each VC but I'm having a little trouble with VC initializers and navigationController pushing and popping.
Any help would be appreciated, maybe someone has something up their sleeve.
You should create a class to hold steps objects
Something like this
class DataClass {
static let shared = DataClass()
var arrayObjects: [Any]
private init() {
arrayObjects = []
}
func addObject(object: Any) -> [Any] {
arrayObjects.append(object)
return arrayObjects
}
func clearObjects() {
arrayObjects.removeAll()
}
func object(at step: Int) -> Any? {
guard arrayObjects.count > step else {
return nil
}
return DataClass.shared.arrayObjects[step]
}
}
And use data something like this in each step.
DataClass.shared.object(at: step)
You can achieved above requirement by storing your ViewController locally into array.
Step:1 Create global Array of UIViewController as below.
var aryAllViewController = [UIViewController]()
Step:2 Append value into Array as below.
aryAllViewController.append(VC)
Step:3 Get old ViewController reference from Array and Push it into Navigation Stack again.
If you follow above step properly then old data will display automatically.
I have created a struct and made an array of that type. The struct consists of two variable:
struct notesarray
{
var prioritycolor : UIColor
var note : String
}
In my secondVC which houses a collectionViewController, I have made an array of type notesarray. I am sending values for prioritycolor and note from firstVC.
I will be setting up CoreData later on, for now I just want this to work in simplest of manners. I am appending data from firstVC to this array like so:
#objc func handleCheckButton()
{
print("Added")
let secondVC = AddedNotesCollectionViewController()
secondVC.allnotes.append(notesarray(prioritycolor: taskTextView.backgroundColor!, note: taskTextView.text))
print(secondVC.allnotes.count)
taskTextView.text = nil
}
allnotes is the name of the array found in secondVC.
For testing purposes I am printing secondVC.allnotes.count but I am just getting '1' in console no matter how many time I add elements to the array.
I have also tested this by placing print(allnotes.count) under viewDidAppear func in secondVC so that whenever I go to secondVC it gives me count of the elements in the array but it also shows '0' every time.
I don't know what I am doing wrong here. Please help me!
Thats because you end up getting a new instance of AddedNotesCollectionViewController every time you press the button.
let secondVC = AddedNotesCollectionViewController()
And new instance is initiated with an empty array and you add one element to it by calling
secondVC.allnotes.append(notesarray(prioritycolor: taskTextView.backgroundColor!, note: taskTextView.text))
Hence count is always one. iOS is correct there my friend :)
What you need:
If second VC is already loaded either by pushing a it on to navigation stack of FirstVC or if its presented then get the reference to the presented/pushed VC rather than creating a new one every time. There are many answers in SO which explains how to access the pushed/modally presented VC :)
If you are about to present/push the SecondVC, as you mentioned in the comments you can always make use of prepareForSegue to pass the data.
If in case your AddedNotesCollectionViewController is never presented then rather consider creating singleton instance of notesArray which you will share between multiple VCs.
Hope it helps
I have a view controller with 2 labels - each to be populated from lists in 2 separate table controllers. In my view controller I have 2 variables (temp and temp_1) to receive the data from the table controllers and populate the labels.
When I call the first table view and select an item that works fine and my first label is populated, when I then call the second table view that works too and my second label is populated - except that the first label is now blank - because temp is now blank.
I have attached my prepareForSegue from my first table view showing me passing my variable temp back to my view controller. (My second is similar and passes back temp_1)
Thanks for any help.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "return_1" {
if let destination = segue.destinationViewController as? main_ViewController {
if let row = tableView.indexPathForSelectedRow?.row {
destination.temp = array1[row] as! String
}
}
}
}
My guess is that what you are saying in words is not what in fact you are actually doing. You say "back to my view controller", as if you were returning from the table view to the main view controller. That is what you want to do and what you should do, but it isn't what you're doing (I'm guessing). I think you've set up your storyboard with a normal segue from the table view to the main view controller. That doesn't go back; it makes a whole new main view controller. So of course the old label value is missing, because this is a different view controller from the one with the old label value (which effectively is still there, two layers down, covered up by this new instance).
What you want is an unwind segue. That is how you say "go back" in a storyboard.
I Need some help to understand how can I filter a information in Uitableview with UISegmentedControl. I have a UITabeView with data that contain two different data, Rec and Dat . I want to load ALL data when start application and separate Deb and Rec when user choose in UISegmentedControl. When I start application I populate 3 Array alls, recs and dats. I show the array alls, and want to change/filter the data when the user change the choose in UisegmentControl. Can you help me please ?
#IBAction func filtroDebitoCredito(sender: AnyObject) {
//when All
if FiltroControlerTable.selectedSegmentIndex == 0 {
// tableView.reloadData() ???
}
//When Creds
if FiltroControlerTable.selectedSegmentIndex == 1 {
// ???
}
//Debs
if FiltroControlerTable.selectedSegmentIndex == 2 {
// ???
}
Tks for help
Actually, you are already there. Your table view displays model data. So when the user changes the value of the segmented control, switch to the correct set of model data and, exactly as you say, tell the table to reloadData(). What I would do is have four arrays: model, all, recs, and dats. The table, let us say, always displays model. So the segmented control would copy, let us say, recs into model and tell the table view to reload!