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.
Related
I am puzzled by the behavior of tableView if you leave their view and come back.
I have a screen with one tableView in it that works when I first enter the view. Adding, removing, and updating table cells work. However, when I press a button to segue into the next view and immediately come back, the tableView no longer works. The code that is supposed to execute ( tableView.reload() and all the associated methods) run as they should. However, the screen does not get updated even though internally the arrays get updated, and reload gets ran and executes the code that should update the screen( that is, tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell runs jus fine).
What am I missing? Does tableView require any special treatment if I leave the view and come back to ti?
Thanks.
Edit:
The code for the class where the tableView is something like:
class DebugViewController: UIViewController, UITableViewDataSource, UITableViewDelegate{
#IBOutlet weak var table: UITableView!
var array = [M]()
override func viewDidLoad() {
super.viewDidLoad()
self.searchbar.delegate = self
self.table.delegate = self
self.table.dataSource = self
search_view = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "Search_view") as? SomeViewController
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "cellManage") as? TableCellManage else { return UITableViewCell() }
let idx = indexPath.row
let value = array[idx]
cell.lbl_time.text = value.month
cell.lbl_background.text = value.color
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 130
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.array.count
}
#IBAction func tapped_addsongs(_ sender: Any) {
self.present( search_view, animated: true, completion: nil)
}
}
Where is the tableView.reload() part? The issue might be generated because you're trying to update the table in a background thread. All UI Updates must be done in main thread:
func someFunc() {
...
DispatchQueue.main.async {
tableView.reload()
}
...
}
After looking at pretty pictures of the life cycle of apps and googling I found the issue and the solution.
The problem was that I had listeners set up to update my table view in a troublesome way. Specifically, I was using the viewDidAppear/viewDidDisappear to bring up and down the listeners, and there was some conflict in the code that managed the state because of this.
Instead, I now bring up the listeners on viewDidLoad. They stay active, regardless of how many views are pushed (within reason, but I only push one), and update my tableview so that when I come back to that view everything is already updated. I don't even see the updates happening, they happen before I get to my view. As for detaching the listeners there is a handy-dandy function I did not know about until 5 minutes ago: deinit. This is the equivalent of destructor in Swift, so I detach my listener when my class object for this view is released from memory.
That solves my issue...and increases performance and I no longer have dangling connections for not managing the listeners well. So a win-win-win.
Thank you all for trying to help! I hope this helps other folks.
again me with a short question since Swift is confusing me ATM - but I hope I will get used to it soon ;)
Ok so what I was wondering is: When I call a TableView and generate different Cells is there a way to Interrupt after a few and wait for User Input and react to it?
For Example: 2nd Cell is something like "Go to North or West" after that I want a User Input - with Buttons - in whatever direction he likes to go and react to it with following Cells (different Story Parts -> out of other Arrays?).
What is confusing me is that I just load the whole Application in viewDidLoad and i don't know how I can control the "Flow" within this.
I would really appreciate if someone could show me how I can achieve this maybe even with a small description about how I can control the Program Flow within the Method. I really think this knowledge and understanding would lift my understanding for Swift a few Levels higher.
Thanks in advance!
Here is my current Code which is not including any functionality for the named Question since I don't know how to manage this :)
import UIKit
class ViewController: UIViewController {
var storyLines = ["Test Cell 1 and so on first cell of many","second cell Go to North or West","third Cell - Buttons?"]
var actualTables = 1
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
tableView.dataSource = self
}
#IBOutlet weak var tableView: UITableView!
}
extension ViewController : UITableViewDataSource{
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return storyLines.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "TxtLine", for: indexPath)
cell.textLabel?.text = storyLines[indexPath.row]
cell.textLabel?.numberOfLines = 0;
cell.textLabel?.lineBreakMode = .byWordWrapping
return cell
}
}
Cocoa is event driven. You are always just waiting for user input. It's a matter of implementing the methods that Cocoa has configured to tell you about it.
So here, for example, if you want to hear about when the user presses a button, you configure the button with an action-and-target to call a method in your view controller when the user presses it. That way, your method can see which button the user pressed and remake the table view's data model (your storyLines array) and reload the table with the new data.
I've been searching for awhile without luck. I am trying to find an example of a View Controller with a UITableView that has sections. The examples I've see are all dealing with a Table View Controller which I cannot use as I have need of buttons in the same view which control the content of the table view. Anyone have an example, know of an example or have an idea about to implement such? Thanks.
Edit
I've got a table view in a view controller, get the data from an api call, separate the sections and data in an array of a struct. I then send this to be bound to the table view. Doing so throws
[UIView tableView:numberOfRowsInSection:]: unrecognized selector sent to instance
but I don't understand where the problem is.
Code for the tablview
//MARK: Tableview delegates
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
if let count = incidentDataSection?.count{
return count
}
return 0
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if (incidentDataSection?.count)! > 0{
return incidentDataSection![section].incidents.count
}
return 0
}
func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return incidentDataSection?[section].title
}
/*
func tableView(tableView: UITableView, iconForHeaderInSection section: Int) -> UIImage? {
return incidentDataSection?[section].icon
}*/
//if clicked, will openn details view passing in the details
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
//let incidentDetails = incidentData?[indexPath.row]
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if let section = incidentDataSection?[indexPath.section] {
let cell = tableView.dequeueReusableCell(withIdentifier: "IncidentTableViewCell") as! IncidentTableViewCell
cell.roadNameLabel.text = section.incidents[indexPath.row].RoadWay
cell.whenLabel.text = section.incidents[indexPath.row].DateCreated
cell.statusLabel.text = section.incidents[indexPath.row].DateCleared
return cell
}
return UITableViewCell()
}
incidentDataSection is an array of a struct which has the section title and the different items.
Answer
Though I received some fairly good feedback, the cause was actually a typo. Looking closely at
func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return incidentDataSection?[section].title
}
you'll notice the problem is that there is no underscore before tableView:. What was happening is that the datasource and delegate were skipping over the functions since with and without call different protocols in swift 3. Thanks to thislink I was able to figure out the cause. My bad for forgetting to mention this was in Swift 3. Might had saved everyone some time.
You need a tableview instance in your view controller.
Implement the protocols UITableViewDelegate, UITableViewDataSource in your view controller as a UITableViewController.
Don't forget bind the tableview in XIB with tableview in the class.
Look this sample:
class Sample01ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var tableView: UITableView?
override func viewDidLoad() {
super.viewDidLoad()
tableView?.delegate = self
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(true)
self.tableView?.reloadData()
}
// ...
You have the required methods implemented, however it sounds like you need to "subclass" or "subcribe" to the UITableView's delegate and dataSource. By using:
class MyViewController : UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet var tableView : UITableView!
}
Now that you have those protocols you will need to set your tableView's delegate and dataSource to your viewController. You can do this using storyboard by drag and drop, or inside of your viewDidLoad() which is what I always do because it is easy for other developers to see from the start of opening your code where your delegate and dataSources are assigned to. Using:
#override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
}
Then your delegate methods and dataSource methods in your viewcontroller will be called for that tableView. Then you can add the IBOutlets to UIButton/UILabel/UISwitch, etc... and do what you will with your ViewController without being limited to simply using a table view inside of that view controller. I Almost always use this methods when using UITableViews/UICollectionViews even if I set the tableView/collectionView to be the size of the whole view because I like the freedom of using a UIViewController over a UITableViewController/UICollectionViewController.
*Note numberOfRows() is not required but I always override it as well, just kind of a habit at this point. Also you sound new to iOS development, so if you aren't already, the next thing I would look into after getting your tableView up and running is pulling your data from your API on a background thread to keep your mainThread open for user response on your UI, DispatchQueue. This is really important if you are displaying images from the API.
I have the following problem:
I am making a Pokédex-like application that displays a list of all 721 Pokémon on the first tab, and another list on the second tab containing My Favorite Pokémon. Essentially, there are two identical ViewControllers connected to my TabBar.
My storyboard is as follows:
So here is the problem:
The TableView on the first (and initial) tab works fine. However, when I load the TableView on the second tab the Pokémon are loaded, but not displayed. I am able to click the TableViewCell and go to the detail page, but the label in the TableViewCell is not showing anything.
This is the code I use for loading Favorites TableView
class FavoritesViewController: BaseViewController,
UITableViewDataSource, UITableViewDelegate {
#IBOutlet var FavoritesListView: UITableView!
var pokemonList: [String] = ["Nothing Here!"]
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("FavoriteCell", forIndexPath: indexPath) as! FavoriteCell
var name = pokemonList[indexPath.row]
capitalizeFirstLetter(&name)
cell.nameLabel.text = name
return cell;
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return pokemonList.count
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
print(pokemonList[indexPath.row])
self.performSegueWithIdentifier("ToPokemonDetail", sender: pokemonList[indexPath.row])
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if(segue.identifier == "ToPokemonDetail"){
let destination = segue.destinationViewController as! PokemonDetailViewController
let thisPokemon = sender as! String
destination.currentPokemon = thisPokemon
}
}
override func viewWillAppear(animated: Bool) {
FavoritesListView.reloadData()
}
override func viewDidLoad() {
super.viewDidLoad()
// Fetch the cached list, getNames returns an array of strings
let list = utility.getNames("Favorites")
pokemonList = list
}
The delegate and the dataSource are set via the storyboard.
The above code works, and shows the Favorites list just fine. The class for the complete Pokédex has a similar construction.
I have tried switching Favorites and Pokédex around, so that it shows the complete Pokémon list on startup. All 721 Pokémon are shown correctly, but then the Favorites are not visible.
What else I have tried:
Checking the Reuse Identifiers, over and over
Referencing outlets should be bound correctly
Calling TableView.reloadData() in the viewDidAppear method
Switching around the tab items
Does anyone have any clue what on earth is going on here?
Feel free to ask any more questions
Edit: this is what happens when I swap the two TabBar Buttons around, no code changes
Pokédex Screen
Favorites Screen
GitHub Project Here
Problem is in storyboard cell label frame. Set constraints of view controller for (Any,Any) Size Class. I can commit the code on github if you can give me write rights on your git. Thanks
Perhaps your table's delegate and dataSource are not set.
table.delegate = self
table.dataSource = self
Of course this is after you add the properties to your view controller
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
Your number of rows is always 0 for that controller,
I looked into your code pokemonList count is always 0 its not updating data in it
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return pokemonList.count
}
The big issue is your PokemonDetailViewController is not a UITableViewController. It needs to inherent from UITableViewDataSource, UITableViewDelegate and then be connected to the storyboard view to provide data and formatting for a table.
First of all, I've looked around to find a solution to my problem, here and on other websites. If I've missed something please show me the link, i didn't intend on bugging you with my problem if there is a solution somewhere else.
My idea was to create an app (just for myself as a practise since I'm fairly new to swift) that would get the NBA schedule from a website, extract the games and results and show them in a table. For that I made a textField where the user could enter from which game day he wanted the results. The Integer he enters changes the url and the url is propperly spilt up and the data I want to display is saved in an array as a string.
Thats were my problem occurs. The items are appended to the array and the array.count displays the right number depending on the day the user entered. The only problem is that the data from the array is not display in the table cell. I've rewrote the code and made sure I didn't mess up the table, but as soon as I add the second part of the app (the information that got from the URL) to the app, the cells don't display anything.
It's kind of weird because both parts are working fine on their own, but as soon as I combine them my problem occurs. Do you know where I might have messed up?
Does anyone have an idea what my mistake may be? I'm not looking for code solutions, just for someone who might tell me where the flaw in my logic is. Maybe i missed something, but i don't get why my cells are not displaying the elements of the array, even though the array is set up properly.
Thanks in advance to anyone answering and have a nice day!
Greetings!
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var gamesArray = [String]()
var gameDay:Int = 0
#IBOutlet var textField: UITextField!
#IBOutlet var gamesTable: UITableView!
#IBAction func enterButton(sender: AnyObject) {
gameDay = Int(textField.text!)!
// webCodeArrayForGames is where i temporarily put the strings I want to add
for var counter = 1; counter<webCodeArrayForGames.count; counter++{
self.gamesArray.append(webCodeArrayForGames[counter])
}
override func viewDidLoad()
{
super.viewDidLoad()
gamesTable.delegate = self
gamesTable.dataSource = self
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return gamesArray.count
}
func TableView(tableView: UITableView, cellforRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell{
let cell = UITableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: "cell")
cell.textLabel?.text = gamesArray[indexPath.row]
return cell
}
override func viewDidAppear(animated: Bool) {
self.gamesTable.reloadData()
}
}
You can check out this one:
https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/TableView_iPhone/TableViewAPIOverview/TableViewAPIOverview.html
On the short, you have to provide UITableViewDelegate and UITableViewDataSource.
From the code, I suspect that you didn't provide the dataSource for your table:
"The data source adopts the UITableViewDataSource protocol. UITableViewDataSource has two required methods. The tableView:numberOfRowsInSection: method tells the table view how many rows to display in each section, and the tableView:cellForRowAtIndexPath: method provides the cell to display for each row in the table. "
You could do this way:
Subclass your viewController from UITableViewDelegate and UITableViewDataSource
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource { ....
in viewDidLoad() assign the tableview's delegate and dataSource:
override func viewDidLoad() {
super.viewDidLoad()
gamesTable.delegate = self
gamesTable.dataSource = self ....
}