I know how to send the user to a new cell after they select a cell but what if the order of my cells change because I am retrieving data from Parse so for each new cell, the row number changes.
How do I ensure the user is sent to the correct page when they select a certain cell? This is what I'm currently using but I know there's got to be a better solution than hardcoding every possible option..
Any advice?
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
if indexPath.section == 0 && indexPath.row == 1 {
self.performSegueWithIdentifier("toSettingsPage", sender: self)
}
}
For my understanding of your questions, I suggest you use a NSMutableDictionary to store all the user info data, and on the didSelectRowAtIndexPath function, you will use the indexPath to find the correct user info.
Your input:
A table view
An index path (section, row) ordered pair
Your output:
A string that identifies a segue
An ideal data structure for this is a dictionary.
First, notice that the table view input is always the same (you only seem to care about one table view - the protocol for data source is written to handle as many table views as you like, but most people use one for one).
Second, think about your keys and values: your key is the index path. And in fact, the index path breaks down into just an Integer because it is always the same section, which is analogous to the situation with table view described above.
So your dictionary is going to be of type: Dictionary<Integer, String>.
Now, instead of using the dictionary directly, let's make a function to wrap it and call the function segueForIndexPathInDefaultTableView:
private let seguesForIndexPaths:[Integer:String] = [0:"segue0",1:"segue1",2:"segue2"]
private func segueForIndexPathInDefaultTableView(indexPath: NSIndexPath) {
return self.seguesForIndexPaths[indexPath.row]
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
self.performSegueWithIdentifier(self.segueForIndexPathInDefaultTableView(indexPath: indexPath), sender:self)
}
Related
How do I get the text of a selected cell in a UITableView? I know how to get the row (indexPath.row), for example, but I can't find how to get the contents of the selected cell.
The bigger picture:
Ultimately, I want to use the cell to select the appropriate segue. I am using a plist for the text of a UITableView's cells. When the user selects an item in the table, the segue is selected.
I set it up like this:
switch indexPath.row {
case 0: segueIdentifier = "segueToTopic0"
case 1: segueIdentifier = "segueToTopic1"
// etc.
default: segueIdentifier = "segueTest"
}
but ended up frequently adding and deleting items, or rearranging the plist, forcing me to rearrange this switch statement.
So, now I'm thinking I could do this by using the cell text in the switch statement:
switch indexPath.row {
case "Topic0": segueIdentifier = "segueToTopic0"
case "Topic1": segueIdentifier = "segueToTopic1"
// etc.
default: segueIdentifier = "segueTest"
}
(Of course, this means I would have to ensure the text in the plist and the text in the switch statement are identical.)
Or maybe there is a better way?
When asking a question here, it's a good idea to think through what you're asking as well as thinking through why you're asking.
Without additional context, your approach doesn't seem to make much sense.
"I am using a plist for the text of a UITableView's cells" ... Might you have 100 "topics" in your plist? Which would mean you have manually created and named 100 segue connections? Likely, there is a much better approach.
However, to answer your specific question...
We assume you have loaded your data from your plist into an array of strings, perhaps called arrayOfTopics ... after loading, it will be something like this:
arrayOfTopics [
"Topic0",
"Topic1",
"Topic2",
"Topic3",
"Topic4",
"Topic5",
]
So, your cellForRowAt implementation looks something like this:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = arrayOfTopics[indexPath.row]
return cell
}
Instead of thinking "how do I get the text from a row" just use the same data structure, so didSelectRowAt could look like this:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let segueID: String = "segueTo" + arrayOfTopics[indexPath.row]
performSegue(withIdentifier: segueID, sender: nil)
}
If you tap on the 3rd row (rows and arrays are Zero based), indexPath.row will be 2... arrayOfTopics[2] contains "Topic2"... appending it to "segueTo" will result in the string segueToTopic2, which you can then use as the segue identifier.
I have a TableView in my app that when user tap the button in one of the cells the safari will open in the app and when the safari dismiss the app will crash because I want to update one of the parameters in the tableview so I need to remove all and append them again from Json But this will take some seconds so the app will crash because there is no data to show I used Timer But timer is not good solution because some times it will take more time than timer so it will crash again I need a method that wait to finish and then do some thing else of if you think this is not a good solution too please guide me which solution is good for this problem
here is what I used in my app after dismiss safari in application(its not all of the codes but these codes has problems )
if let payed = dict.value(forKey: "payed"){
factorViewController.payed.removeAll()
self.factorTableView.reloadData()
factorViewController.payed.append(payed as! Int)
self.factorTableView.reloadData()
print("payed = \([payed])")
}
Try reloading the data only once:
if let payed = dict.value(forKey: "payed") {
// Empty Array here
factorViewController.payed.removeAll()
// You are appending here, so array will not be empty
factorViewController.payed.append(payed as! Int)
// Now you can reload data
self.factorTableView.reloadData()
print("payed = \([payed])")
// Also, if this doesn't work, you can use a callback(completion handler).
}
That way, your array will not be empty when you need to reload data
How TableView Works
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return your_array.count // It returns how much cells you want in your tableview.
}
// This func defines what you wanted in your cell
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = dequeue_property // to dequeue your cell
your_lbl.text = your_array[indexpath.row] // as per logic
return cell
}
In your case
Count of array in numberOfRowsInSection is more than the array you'r using in the cellForRowAt so on dequeue your cell have more index than the array you are using in cellForRow. Thats why is doesn't getting index and crashes with IndexOutOFRange
You have array issue not tableView Reload issue
Take ref : Swift array always give me "fatal error: Array index out of range"
Ok That was easier than I thought ! I just defined another variables in my app and when started getting Json append new data to the new variables and then equals new variables to old variables ! thats it!
I have been learning swift through the last few days and I have come across an error that I have been stuck on for quite a while now.
I am attempting to get the selected indexPath so that I can then push data according to which item he selected. I have searched through and tried many different solutions I have found on stack overflow as well as different websites but I am not able to get this figured out still.
The code is below:
#IBOutlet var selectGroceryTable: UITableView!
/* Get size of table */
func tableView(tableView: UITableView, numberOfRowsInSection: Int) ->Int
{
return grocery.count;
}
/* Fill the rows with data */
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let myCell:UITableViewCell = selectGroceryTable.dequeueReusableCellWithIdentifier("groceryListRow", forIndexPath:indexPath) as! UITableViewCell
myCell.textLabel?.text = grocery[indexPath.row];
myCell.imageView?.image = UIImage(named: groceryImage[indexPath.row]);
return myCell;
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath)
{
print("Row Selected");
NSLog("Row Selected");
}
Nothing ever prints acting like the function is not being called. However, I do not understand why this would not be called?
Any help would be greatly appreciated!
UPDATE:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
selectGroceryTable.data = self;
selectGroceryTable.delegate = self; //gives error states you can not do this
}
There are a couple of things to check in cases like this:
First, what kind of method is didSelectRowAtIndexPath?
Answer: It's a UITableViewDelegate method. Did you set your view controller up as the delegate of the table view? If not, this method won't get called.
Second, have you made absolutely certain that the method signature is a perfect match for the method from the protocol? A single letter out of place, the wrong upper/lower case, a wrong parameter, and it is a different method, and won't be called. it pays to copy the method signature right out of the protocol header file and then fill in the body to avoid minor typos with delegate methods.
It looks to me like your method signature is correct, so my money is on forgetting to set your view controller up as the table view's delegate.
say me, please, how better compare two same (label has name "labelNumber") labels from different rows in Tableview
For example: I know, that in row №0 label is "06" (Int) and in next cell (row №1) this label is "07". So, "07" > "06". How compare it with swift language?
Thanks!
The wrong way
As stated by #vadian, you should NOT use the UI you populated to perform calculations on data.
However this is the code to compare the values inside 2 UITableViewCell(s)
class Controller: UITableViewController {
func equals(indexA: NSIndexPath, indexB: NSIndexPath) -> Bool? {
guard let
cellA = tableView.cellForRowAtIndexPath(indexA),
cellB = tableView.cellForRowAtIndexPath(indexB) else { return nil }
return cellA.textLabel?.text == cellB.textLabel?.text
}
}
The right way
Think about how you are populating the cells. I imagine you are using some Model Value. So just compare the Model Values you are using to populate the cells.
Probably something like this
class Controller: UITableViewController {
var data: [Int] = [] // ....
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCellWithIdentifier("YourCellID") else { fatalError("No cell found with this id: 'YourCellID'")}
cell.textLabel?.text = String(data[indexPath.row])
return cell
}
func equals(indexA: NSIndexPath, indexB: NSIndexPath) -> Bool? {
return data[indexA.row] == data[indexB.row]
}
}
Compare the values stored within your data array:
if dataArray[0].myIntegerValue > dataArray[1].myIntegerValue {
// Do your stuff
}
Edit: this assumes your data is stored as objects with that Int as an attribute.
As the others have said, don't do that. In the MVC design pattern, labels are views. They are for displaying data, not storing it.
Trying to read values from table view labels is especially bad, because as the table view scrolls, the cells that go off-screen will be recycled and the values in their views will be discarded. You need to save your data to a model object. (An array works just fine to save table view data, or an array of arrays for a sectioned table view.)
I am programming my first iOS application and am in the process of learning about protocols and delegation. The application is a basic random response generator. When the user shakes the device or taps the screen, a random response should display in a label.
Currently, I have three view controllers embedded in a navigation controller: MainViewController.swift, SettingsViewController.swift, and ResponsesViewController.swift.
I have an array in MainViewController.swift that holds the responses. I have successfully been able to pass the data to the third view controller, which is ResponsesViewController.swift, which is a table view for displaying the stored responses. I have set my first view controller, MainViewController as the delegate for my third view controller, ResponsesViewController, and implemented the method where I would like to remove the selected response from the data model (responses array).
My problem is when I try deleting a response I get fatal error: Cannot index empty buffer in the console. I used println(responses.count) to make sure that I was passing the array to the second and third view controllers successfully. I placed the println(responses.count) statement in all three view controllers within the methods viewDidLoad(), viewWillAppear(), and viewDidDisappear(). This showed that the data was passing successfully as there were 3 objects in the array with each println() statement. However, in my delegate, MainViewController.swift, I keep getting the error when I try to remove the selected response from the data model. I placed println(responses.count) into this method, but it keeps returning 0 and crashing with the error. It is only happening when func responsesViewController(controller: ResponsesViewController, didDeleteResponseAtIndexPath indexPath: NSIndexPath) is being called in the delegate (MainViewController.swift)
Here is my code:
MainViewController.swift
var responses: [Response] = []
let response1 = Response(text: "String 1")
let response2 = Response(text: "String 2")
let response3 = Response(text: "String 3")
override func viewDidLoad() {
super.viewDidLoad()
navigationController?.navigationBarHidden = true
responses += [response1, response2, response3]
}
* delegate *
func responsesViewController(controller: ResponsesViewController, didDeleteResponseAtIndexPath indexPath: NSIndexPath) {
responses.removeAtIndex(indexPath.row)
}
ResponsesViewController.swift
protocol DeleteResponseDelegate {
func responsesViewController(controller: ResponsesViewController, didDeleteResponseAtIndexPath indexPath: NSIndexPath)
}
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle == .Delete {
delegate?.responsesViewController(self, didDeleteResponseAtIndexPath: indexPath)
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Automatic)
tableView.reloadData()
}
}
-EDIT-
It turns out that I was able to fix my issue by simply passing the array from my third view controller back to my first view controller. I was obviously confused about delegation. Here is my updated code where I deleted the item from the array and then passed that array back to the first view controller:
ResponsesViewController.swift
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle == .Delete {
responses.removeAtIndex(indexPath.row)
let mainViewController = navigationController?.viewControllers.first as MainViewController
mainViewController.responses = self.responses
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Automatic)
tableView.reloadData()
}
}
From your comment -
I had assumed that the responses array in MainViewController still
held the objects that it was created with. Is that incorrect? Does the
data travel across view controllers and only have one instance of the
array at all times?
This would be true of an NSArray, which is an object. Swift arrays are structures, and structures are value types. Value types are copied when they are assigned.
In fact, all of the basic types in Swift—integers, floating-point
numbers, Booleans, strings, arrays and dictionaries—are value types,
and are implemented as structures behind the scenes.
All structures and enumerations are value types in Swift. This means
that any structure and enumeration instances you create—and any value
types they have as properties—are always copied when they are passed
around in your code.”
Excerpt From: Apple Inc. “The Swift Programming Language.” iBooks. https://itun.es/au/jEUH0.l