Move all views up after hiding one - swift

In my Android app, I was able to create use a standard table with rows and have text boxes in it. Based on user preferences, some of these rows are shown/hidden. For example if the user doesn't want to enter in cash/credit sales, or track any discounts given to a customer, etc, these fields shouldn't appear.
I simply would hide the entire row, and just like a DIV in html, the rest of the rows would move up nicely.
So for example:
element1
element2
element3
I'd want to remove element 2, and 3 moves up and takes 2's place. I'll probably have 8 of these fields btw.
In iOS, however, is something like this possible? Some googling yielded results that more or less made this seem very complicated for showing/hiding form elements and having everything slide up nicely. Some solutions included setting up very complex autolayout scenarios dynamically, or not using autolayout at all, etc. Then again, these solutions were for older versions of ios.
Is something like this possible? How could I achieve this?
Thanks!

Use UITableView.
UITableView allows you to represent list of cells and manipulate them (add, remove, reorder) with animations.
In you case every option will be a UITableViewCell.
Create a subclass of UITableViewController
Set a dataSource of UITableView
Implement UITableViewDataSource
That is 2 functions
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath:NSIndexPath) -> UITableViewCell
Update TableView items
You can easily reload tableView by editing your objects and calling reloadData method on tableView
// Add, remove or replace your objects you want to display in table View
objects = ["1", "2", "3"]
// call tableView to reload it's data. TableView will call dataSource delegates methods and update itself
tableView.reloadData()
You can remove or add specific item in tableView
First update your object, by removing or adding it, than call update tableView and say what cell (at which position) it should add or remove. You can also say what type of animation it should use
var index = 2
// Update your object Model, here we remove 1 item at index 2
objects.removeAtIndex(index)
// saying tableView to remove 1 cell with Fade animation.
tableView.deleteRowsAtIndexPaths([NSIndexPath(forRow: index, inSection: 0)], withRowAnimation: .Fade)
Here is full Code example
class MasterViewController: UITableViewController {
var objects = ["Option 1", "Option 2", "Option 3"]
func insert(obj: String, at: Int) {
objects.append(obj)
let indexPath = NSIndexPath(forRow: at, inSection: 0)
self.tableView.insertRowsAtIndexPaths([indexPath], withRowAnimation: .Automatic)
}
func deleteAt(index: Int) {
objects.removeAtIndex(index)
tableView.deleteRowsAtIndexPaths([NSIndexPath(forRow: index, inSection: 0)], withRowAnimation: .Fade)
}
override func viewDidLoad() {
super.viewDidLoad()
}
func insertNewObject(sender: AnyObject) {
insert(NSDate.date().description, at: 0)
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return objects.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as UITableViewCell
let object = objects[indexPath.row]
cell.textLabel?.text = object
return cell
}
override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
// Return false if you do not want the specified item to be editable.
return true
}
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle == .Delete {
deleteAt(indexPath.row)
} else if editingStyle == .Insert {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view.
}
}

Related

Saving Data in UITableview

I have an app whereby a user can add books to a favourites list, by populating a tableview. Details on the book are displayed in a view and by tapping a 'favourites', a segue is performed inputting info on that book into a tableview cell.
At present only one book can appear in the table at a time adding a new book will remove the initial entry (so in effect only the first cell of the tableview is ever used)
Is there a way to save each entry in the tableview so in effect a list of favourites is created
saveButton
#IBAction func saveButton(_ sender: Any) {
let bookFormat = formatLabel.text
if (bookFormat!.isEmpty)
{
displayMyAlertMessage(userMessage: "Please Select a Book Format")
return
}
else{
self.performSegue(withIdentifier: "LibViewSegue", sender: self)
}
}
TableView
extension LibrarybookViewController: UITableViewDataSource, UITableViewDelegate{
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 115
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print(#function, dataSource.count)
return dataSource.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
print(#function, "indexPath", indexPath)
guard let bookCell = tableView.dequeueReusableCell(withIdentifier: "libCell", for: indexPath) as? LibrarybookTableViewCell else {
return UITableViewCell()
}
let libbook = dataSource[indexPath.row]
bookCell.cellTitleLabel.text = libbook.title
bookCell.cellReleaseLabel.text = libbook.release
bookCell.cellFormatLabel.text = bookFormat
return bookCell
}
I have been reading about defaults and CoreData but I'm not sure whether this should be implemented within the segue button action or within the tableview functions?
I see you have a dataSource array which contains list of books. At the simplest you could just append data to your dataSource, then reload your UITableView. But if you want persistent storage, you could look into local db solutions like SQLite, CoreData, or Realm. Then it would just be the matter of storing -> get data -> display on UITableView.
The general idea would be on add button tap (or whatever event listener you want to attach the action to), save it to persistent storage, reload your tableView, since the dataSource is from persistent storage, it'll update automatically. Suppose you load the data from persistent storage.
Also, don't store your array in UserDefaults, it's for bite sized data for things like user session etc.
Edit: as #Leo Dabus point out, indeed you can insert row without reloading the tableView by using
let index = IndexPath(row: items.count - 1, section: 0) // replace row with position you want to insert to.
tableView.beginUpdates()
tableView.insertRows(at: [index], with: .automatic)
tableView.endUpdates()

How to pass all info from multiply table views cells with the info INSERTED to a new view controller?

#objc func addRow(sender: UIButton!) {
print("add")
names.insert("New Name", at: 0)
tableView.insertRows(at: [IndexPath(row: 0, section: 0)], with: .right)
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == UITableViewCell.EditingStyle.delete {
names.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: UITableView.RowAnimation.automatic)
}
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// if section == 1 {
// return carArray.count
// }
return names.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: bondeCellId, for: indexPath) as! BondCell
//
// if let lbl = cell.contentView.viewWithTag(101) as? UILabel {
// lbl.text = names[indexPath.row]
// }
//
// if let btnDelete = cell.contentView.viewWithTag(102) as? UILabel {
// btnDelete.addTarget(self, action: #selector(deleteRow()), for: .touchUpInside)
// }
return cell
}
enter image description hereI am fairly knew to programing and don’t know a lot.
I have built table view that lets the user add more table cells if they press the add button.
So the user will add more table views on depending if more information is needed.
The problem is to understanding is how to pass all info from multiply table views cells with the info to a new view controller.
If the User has insert 5 or 10 table cells I want those table cells info to be passed into a one new view controller.
The table view cell contains two text fields and two labels and the text field contains UIpickers and depending what they chose it updates in the labels in cell tables.
But I also want sum of all the table views chosen by the user to be added to a chart that I am making to a new view controller.
Would the labels and the text fields need to get new names each time a new table cell is made?
I am stock with ideas
If the User has insert 5 or 10 table cells I want those table cells info to be passed into a one new view controller.
The code you posted is a mess, and not very helpful with your question. It's mostly boilerplate.
You are thinking about this wrong. Cells don't store data. More generally, view objects don't store data. They display data.
You need a model object that stores the data for your table view. It looks like your table view only has one section, so a simple array would work just fine. Create a struct to hold the info you collect from the user. Let's call that struct CellData. Your model would be an array of CellData structs.
As the user creates new cells, you'd collect the new info and add it to your array.
Then when the user selects cells, and invokes the new view controller, you'd pass the whole array, along with the indexes of the selected cells, to the other view controller. (or perhaps just the structs for each selected item.)

How to control the contents of a table view based on cell selection in a collection view?

I have a collection view with 4 cells and a table view inside the same view controller. Each cell in the CV must make the table view display a different list; one table cell type per CV cell. When the user taps on one CV cell the table must reload its data and display the correct type of cell, (basically a different list with a custom cell). Each of these lists contains only 1 section.
I created and registered all my different cells with the table view and have all my data ready, with the views set and so on. How do I make the table view discard its current cells (list) and show a new list with different cells when the user has tapped on another CV cell?
I suppose that the solution is in the didSelectItem method from the CV delegate but I cannot find any information that shows how to make the table dequeue a different type of cell when the user has changed the cell selection inside the CV; or discard the previous one if needed.
At the moment I only register and dequeue one type of cell, and inside the delegate method for the CV I am calling empty functions that are supposed to put the new list inside the table.
The number of rows for each list is dynamic and this implies that I would have to call the delegate method on the table view again.
I have found an example of the MVVM pattern but I cannot apply it to my logic as that is more static.
Any help would be much appreciated. Thank you.
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let selectedMenuTab = indexPath.item
switch selectedMenuTab { // show different type of cell.
case 0: showAList()
case 1: showBList()
case 2: showCList()
case 3: showDList()
default:
print("no such main tab")
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableOfConversations.dequeueReusableCell(withIdentifier: rowID, for: indexPath) as! ConversationsListCell
let messageInChatList = listOfOneToOneChats[indexPath.item]
cell.messageInChatList = messageInChatList
cell.selectionStyle = .none
return cell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return listOfOneToOneChats.count
}
I think this should be as simple as calling tableView.reloadData() at the end of the collection view delegate's didSelectItemAt method.
The tableView should have it's data source based on a common array, for example:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return displayingList.count
}
And then in the didSelect of the collection view set that array and then tell the tableView to reload:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let selectedMenuTab = indexPath.item
switch selectedMenuTab { // show different type of cell.
case 0: displayingList = listA
case 1: displayingList = listB
case 2: displayingList = listC
case 3: displayingList = listD
default:
print("no such main tab")
}
tableView.reloadData()
}
And for dequeueing cells, check based on the type if they are different, or based on selectedMenuTab:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: UICollectionViewCell
let selectedMenuTab = indexPath.item
switch selectedMenuTab { // show different type of cell.
case 0: cell = tableOfConversations.dequeueReusableCell(withIdentifier: rowID, for: indexPath) as! ConversationsListCell
//And so on
default:
fatalError("No cell for this tab")
}
let item = displayingList[indexPath.item]
// Cell setup
return cell
}
The type of the values will be something to consider in order to avoid an array of Any, which would not be super type-safe, but that will depend on the types of your objects.

Select cell in TableView Section

I have been searching for an answer on how to do this, but nothing seems straight forward, or the user wants to do something different(like selecting multiple cells).
Background:
I am making an app about retrieving quotes everyday for different professions such as Reading, gardening, sports. I have a UITabBarController with 3 tabs.
First tab = Quote ; Second tab = Categories ; Third tab = Settings *
The Settings tab is a UITableView with 3 sections, and 2 cells in each section.
Problem: I want to make each cell go to a different destination. The first cell in the first section will just be a view controller with a slider(color slider to change text color)(will get to that later). How would I be able to accomplish this?
import UIKit
class settingsTab: UIViewController, UITableViewDelegate {
struct Objects {
var sectionName : String!
var sectionObjects : [String]!
}
var objectsArray = [Objects]()
override func viewDidLoad() {
super.viewDidLoad()
objectsArray = [Objects(sectionName: "Options", sectionObjects: ["Change color of text" , "Notifications"]),Objects(sectionName: "Extras", sectionObjects: ["Remove ads" , "Restore purchases"]),Objects(sectionName: "Support", sectionObjects: ["Rate this app" , "Email developer"]), Objects(sectionName: "Jordan Norris - Quote Daily 2016", sectionObjects: [])]
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell") as UITableViewCell!
cell.textLabel?.text = objectsArray[indexPath.section].sectionObjects[indexPath.row]
cell.backgroundColor = UIColor.cyanColor()
return cell
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return objectsArray[section].sectionObjects.count
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return objectsArray.count
}
func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String?{
return objectsArray[section].sectionName
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
}
}
This is settingsTab.swift, where the TableViewController is established. If you need any more information, just comment what you would like to be added and I'll edit this post immediately. Thank you in advance!
You can access each section and each row through your last function didSelectRowAtIndexPath using indexPath.section and indexPath.row. For example if you want to access the second section and the first row, you can do
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
print("Section \(indexPath.section), Row : \(indexPath.row)")
if indexPath.section == 1 && indexPath.row == 0{
//Code to implement View Controller to remove adds
print("Remove Adds")
}
}
Try selecting each row to show which section it prints and which row.
Will the number of sections and items in the section change? If not, creating static cells and hooking up a segue to each cell to a different destination can be done with no code (all in Interface Builder).

TableView on simulator is not referencing correctly

Hello and how is everyone?
When I run the simulation of my application I go to select the first object in the table. Nothing happens. I then go and select the second object and it performs the action that the first object was supposed to. Basically the first object is not active until I select it and then select another object.
Maybe this will help:
I turn the simulator on. I select the first object nothing happens(I have a print("what row I selected") within the didSelect function to make sure it is working.). But again no printout until I hit the second object, the second object should print #1 row selected but it says #0 row selected and then performs the segue setup for 0#. After I come back from the segue the first object still performs the same.
Here is my tableView Code:
var objects: NSMutableArray! = NSMutableArray()
override func viewDidLoad() {
super.viewDidLoad()
self.objects.addObject("help")
self.objects.addObject("me")
self.objects.addObject("please")
self.objects.addObject("thankyou")
self.tableView.reloadData()
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print(self.objects.count)
return self.objects.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = self.tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! TableViewCell
cell.titleLabel.text = self.objects.objectAtIndex(indexPath.row) as? String
return cell
}
func tableView(tableView: UITableView, didDeselectRowAtIndexPath indexPath: NSIndexPath) {
print("You selected cell #\(indexPath.row)!")
Thanks for the help.