Understand TableView and UISegmentedControl - ios

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!

Related

Adding a cell to another TableView. (selecting an item from a tableview, showing it in another tableview)

I'm building an app which which has built in with 2 different tabs. First tab is is "Home" which basically has a tableview with cells that configured from an api.(The api gets me country names for now)
Those cells also have a "Star" button which prints the data of the specific cell for now.
Second tab is "Saved" tab(SavedViewController), where I want to show the "starred" countries, using a tableview.
You can see the image below in order to get an idea for the app.
App simulation Image
The star button has a function in my CountriesTableViewCell. I'm using a saveButtonDelegate in order to let the SavedViewController know about an item is going to be saved. The code in CountriesTableViewCell for star button is as below.
#objc func buttonTapped() {
//If Button is selected fill the image. Else unfill it.
if !isSaveButtonSelected {
saveButton.setImage(UIImage(systemName: "star.fill"), for: .normal)
isSaveButtonSelected = true
saveButtonDelegate?.saveButtonClicked(with: countryData) //Checking if save button is clicked
}
}
countryData is the data that I get from the api, and this is the data I want to pass to SavedViewController.
struct CountryData: Codable {
let name : String
}
So on the SavedViewController, I'm handling the data using the SaveButtonProtocol conformance as below:
extension SavedViewController: SaveButtonProtocol {
func saveButtonClicked(with data: CountryData) {
countryDataArray.append(data)
print("saveButtonClicked")
print("countryData in savevc is \(countryDataArray)")
DispatchQueue.main.async {
self.countriesTableView.reloadData()
}
}
}
Whenever I click the star button on the first tab, this function is getting called on SavedViewController. So whenever I click to button, those print statements above work fine.
The problem is, whenever the star button is clicked, it should append the data of the current clicked cell to countryDataArray in SavedViewController. But the array is not populating as it should.
Let's say I pressed the first cell's star button, my print("countryData in savevc is (countryDataArray)") statement prints : ["Vatican City"], then I press the second cell's star button it only prints ["Ethiopia"] while it should print ["Vatican City", "Ethiopia"]
Why this issue is happening? My best guess is I'm delegating SavedViewController from the cell class so it behaves differently for every other cell. If that is the problem what should I do to solve it?
Many Thanks.
You should store your data in a shared (static array) object so you only have one source and add the saved indicator in your country struct so you do not rely on what is displayed in one view controller.

Swift combine different type into one array to display data for UITableView

I have tableview where I need to show few sections. You should imagine this table like a playlist of your songs. In the top first section I need to display a cell with button which will add more songs to the playlist and other sections of tableview are header titles of Music category (like pop, rock and etc). Each of these sections contains cells which are songs names.
I have an array of songs called like this: var songsGroups = [SongGroup]. Which is actually my datasource.
SongGroup contains few properties:
var categoryName: String
var songs: [Songs]
But the problem appears on the next level. I every time need to check indexPath.section and do like this:
if indexPath.section == 0 {
// this is a section for ADD NEW SONG BUTTON cell no need in header title as there is no data repression only static text on the cell.
} else {
var musicCategoryName = songsGroups[indexPath.seciton - 1]. categoryName
headerTitle.title = musicCategoryName
}
As you see my code became magical by adding this cool -1 magical number. Which I replay don't love at all.
As an idea for sure I can try to combine my ADD NEW SONG BUTTON section (by adding some additional object) with songsGroups array and create NSArray for this purposes. Like in Objective-C as you remember. So then my datasource array will looks like this:
some NSArray = ["empty data for first cell", songsGroups[0], songsGroups[1]... etc]
So then there is no need to check any sections we can trust our array to build everything and even if I will add more empty data cells there is no need for me to handle my code via if block and adding tons of magical numbers.
But the issue I see here that we don't use explicit types of array and it's upset.
So maybe you know more beautiful solutions how to resolve my issue.
You can introduce a helper enum:
enum Section {
case empty
case songCategory(categoryName: String, songs: [String])
}
Your data source would then look something like this:
let datasource: [Section] = [.empty, .songCategory(categoryName: "Category1", songs: ["Song 1", "Song2"])]
So now you can use pattern matching to fill the table view:
let section = datasource[indexPath.section]
if case let .songCategory(categoryName, songs) = section {
headerTitle.title = categoryName
} else {
// this is a section for ADD NEW SONG BUTTON cell no need in header title as there is no data repression only static text on the cell.
}
I am not sure if I understand you right. But is seems to me that you want to display
1) something that lets the user add a new song by tapping a button, and
2) a table of songs, sectioned into groups.
If this is the case, why don’t you put the add new song button in the table header view, and all your song groups and songs in a 2-dim array used as your dataSource?

Observing the changes to Object Swift 4

I have Swift object with about 20 Properties. In the app, there is a screen to get the user input and create the above swift object from the user entered value. Right now, if the user clicks the back button, all the user entered data will lose. So I want to alert the user to save the details if he/she has made any changes. How do we identify if the user has made any changes to the properties. Is it possible to use KVO in this case as we have too many properties?
What you need is a data model to hold the information in that particular screen, and then compare it with the original data when leaving the screen.
For the sake of simplicity, let's assume your screen has 2 text fields. One holds a name and another the age of a person.
struct Person: Equatable {
var name: String
var age: Int
}
When you first open this screen, the model will have the default values. Create a copy of this model and whenever the user makes any changes to the values on the screen, update the copy.
class YourViewController: UIViewController {
// Populate these 2 values when creating your view controller
var person: Person!
var personCopy: Person!
.
.
.
// You need to add this target to your text fields
#objc func textFieldDidChange(_ textField: UITextField) {
switch textField {
case personTextField:
personCopy.name = personTextField.text!
case ageTextField:
personCopy.age = Int(ageTextField.text!)!
default:
// Handle other text fields here or write separate cases for them
}
func dismissView() {
if person == personCopy {
// Dismiss your view
} else {
// Show alert
}
}
}
If the user presses the back button, all you need to do is compare these 2 models and check if they are the same. If they are the same, you can go back, if not, prompt an alert asking the user to save changes or discard.
I think KVO would be overkill here. Use KVO only for distant objects in your app.
Here you have the UITextFields in your viewController and you must have reference to the user object anyway.
Easier is: On the back button press you would check all text properties of your UITextField objects to the (existing) values of your user object. If one of them has changed then present the alert.

A UIButton is pressed in 1 VC and I need it to save the buttons title to a label in a different view controller not directly segued to each other

In the initial set up of my app I need the user to chose a genre type. i have it set up such that the user selects from a list of UIButtons which are all different genre types. I need this genre selection to save to the users profile. The next view controller is the same set up as the genreVC but asking for instrument instead of genre. The next segue from here is to view controller 3 the usersVC profile and it should have saved which two buttons were selected on the previous two VC's by updating a label. I have tagged each button with a number and have tried next to everything.
How do I save which button has been pressed on a different view controller to the users profile and update the label on the users profile to the name of the button pressed?
I am using each button individually and have them tagged as such as seen here:
#IBAction func popGenreButton(sender: UIButton) {
if(sender.tag == 6){
print("Pop genre selected")
}
}
#IBAction func altGenreButton(sender: UIButton) {
if(sender.tag == 7){
print("Alternative genre selected")
}
}
#IBAction func electronicGenreButton(sender: UIButton) {
if(sender.tag == 8){
print("Electronic genre selected")
}
}
I also tried adding each button into genreSelection as shown below that didn't work so I stuck with the individual buttons above.
#IBAction func genreSelectionButton(sender: UIButton) {
print(sender.titleLabel!.text!)
genreResult.text = genreString + " genre has been saved to your profile"
}
I also created a nib with myModalDelegate and protocol and nope.
I thought maybe I could create a genre array and when a certain button is selected it calls a certain item from the array and updates that item to the label but I was unsure how to go about this too.
I am using Xcode 7 and Swift 2.0 and Parse as my cloud server.
As you can see I have a lot to learn yet with Swift. I have been searching how to do this for the past week and can't figure it out. I am a 'lot' out of my depth but want to figure it out, if anyone can help that would be great!
A simple solution would be something like creating a global store for those values
i.e.
struct Choices {
static var genre: String?
static var instruments: String?
}
then you could in your different view controllers write and read from it by simply
Choices.instrument = "trumpet"
or
label.text = Choices.genre
A prettier solution would be to create a similar class as the previous struct but not have the variables static. You could then pass it along and fill up with the values from each VC and use the values in the last VC where you are setting the label text

Passing selected rows between ViewControllers and conditional segues in Swift

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.

Resources