I want to implement a simple tableView in my Viewcontroller but the output is not complete. The content is just visible in one sometimes in two rows.
The classic things:
The class use this:
class MealOfWeekView: UIViewController, UITableViewDelegate, UITableViewDataSource {...}
I set the delegates
override func viewDidLoad() {
super.viewDidLoad()
self.tableViewFood.delegate = self
self.tableViewFood.dataSource = self
self.tableViewFood.reloadData()
}
I use the right identifier:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("foodIdent", forIndexPath: indexPath) as! FoodTableViewCell
cell.dayLabel?.text = "\(day[indexPath.row])"
return cell
}
return 1 section and return 7 rows
=> I use the first time the Tab Bar Controller, in my first tab there is already a tableView. This one works perfect.
The tableView shows as far as I know the days tuesday, saturday or sunday... don't know, whether the info is important :)
EDIT
So with your help I figured out, that my daylabel is nil.
My FoodTableViewCell
class FoodTableViewCell: UITableViewCell {
#IBOutlet weak var dayLbl: UILabel!
}
I add to my viewDidLoad this line:
self.tableViewFood.registerClass(FoodTableViewCell.self, forCellReuseIdentifier: "foodIdent")
But it doesn't work.
If you need more code, give a sign.
Thank you!
Looks like this:
Your issue is with your custom class FoodTableViewCell, but I would verify the following first.
Confirm that the label is being set with the day of the week for the row index. You can do this by setting a breakpoint or printing out statements such as where you create your cells.
print("dayLabel: (cell.dayLabel?.text)")
print("day[indexPath.row]: (day[indexPath.row]")
Confirm you are registering the FoodTableViewCell with the table view.
Confirm that your subclass FoodTableViewCell's dayLabel property is setup correctly. Try changing the background color so you know it is least being displayed in the UI.
Check that your subclass of UITableViewCell is overriding and setting up the dayLabel for reuse correctly.prepareForReuse()
Background information for working with table views
SOLUTION
I found my answer here
When you use Tab Bar Controller you have to use viewDidAppear or viewWillAppear
this lines worked for me:
override func viewWillAppear(animated: Bool) {
self.tableViewFood.delegate = self
self.tableViewFood.dataSource = self
dispatch_async(dispatch_get_main_queue(), {
self.tableViewFood.reloadData()
})
}
Related
Problem I want to allow users to hit 'swap' in a table cell and then find a different Realm object to populate the 2 text labels (for exercise name and number of reps) in the cell with the values from the new object.
Research There's quite a bit (admittedly old) on 'moving rows' (e.g. here How to swap two custom cells with one another in tableview?) and also here (UITableView swap cells) and then there's obviously a lot on reloading data in itself but I can't find anything on this use case.
What have I tried my code below works fine for retrieving a new object. i.e. there's some data in the cell, then when you hit the 'swapButton' it goes grabs another one ready to put in the tableView. I know how to reload data generally but not within one particular cell in situ (the cell that the particular swap button belongs to... each cell has a 'swap button').
I'm guessing I need to somehow find the indexRow of the 'swapButton' and then access the cell properties of that particular cell but not sure where to start (I've played around with quite a few different variants but I'm just guessing so it's not working!)
class WorkoutCell : UITableViewCell {
#IBOutlet weak var exerciseName: UILabel!
#IBOutlet weak var repsNumber: UILabel!
#IBAction func swapButtonPressed(_ sender: Any) {
swapExercise()
}
func swapExercise() {
let realmExercisePool = realm.objects(ExerciseGeneratorObject.self)
func generateExercise() -> WorkoutExercise {
let index = Int(arc4random_uniform(UInt32(realmExercisePool.count)))
return realmExercisePool[index].generateExercise()
}
}
//do something here like cell.workoutName
//= swapExercise[indexRow].generateExercise().name???
}
Hold your objects somewhere in a VC that shows UITableView. Then add the VC as the target to swap button. Implement swapping objects on button press and reload data of table view after.
The whole idea is to move logic to view controller, not in separate cell.
There are 2 ways.
1. Adding VS as button action target.
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = ... // get cell and configure it
cell.swapBtn.addTarget(self, action: #selector(swapTapped(_:)), for: .touchUpInside)
return cell
}
func swapTapped(_ button: UIButton) {
let buttonPosition = button.convertPoint(CGPointZero, toView: self.tableView)
let indexPath = self.tableView.indexPathForRowAtPoint(buttonPosition)!
// find object at that index path
// swap it with another
self.tableView.reloadData()
}
Make VC to be delegate of cell. More code. Here you create protocol in cell and add delegate variable. Then when you create cell you assign to VC as delegate for cell:
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = ... // get cell and configure it
cell.delegate = self
return cell
}
func swapTappedForCell(_ cell: SwapCell) {
// the same logic for swapping
}
Solution from OP I adapted the code here How to access the content of a custom cell in swift using button tag?
Using delegates and protocols is the most sustainable way to achieve this I think.
I hope this helps others with the same problem!
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.
Currently I have a few of custom cell's prototypes created in Storyboard with text fields embedded in them. To access these text fields, I use nameTextField = cell.viewWithTag:(1) in cellForRowAtIndexPath:. But viewDidLoad: and viewWillAppear: methods get called before cellForRowAtIndexPath, so at that time nameTextField is nil. To populate text fields when table view shows on screen, I use viewDidAppear:, but it results in a noticeable delay. Also, when I scroll table view up and down, cellForRowAtIndexPath: gets called again and again, resetting already entered data in text fields.
Are there more efficient ways to populate text fields embedded in custom cells' prototypes with data just before the view shows up, and to prevent resetting of entered data in each cellForRowAtIndexPath: call?
I guess you're creating profile screen (or something with many textField to get input data from user). Am I right?
If I'm right, you can use a static tableView (when you have a few textFields)
Hope this can help.
I'm not sure I understand completely what you're trying to do, but cells are normally configured in the cellForRowAtIndexPath: method, not in viewDidLoad. You can also try connecting the textfield to an outlet on your custom cell class. Then you can do:
// in view controller
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)
as! CustomCell
let object = myDataSource[indexPath.row]
cell.textField.text = object.description
cell.shouldBecomeFirstResponder = indexPath.row == 0
return cell
}
// then in the cell
class CustomCell: UITableViewCell {
#IBOutlet weak var textField: UITextField!
var shouldBecomeFirstResponder: Bool = false
override func awakeFromNib() {
if shouldBecomeFirstResponder {
textField.becomeFirstResponder()
}
}
}
Then when users input text into the textfield, it would make sense to update your data source.
In viewDidLoad try to run something like self.tableView.reloadData before you do this line "nameTextField = cell.viewWithTag:(1)".
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 ....
}
I have a tableView with 7 cells like this:
I wanna trigger some events when you select a cell. For example, start editing the username when you tap the Username row. And pop up a picker view at the bottom with Male/Female selection inside when you tap the Gender row.
As far as I know, I need to put those events inside this:
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
}
But I have no idea how to accomplish this. Anyone has ideas? Thank you in advance.
Basically, you can make each cell has its own picker view.
open class DatePickerTableViewCell: UITableViewCell {
let picker = UIDatePicker()
open override func awakeFromNib() {
super.awakeFromNib()
picker.datePickerMode = UIDatePickerMode.date
}
open override var canBecomeFirstResponder: Bool {
return true
}
open override var canResignFirstResponder: Bool {
return true
}
open override var inputView: UIView? {
return picker
}
...
}
And then in your didSelectRowAt, just make the cell becomeFirstResponder:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if let cell = tableView.cellForRow(at: indexPath) as? DatePickerTableViewCell {
if !cell.isFirstResponder {
_ = cell.becomeFirstResponder()
}
}
}
You can check my library for detail:
https://github.com/hijamoya/PickerViewCell
You are correct. Putting the logic in didSelectRowAtIndexPath is a good way to go.
How you do it is to write code. There is no stock answer.
If you want content to appear on top of the current window then you will need to handle that yourself. On iPad, you could use a popover, but popovers are not supported natively on iPhone/iPod touch. You might look at using a 3rd party popover library that offers popover support for iPhone. There are several on Github, and probably several on Cocoa Controls as well. I've used one before, but it had a few issues, so I wouldn't recommend it.
If you are ok presenting a whole new view controller then simply define a new view controller in your storyboard, give it a unique identifier, use instantiateViewControllerWithIdentifier to create it, then presentViewController:animated: to display it modally.
UIPickerView is subclass of UIView, so you can add and use it same like any other UIView object. for your specific recquirment you should create an object of UIPickerView and show and hide it when necessary.
So create a UIPickerView and add above the table view inside view in which you added tableView and in didSelectRowAtIndexPath set pickerView.hidden = false
And also you can animate it from bottom via
UIView.animateWithDuration(1, animations: { () -> Void in
// And set final frame here
})