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

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.

Related

Handle the button list in UITableViewCell

I’m continuing to the last ViewController in my movie booking project. Because I’m new on iOS so please apologize if my question is so popular and very basic. Thank you in advance!.
So I have trouble to solve problem with the list of buttons in the cell.
E.g:
I have the swift class file for the button:
class ButtonReverse: UIButton {
require.init()
//something here. Color, etc.
}
class TypeOfSeat {
var normalSeat: Bool
var coupleSeat: Bool
var reversedSeat: Bool
var reversingSeat: Bool
// something here
}
class Total: TypeOfSeat {
var priceOfSeat: Int
func invoice () -> Int {
/// something here to return total of invoice.
// return total }
In the UITableViewCell.
I have list of the buttons to be ordered vertically.
How can I loop over the cell and create list of the buttons in one cell and random the button color that I have defined before be implement the function:
cellForRows at: IndexPath
All of buttons will be sorted in just one cell. Red is reversed seats. Yellow is reversing seat. And Green is available seat ready for reverse.
I have idea to use uicollectionview. But I want to use it with tableview only because i’m just starting with uitableview.
To handle the button in the cell I’m just using simple function to call delegate for UIAlert and show message: the seat is reversed or show the total......when user touch the button.
Many thanks,
To achieve your desired result you can follow these steps :
Use CollectionView.
Take your colors in an array and use random to get color randomly and make an array for your collection (say dataArray).
If you are using buttons just to click, then don't use them instead you can give cell's content view (or you can take one view in cell) corner radius to make them circle and assign the color.
Then implement didSelectItem method to get the click item of particular cell (or color) and fetch the data from your dataArray based on indexPath.item as index.

RxSwift - auto reloading UITableView initialized with Observable

I have main UITableView with some Todos that are populated used an array of Observables:
// on viewDidLoad
self.todosViewModel.todos.asObservable()
.bind(to: tableView.rx.items(cellIdentifier: "TodoViewCell", cellType: TodoTableViewCell.self)) { (row, todo, cell) in
cell.status.image = todo.getStatusImage()
cell.title.text = todo.title.value
cell.todoDescription.text = todo.description.value
cell.dueDate.text = String(describing: Utilities.dateFormatter.string(from: todo.dueDate.value))
}.disposed(by: disposeBag)
On another screen I can add/edit the data and then, click "save" button to append a new todo or modify the one being edited. This works great, except that the tableView don't reload automatically, forcing me to call tableView.reloadData() on viewDidAppear, which is triggered when the other screen is dismissed.
So,
Is there a native way for me to reload a table automatically when then todosViewModel variable is updated?
EDIT:
If on the screen of edition of the todo I reassociate the todosViewModel's value property with the same value, it also works:
self.todosViewModel.todos.value = self.todosViewModel.todos.value
That's pretty ugly, but I only know how to reload a tableView using one of those ways.
When I append a new todo or reassociate a new value to the base todos it works. When I edit, no.
That's the whole point. For the UITableView to update, your todos have to emit when an item inside has been edited or you have to bind something from your todo within the cell (this way you cannot change hight of the cell but you can push some dynamic information onto the cell).
Another option would be to map some observable from todo to the index of the cell where it's presented and call UITableView.reloadRows to update your edited cell.
I would recommend you to take a look at RxDataSources examples.

Update array data used to create UITableView when IBAction is fired

I have created a UITableView that includes a UIImage, and two UILabels. This data is passed to a details view controller when the user selects the row. I have also successfully created a UIButton in the prototype cell that updates the text of one of the UILabels. I am unsuccessfully attempting to update this label within the array so this change is reflected on the details view controller. This is what I tried with no success:
#IBAction func updateTableData(_ sender: Any) {
grinds.removeAll()
grinds = createArray()
}
Here is a link to the repo: https://github.com/andrewTuzson/tableViewPractice
In the screenshot below, the left image displays the tableview after the first cell has been toggled to "Needs Work". The right image displays the details screen that is loaded after selecting the row.
How should I approach solving this?
If you want to update a tableViewCell upon the button click then, you should make the model array global to access it in click method and update what you want then reload the tableView either with delegate or NSNotificationCenter
--
in the custom class of the cell declare an integer property and in cellForRow set it like this
cell.index = indexpath.row

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

TableViewCell button click firing in multiple sections of TableViewControl

Bit of a weird one i can't seem to understand so i have a tableview control in a viewcontroller and within the tableviewcell i have a button which is using a delegate.
So when the button is clicked it will change the tag number of the cell and also change the image of the button based on the tag of the cell in the tableview.
But the problem i'm having is that when you select a button in a row i.e. the first one it seems to be running the function to change the image for each item in a section almost. I.e choosing the 5th item will update every 5th button in a cell to the selected image which is weird. Since it's almost like the tableview is being split up to sections of 5.
Can anyone help prevent this behaviour so it only changes the icon for the button in the cell which has been pressed.
Below are the functions that i've used and a link to a video on dropbox showing the behaviour https://www.dropbox.com/s/a4aasti78872w6j/Help.mov?dl=0
Delegate
protocol StoryTableViewCellDelegate: class {
func didBookmarkItem(cell: StoryTableViewCell, sender: AnyObject)
}
Function for Delegate
#IBAction func bookmarkButtonDidTouch(sender: AnyObject) {
bookmarkButton.animation = "pop"
bookmarkButton.curve = "easeOut"
bookmarkButton.duration = 0.5
bookmarkButton.damping = 0.4
bookmarkButton.velocity = 0.4
bookmarkButton.animate()
// The delegate which will handle bookmarking items
delegate?.didBookmarkItem(self, sender: sender)
}
Function being used in tableview class
// MARK: StoryTableViewDelegate
func didBookmarkItem(cell: StoryTableViewCell, sender: AnyObject) {
// TODO: Implement bookmark functionality
switch cell.bookmarkButton.tag {
case 0:
cell.bookmarkButton.setImage(UIImage(named: "Bookmark-Selected"), forState: UIControlState.Normal)
cell.bookmarkButton.tag = 1
case 1:
cell.bookmarkButton.setImage(UIImage(named: "Bookmark-Not-Selected"), forState: UIControlState.Normal)
cell.bookmarkButton.tag = 0
default:
break
}
}
The reason is, you are probable creating a cell using :
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cellID")
...
like you should do. However dequeueReusableCellWithIdentifier re-uses a cell, so if you have changed anything in a cell (like adding buttons), those buttons will still be in that recycled cell.
So you must set the book mark status in your cellForRowAtIndexPath for each cell.
I once had a similar problem when adding buttons to a cell, and when scrolling the cells automatically had extra buttons. So the first thing I did when re-using a cell was remove all buttons.
Edit sample code
in your
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath)
you have to add code for setting the button to the desired state, it will be something similar to the code you're using in didBookMarkItem :
switch myProperty {
case 0:
cell.bookmarkButton.setImage(UIImage(named: "Bookmark-Selected"), forState: UIControlState.Normal)
cell.bookmarkButton.tag = 1
case 1:
cell.bookmarkButton.setImage(UIImage(named: "Bookmark-Not-Selected"), forState: UIControlState.Normal)
cell.bookmarkButton.tag = 0
default:
break
}
You'll need a myProperty variable of course, which you should change in your didBookMarkItem as well
As others have said, it's due to table cell reuse. You want your tableView(_:cellForRowAtIndexPath:) method to always update the cell state from your model. Your bookmark state should be a property of your model. When the bookmark button state for a cell is changed by the user, the new state should be reflected in your model. If you do these things, your table cell state will always be correct. If you don't already, it's nice to always define a custom UITableViewCell class that contains outlets for all of the views and controls within your table view cell (your bookmark button would have an outlet.) Also, within your custom cell class, when you capture the button selection, you can call back to the controller either via a delegate (as you are doing), or a "bookmarkChanged" handler (closure) defined on the cell. I prefer the closure method. If you would like to see an working example of this, let me know.
Also, nix the tag. You don't need the tag to keep track of your bookmark button selection state, unless there's more to it than you have shown/described. If you follow the approach I described, it's taken care of.
Table view cells are reused. Once you select a cell and change its tag number and scroll the selected cell out of the table view, it will be used again to show a row at the index path of the new row that appears. And since its tag represents that of a selected cell it is displayed as highlighted.
Try to look up about cell reuse in table views.
You need to change your approach about determining which cell is to be displayed as selected. Add it as a property in your data source and based on that set the bookmark as selected or not selected.

Resources