How can I make an if statement to determine what array to use in a tableView? - ios

Using xcode 9.4, Swift 4 and ios 11, I want to select an item from a collection view screen that goes to a table view screen. On the table view screen, I have a series of arrays and, depending on what I've chosen on the collection view screen, I want to display a specific array on the table view screen.
I've already set up the prepareForSegue function on the collection view screen that references a var on the table view, but I'm a bit stuck on the "select an array" bit. I'm pretty sure it involves an if statement, but I'm not sure how or where to put this if statement.
Any help would be greatly appreciated.
Here's the if statement that I made (I don't know if its correct):
if cellItem == "jeans" {
tableArray.append(jeanArray[])
} else if cellItem == "skirts" {
tableArray.append(skirtArray[])
}
Then in the functions for the table set up, I've referenced tableArray.

There are a number of ways you can do this, I'm guessing the arrays contain similar information (guessing from jeans and skirts its clothing).
I would have a single value for determining which product type is to be displayed, then you have a few options available....
enum TableMode {
case jeans, skirts, trousers, hats
}
The multiple products array way (not recommended)
class TableViewController: UITableViewController {
var tableMode: TableMode
func tableView(UITableView, numberOfRowsInSection: Int) -> Int {
switch tableMode {
case .jeans:
return jeansArray.count
/// and so on...
}
}
}
Another option would be to have a single array that will be used, just fill it with the correct data: (best method)
// the int value could be the category ID from your API
enum TableMode: Int {
case jeans = 13
case skirts = 2
trousers = 4
hats = 19
}
class TableViewController: UITableViewController {
var tableMode: TableMode
var products: [Products]()
override viewDidLoad() {
super.viewDidLoad()
// fetch all data from api/database where categoryId = .tableMode value
self.products = coreDataFetchResult
self.tableView.reloadData()
}
Last idea, fetch all products and just filter it as needed to show the current product type selection
class TableViewController: UITableViewController {
var tableMode: TableMode
var products: [Products]()
override viewDidLoad() {
super.viewDidLoad()
// fetch all data from core data
self.products = coreDataFetchResult.filter { $0.categoryId = self.tableMode }
self.tableView.reloadData()
}
The best option depends on where you are filling the arrays from. I wouldn't suggest having multiple arrays if you are only going to use one.

Related

How to filter a table view output via 2 different buttons?

I am working on table view which looks like this:
I looked on the internet but all information is about a search bar and I do not want a search bar but only the two options - vaccinated and not vaccinated. How could I make so that I can filter the table by them?
You should have 2 arrays
var allData = [Model]()
var filteredData = [Model]()
And
struct Model {
var vaccinated:Bool
}
when the button is clicked do
filteredData = allData.filter { $0.vaccinated } // or !$0.vaccinated for the second option
tableView.reloadData()

UICollectionView state restoration: restore all UICollectionViewCells

I searched a lot through Google and SO, so please forgive me, if this question has already been answered!
The problem:
I have a UICollectionView with n UICollectionViewCells. Each cell contains a UIView from a XIB file. The Views are used for data entry, so all cells have a unique reuseIdentifier. Each View has also a unique restorationIdentifier. Everything works in normal usage, but not when it comes to state restoration:
The first 3 or 4 cells are getting restored properly because they are visible on the screen on startup, but the remaining cells, which are not visble, are not getting restored.
Current solution:
So I've discovered so far that a View is only restored if it's added to userinterface at startup.
My current working solution is to set the height of all cells to 1 in the process of restoring. Now every cell is loaded and all views are restored.
When applicationFinishedRestoringState() is called, I reload the CollectionView with the correct height.
Now my question is: I'm not happy with this solution, is there a more clean way to achieve restoring of all the UIViews?
I think you are getting a bit confused between your data model and your views. When first initialised, your table view is constructed from a data model, pulling in stored values in order to populate whatever is in each cell. However, your user does not interact directly with the data model, but with the view on the screen. If the user changes something in the table view, you need to signal that change back up to the view controller so that it can record the change to the data model. This means in turn that if the view needs to be recreated the view controller has the information it needs to rebuild whatever was in the table when your app entered the background.
I have put together a simple gitHub repository here: https://github.com/mpj-chandler/StateManagementDemo
This comprises a CustomTableViewController class which manages a standard UITableView populated with CustomTableViewCells. The custom cells contain three switch buttons, allowing the state of each cell to be represented by an array of Boolean values.
I created a delegate protocol for the cells such that if any of the switches is tripped, a signal is sent back to the view controller:
protocol CustomTableViewCellDelegate {
func stateDidChange(sender: CustomTableViewCell) -> Void
}
// Code in CustomTableViewCell.swift:
#objc fileprivate func switched(sender: UISwitch) -> Void {
guard let index : Int = switches.index(of: sender) else { return }
state[index] = sender.isOn
}
// The cell's state is an observed parameter with the following didSet method:
fileprivate var state : [Bool] = Array(repeating: false, count: 3) {
didSet {
if state != oldValue, let _ = delegate {
delegate!.stateDidChange(sender: self)
}
}
}
CustomTableViewController is registered to the CustomTableViewCellDelegate protocol, so that it can record the change in the model as follows:
// Code in CustomTableViewController.swift
//# MARK:- CustomTableViewCellDelegate methods
internal func stateDidChange(sender: CustomTableViewCell) -> Void {
guard let indexPath : IndexPath = tableView.indexPath(for: sender) else { return }
guard indexPath.row < model.count else { print("Error in \(#function) - cell index larger than model size!") ; return }
print("CHANGING MODEL ROW [\(indexPath.row)] TO: \(sender.getState())")
model[indexPath.row] = sender.getState()
}
You can see here that the function is set up to output model changes to the console.
If you run the project in simulator and exit to the home screen and go back again you will see the state of the tableView cells is preserved, because the model reflects the changes that were made before the app entered the background.
Hope that helps.

Way to query whether an object is a member of an array?

I'm designing a FreeCell card game and I have both my cards (model) and cardViews (view) in arrays that represent columns of cards – and then the whole board is an array of columns:
//in the model:
typealias Card = DeckBuilder.Card
typealias Column = [Card]
var board = [Column](repeating: [], count: 8)
//in the view controller:
var boardForView = [[PlayingCardView]](repeatElement([PlayingCardView](), count: 8))
When a user taps on a cardView, I want the controller to take the selected cardView's column & row (where the column is which column in the board and the row is which card in the column) and then find the card in the corresponding position in the model.
I've got my tap gesture recognizer set up, and have extracted the cardView from it:
func selectCard(_ sender: UITapGestureRecognizer) {
if let selectedCard = sender.view as? PlayingCardView {
print("card selected: \(selectedCard.cardDescription?.string)")
}
}
So the question is: is there a property of the cardView that is its place in a column array, and then a property of that column that is the column's place in the boardForView array?
I figured it out! One way I had thought of was iterating through each card in each column of the board and checking to see if it == my current card. Seemed way too clunky. But then I found array.index(of: element) and my day is saved! I do have to iterate through each column on the board, but it's still much more elegant:
func selectCard(_ sender: UITapGestureRecognizer) {
if let currentCard = sender.view as? PlayingCardView {
for column in 1...boardForView.count {
if let row = boardForView[column - 1].index(of: currentCard) {
print("card selected: \(currentCard.cardDescription!.string) in row \(row+1) of column \(column)")
selectedCard = freeCellGame.board[column - 1][row]
print("card selected from model: \(selectedCard!.description)")
}
}
}
}
Since it's a board game, you surely have a logic set for how the board should be laid out. Make that logic work both ways. That is, use it to provide layout info while creating your board and also use it to get information about your board.
This way, when the gesture recognizer triggers, fetch the tap point and let your layout logic tell you the row and column of the card the tap came from.
Now, you can directly fetch the Card from your board property with those two indices (row and column).

Need to display certain string elements in an array at the top of a UITableView in Swift

I have a UITableView which displays a list of strings from an array. The array is in alphabetical order, and at the top of the UITableView is a UISearchController bar. Everything is working fine. However, I need to make a modification to the list that is presented in the UITableView where a certain subset within the collection is presented at the top of the UITableView (and this subset should ALSO be in alphabetical order). However, once the user enters a character inside the search filter at the top, the list of strings displayed shouldn't matter.
For example, I have a UITableView which displays a list of employees in alphabetical order. What I want to do is when the UITableView loads, instead of presenting ALL the employees in alphabetical order, I would like to first list the managers in alphabetical order, and then the remaining employees in alphabetical order (i.e. AFTER the managers).
The array being received in the ViewController which holds the UITableView from the previous ViewController, which sends this list already sorted in alphabetical order. In other words, the array to begin with, is received already sorted.
I'm assuming you don't want to use sections? That you just want them to all in the same section?
If that's the case you would probably need to do some preprocessing to split the array into your subsets (in viewDidLoad or somewhere else at the beginning of your controller's life cycle):
self.managerArray = [AnyObject]() // you'll need a property to hold onto this new data source
self.employeeArray = [AnyObject]()
for employee: Employee in self.allEmployees {
// assume allEmployees is the alphabetical array
if employee.isManager {
// your condition for determining the subset
managerArray.append(employee)
}
else {
employeeArray.append(employee)
}
}
Because the array is already alphabetized this should populate the subarrays in alphabetical order as well (append just adds to the next index).
Then to make sure the table doesn't load the values before it's been processed in this way you'd need to have
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if self.employeeArray && self.managerArray {
return self.employeeArray.count + self.managerArray.count
}
return 0
}
Then you can just just populate cells from self.managerArray before moving onto self.employeeArray.
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if indexPath.row < self.managerArray.count {
var manager: Employee = self.managerArray[indexPath.row]
// populate your cell info here
}
else {
// else we've already filled in manager's time to start on employees
// subtract the number of managers from the indexPath to start at beginning of employeeArray
var employee: Employee = self.employeeArray[indexPath.row - self.managerArray.count]
// populate cell here
}
return cell
}
Then when you search, you can search on the original self.allEmployees array like normal.

How is better to manage data details (Swift)

I want to do a little list of data in TableView, and a DetailView.User tap on cell and goes to DetailView. In DetailView I have to check, what kind of data user selected in TableView. I pass name of data (title of cell) and Index(by prepareForSegue method). I can check name of data by two ways: by index, or by name (by switch). Which Way is better. My code for IndexCheck is something like this:
let Details = ["first","second","third"]
var PassedIndex:Int
override func ViewDidLoad () {
super.ViewDidLoad()
self.DetailLabel.text = Details[PassedIndex]
}
And a check by name is something like this:
var PassedName:String?
var DetailText:String
switch PassedName {
case "NumberOne":
DetailText = "#1"
case "NumberTwo":
DetailText = "#2"
case "NumberThree":
DetailText = "#3"
default:
DetailText = "Unknown number"
}
override func ViewDidLoad () {
super.ViewDidLoad()
self.DetailLabel.text = DetailText
}
Which way is the best? Which makes system working faster? I am just a beginner, so I need an advice from more experienced programmer than I
The best way of your two methods to check Details is check-by-index method. Why? Because it takes O(1) to take value that you need. Check by String takes O(n) in the worst case. But i suggest you to create an Enum that contains your Details and pass exactly detail that you need.

Resources