I want to display 128 cells in the table view. However, due to some reason the table view displays a maximum of five cells. I checked the number of rows returned by the code, it is greater than 5. So I am sure that part is correct. Also, I have written code for custom cell. Does this contribute to this behavior? If yes, what should I do ? If no, what am I doing wrong ?
/* Custom cell code */
class myCustomCell: UITableViewCell{
#IBOutlet var myTitle: UILabel!
#IBOutlet var mySubtitle: UILabel!
convenience required init(reuseIdentifier: String!){
self.init(style: UITableViewCellStyle.Value1, reuseIdentifier: reuseIdentifier )
}
}
/* code for table view */
import Foundation
import UIKit
import CoreLocation
class TableViewController: UITableViewController{
var rowNumber: String!
override func viewDidLoad() {
super.viewDidLoad()
//println("Count is : \(dataArray.count)")
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func numberOfSectionsInTableView(tableView: UITableView!) -> Int {
return 1;
}
override func tableView(tableView: UITableView!, numberOfRowsInSection section: Int) -> Int {
//println("Here Count is : \(dataArray.count)")
return dataArray.count
}
override func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell! {
let cellId = "cell"
var cell = tableView.dequeueReusableCellWithIdentifier(cellId) as? myCustomCell
//UITableViewCell
if nil==cell {
cell = myCustomCell(reuseIdentifier: cellId)
}
if let ip = indexPath{
var dict: NSDictionary! = dataArray.objectAtIndex(indexPath.row) as NSDictionary
cell!.myTitle.text = dict.objectForKey("name") as String
}
return cell
}
override func tableView(tableView: UITableView!, didSelectRowAtIndexPath: NSIndexPath){
//println("Clicked \(didSelectRowAtIndexPath.row)")
}
override func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!) {
if(segue.identifier == "centerDetails"){
var svc = segue!.destinationViewController as CellClickController
var selectIndex = self.tableView.indexPathForCell(sender as UITableViewCell)
svc.cellIdx = selectIndex.row
}
}
}
Thanks!
The UITableView method dequeueReusableCellWithIdentifier is why only 5-ish cells are instantiated at one time. The UITableView only creates enough UITableViewCell objects are to fill the screen. When one scrolls off and is no longer in view, it is queued for reuse. If you are scrolling quickly, it may create more cells than the screen needs, but generally it will use just enough to cover the screen.
You can create a new UITableViewCell for each index path you display. However it will go out of scope when it scrolls offscreen unless you retain the object reference yourself. You can do this by adding it to an array that your class manages.
Related
I am trying to make a simple Times Table App (for numbers 1-9) in Swift) using a slider and a Table View. I am managing to make the slider work and an array to be created for each number that is selected with the slider and although the array is shown on the console. I cannot get the numbers to appear on the Table View. Can you please help me and tell me what am I missing?
Here is what I have written so far:
class ViewController: UIViewController, UITableViewDelegate {
#IBOutlet var sliderValue: UISlider!
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 9
}
#IBAction func sliderMoved(sender: UISlider) {
sender.setValue(Float(lroundf(sliderValue.value)), animated: true)
print(sliderValue)
var cellContent = [String]()
for var i = 1; i <= 10; i += 1 {
cellContent.append(String(i * Int(sliderValue.value)))
}
print(cellContent)
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: "Cell")
cell.textLabel?.text = cellContent[indexPath.row]
return cell
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
I'm afraid there's quite a lot in the code you've supplied that doesn't make all that much sense. I've mentioned some of it in my comment above but you've also nested what looks like a tableViewDataSource-function into your sliderMoved function. The whole array thing looks rather flakey as well as the proposed cell-count does not actually consider the size of the array. I think you probably want something like this:
class ViewController: UIViewController, UITableViewDataSource {
#IBOutlet var valueSlider: UISlider!
#IBOutlet var tableView: UITableView!
private var cellContent = [String]()
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
}
#IBAction func sliderMoved(sender: UISlider) {
sender.setValue(Float(lroundf(valueSlider.value)), animated: true)
tableView.reloadData()
}
// TableViewDataSource
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 9
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: "Cell") // Must exist with the same identifier in your storyboard
cell.textLabel?.text = valueStringForIndex(indexPath.row)
return cell
}
// Private functions
private func valueStringForIndex(index: Int) -> String {
return "\(index * Int(valueSlider.value))"
}
}
Have you tried creating cellContent array as a instance variable and the following code may work. Check it once.
var cellContent = [String]()
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 9
}
#IBAction func sliderMoved(sender: UISlider) {
sender.setValue(Float(lroundf(sliderValue.value)), animated: true)
print(sliderValue)
for var i = 1; i <= 10; i += 1 {
cellContent.append(String(i * Int(sliderValue.value)))
}
print(cellContent)
self.tableview.reloadData()
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: "Cell")
cell.textLabel?.text = cellContent[indexPath.row]
return cell
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Although not directly an answer to your question ->
Depending on how you want the table displayed, a UICollectionView may be a great fit for this application. Very similar to UITableView to implement but with boxes and columns of data, may be simpler to format (and changing the slider could add some fun animation when the collection view updates).
The sample UIViewController below demonstrates using a UICollectionView. In the storyboard, I simply:
Added a UISlider, UICollectionView, and UILabel and created outlets in MultiplicationTableViewController
In the UICollectionView default cell I gave it the reuseIdentifier "numberCell", and added a label (to hold the product)
Made the MultiplicationTableViewController the dataSource and delegate for the UICollectionView
CODE:
import UIKit
class MultiplicationTableViewController: UIViewController {
#IBOutlet var timesTableCollectionView: UICollectionView!
#IBOutlet weak var numberSlider: UISlider!
#IBOutlet weak var label: UILabel!
var products = [Int]() //array to hold the computed value for each cell in the collectionView
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(self.viewRotated), name: UIDeviceOrientationDidChangeNotification, object: nil) //register for rotation notifications
products = createTableOfValues() //populate products with initial values
label.text = "\(Int(numberSlider.value)) x \(Int(numberSlider.value))"
}
#IBAction func sliderUpdated(sender: UISlider) {
sender.value = Float(Int(sender.value)) //make the slider stop only on whole numbers
label.text = "\(Int(sender.value)) x \(Int(sender.value))"
products = createTableOfValues() //create the new table values
timesTableCollectionView.reloadData() //tell the collectionView to read the new data and refresh itself
}
func createTableOfValues() -> [Int] {
var prod = [Int]() //temp array to hold the generated products
for row in 0...Int(numberSlider.value) { //iterate from row 0 (header) to
var columns = [Int]() //temp array to build column products
for column in 0...Int(numberSlider.value) {//iterate through each column, including column 0 (header)
if column == 0 {
columns.append(row)
} else if row == 0 {
columns.append(column)
} else {
columns.append(column * row)
}
}
prod.appendContentsOf(columns) //add the current row of products to the temp array
}
return prod
}
func viewRotated() {
timesTableCollectionView.reloadData() //called to force collectionView to recalc (basically to get new cell sizes
}
}
extension MultiplicationTableViewController: UICollectionViewDataSource {
func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 1 //required for UICollectionViewDataSource
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return Int(numberSlider.value + 1) * Int(numberSlider.value + 1) //tells the UICollectionView how many cells to draw (the number on the slider, plus header rows)
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("numberCell", forIndexPath: indexPath) //get an existing cell if it exists
if cell.frame.origin.y == 0.0 || cell.frame.origin.x == 0.0 { //if the cell is at the top or left of the collectionView
cell.backgroundColor = UIColor.yellowColor()
} else {
cell.backgroundColor = UIColor.clearColor() //If not, reset the color (required because cells are reused
}
cell.layer.borderColor = UIColor.blackColor().CGColor
cell.layer.borderWidth = 1.0
let numberItem = cell.viewWithTag(101) as? UILabel //get a reference to the label in the current cell
numberItem?.text = String(products[indexPath.row]) //get the value generated earlier for this particular cell
return cell
}
}
extension MultiplicationTableViewController: UICollectionViewDelegateFlowLayout {
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
let columns = CGFloat(numberSlider.value + 1) //get the number of columns - slider value + 1 for header
let width = timesTableCollectionView.bounds.width / columns //divide the width of the collectionView by the number of columns
return CGSizeMake(width, width) //use width value to make the cell a square
}
}
Screenshot:
I get an error by the IBoutlet of tableView saying "cannot override with stored property "tableView"". I'm not quite sure what this means, any type of help will be appreciated.
import UIKit
class UsersViewController: UITableViewController {
#IBOutlet var tableView: UITableView!
var users = ["Marc", "Mark" , "Louise", "Ahinga", "Amossi", "Kalenga"]
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(tableView:UITableView, numberOfRowsInSection section:Int) -> Int {
return 3
}
override func tableView(tableView:UITableView, cellForRowAtIndexPath indexPath:NSIndexPath) -> UITableViewCell {
let cell = self.tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! userCell
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
}
}
A UITableViewController comes with a UITableView built in, aptly named tableView. In the code you pasted you are adding an extra UITableView to it and giving it the same name. That's what the compiler is complaining about.
In theory, when you are using a plain vanilla UITableViewController from the storyboard you should not link the table to an IBOutlet. That's taken care of.
If you are building something more sophisticated that needs an extra UITableViewController on top of the one that the class provides in storyboard, then you should name it differently.
you have to set as it's delegate and dataSource in viewDidLoad() like this
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.delegate = self
self.tableView.dataSource = self
}
Iv'e made a code for a simple tableview app and with textfield and a button.
When I click on the button it adds to the array but do not show it on the table view.
What can I do to see it?
Here is the code:
import UIKit
class ViewController: UIViewController, UITableViewDelegate {
#IBOutlet weak var textfieldd: UITextField!
#IBOutlet var tasksTable:UITableView!
var toDoList:[String] = []
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func tableView(tableView: UITableView!, numberOfRowsInSection section: Int) -> Int {
return toDoList.count
}
#IBAction func Add(sender: AnyObject) {
toDoList.append(textfieldd.text)
println(toDoList)
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell! {
var cell = UITableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: "Cell")
cell.textLabel?.text = toDoList[indexPath.row]
return cell
}
}
You've updated your data source (the array), but not the actual display (the table view). You should call the reloadData() function whenever you want to update your table view:
tasksTable.reloadData()
You need to reload the TableView after you've added the new element to the list.
You can do so with the reloadData method.
tasksTable.reloadData()
You are not applying the UITableviewDataSource protocol to your class, just the delegate.
Add it and set the datasource for the table to the class?
I'm trying to make a view controller that has one text field that populates the tableview below, ideally the user will be able to continue to add to the tableview without jumping between two views.
I previously had it working with the text field on one view that populates a UITableView and used prepareForSegue to push the data to the table, but I haven't been able to get it to work with just one view.
Can anyone please point out where I'm going wrong or push me to a tutorial / documentation to help?
Edit: Clarity
import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UITextFieldDelegate {
#IBOutlet var tableView: UITableView!
#IBOutlet weak var textField: UITextField!
var items: [String] = ["Pls", "work", "pls", "work", "pls"]
var foodGroup: FoodGroup = FoodGroup(itemName:"")
//var foodGroup: [FoodGroup] = []
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: "cell")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.items.count;
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell:UITableViewCell = self.tableView.dequeueReusableCellWithIdentifier("cell") as UITableViewCell
cell.textLabel.text = self.items[indexPath.row]
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
println("Selected cell #\(indexPath)")
}
func addFood(sender: AnyObject!) {
if (countElements(self.textField.text) > 0) {
self.foodGroup = FoodGroup(itemName: self.textField.text)
}
}
#IBAction func addFoodToList() {
let source = FoodGroup
let foodGroup:FoodGroup = source.foodGroup
if foodGroup.itemName != "" {
self.foodGroup.append(foodGroup)
self.tableView.reloadData()
}
}
}
It seems like your intention here is to have your dataSource be an array of FoodGroup objects. If this is indeed the case you can get rid of your foodGroup instance variable and update your items definition to be like so:
var items = [FoodGroup]()
then in addFoodToList:
if self.textField.text != "" {
let foodGroup = FoodGroup(itemName: self.textField.text)
self.items.append(foodGroup)
self.tableView.reloadData()
}
and finally in cellForRowAtIndexPath:
var cell = self.tableView.dequeueReusableCellWithIdentifier("cell") as UITableViewCell
let foodGroup = self.items[indexPath.row] as FoodGroup
cell.textLabel.text = foodGroup.itemName
return cell
Also I don't quite see the intention of your the addFood(sender: AnyObject!) function. Looks like cruft. I would get rid of it. Good luck!
I am new to Swift and I dont have experience in coding in Objective C. The problem I am facing is that, when I populate data from the class object into table view cells all the cells show the same data (the last recently accessed array object). Could someone help me with this ?
Thanks.
class TableViewController: UITableViewController{
var urgentCenters:Array<UrgentCenterDetails> = []
override func viewDidLoad() {
super.viewDidLoad()
self.loadInfo()
println("Number of entries: \(self.urgentCenters.count)")
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func numberOfSectionsInTableView(tableView: UITableView!) -> Int {
return 1;
}
override func tableView(tableView: UITableView!, numberOfRowsInSection section: Int) -> Int {
//println("Number of rows \(urgentCenters.count)")
return self.urgentCenters.count
}
override func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell! {
let cellId = "cell"
var cell = tableView.dequeueReusableCellWithIdentifier(cellId) as? UITableViewCell
if nil==cell {
cell = UITableViewCell(style: UITableViewCellStyle.Value1, reuseIdentifier: cellId)
}
if let ip = indexPath{
cell!.textLabel.text = urgentCenters[indexPath.row].title
}
return cell
}
func loadInfo(){
// Append data to urgentCenters data
var center = UrgentCenterDetails()
var title:String
title = "Penn's Landing"
center.setCenterDetails(title)
urgentCenters.append(center)
title = "Liberty Bell"
center.setCenterDetails(title)
urgentCenters.append(center)
}
}
Your problem is that you are only creating a single UrgentCenterDetails object and adding it to the array multiple times. Because there is only a single object, when you change its properties you are affecting all elements of the array (because it is the same object in all elements).
You should have -
func loadInfo(){
// Append data to urgentCenters data
var title:String
title = "Penn's Landing"
center.setCenterDetails(title)
urgentCenters.append(center)
center = UrgentCenterDetails()
title = "Liberty Bell"
center.setCenterDetails(title)
urgentCenters.append(center)
}