I have a UITableViewController with three labels. The first two are from the .textLabel and .detailTextLabel, I have added a third label into the storyboard and hooked it up to a UITableViewCell file. When I run, the app crashes unless the label is set as an optional with a "?", but nothing is still presented in the table. If it does not have the optional sign, it crashes and I get a response saying "unexpectedly found nil while unwrapping an optional value". The other two .textLabel and .detailTextLabel work fine. I would appreciate any help!
Here is my TableViewController File,
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.groupscores.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier(cellidentifier) as? ScoresPageCell
if (cell != nil) {
cell = ScoresPageCell(style: UITableViewCellStyle.Subtitle, reuseIdentifier: cellidentifier)
}
let groupscorelist: PFObject = self.groupscores.objectAtIndex(indexPath.row) as! PFObject
var scores: AnyObject = groupscorelist.objectForKey("Score")!
var user: AnyObject = groupscorelist.objectForKey("User")!
var info: AnyObject = groupscorelist.objectForKey("Info")!
cell!.textLabel?.text = "\(scores)"
cell?.detailTextLabel?.text = "\(user)"
cell!.UserNameCellLabel?.text = "\(info)"
return cell!
}
And my UITableViewCell File,
class ScoresPageCell: UITableViewCell {
#IBOutlet var UserNameCellLabel: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
}
}
Follow this my friend:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier(cellidentifier, forIndexPath: indexPath) as! ScoresPageCell
let groupscorelist = self.groupscores[indexPath.row]
var scores = groupscorelist["Score"] as! String // cast it to String if they are string.
var user = groupscorelist["User"] as! String
var info = groupscorelist["Info"] as! String
cell!.text1?.text = scores
cell?.text2?.text = user
cell!.UserNameCellLabel?.text = info
return cell
}
class ScoresPageCell: UITableViewCell {
#IBOutlet weak var text1: UILabel!
#IBOutlet weak var text2: UILabel!
#IBOutlet weak var UserNameCellLabel: UILabel!
}
Related
import UIKit
class ViewController: UIViewController,UITableViewDelegate, UITableViewDataSource, pass {
var array = [String]()
#IBOutlet weak var tblView: UITableView!
#IBOutlet weak var btnPush: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return array.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "TableViewCell", for: indexPath) as!TableViewCell
cell.lblName.text = array[indexPath.row]
cell.lblFullName.text = array[indexPath.row]
cell.lblRollno.text = array[indexPath.row]
cell.lblClass.text = array[indexPath.row]
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 200
}
#IBAction func btnPush(_ sender: UIButton) {
let vc = storyboard?.instantiateViewController(withIdentifier: "SecondVC") as!SecondVC
vc.mac = self
self.navigationController?.pushViewController(vc, animated: true)
}
func Datapass(Name: String, FullName Address: String, Rollno: String, Class: String) {
self.array.append(Name)
self.array.append(Address)
self.array.append(Rollno)
self.array.append(Class)
tblView.reloadData()
}
}
import UIKit
protocol pass {
func Datapass(Name:String, FullName:String, Rollno:String, Class:String)
}
class SecondVC: UIViewController {
#IBOutlet weak var textFldName: UITextField!
#IBOutlet weak var txtFldFullName: UITextField!
#IBOutlet weak var txtFldRollno: UITextField!
#IBOutlet weak var txtFldClass: UITextField!
var mac:pass?
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func btnAdd(sender: UIButton) {
mac?.Datapass(Name: textFldName.text!, FullName: txtFldFullName.text!, Rollno: txtFldRollno.text!, Class: txtFldClass.text!)
self.navigationController?.popViewController(animated: true)
}
}
your adding data to your array the wrong way. your adding 4 parameter
and your array will be like this:
array: 0:name|1:Address|2:Rollno|3:Class
and when your reading this from table every time you map this:
cell.lblName.text = array[0]
cell.lblFullName.text = array[0]
cell.lblRollno.text = array[0]
cell.lblClass.text = array[0]
every time table only reads one element of array for 4 times! you are not changing indexpath.row , it will change it's number after loading 1 cell.
the right way to do this is code below:
struct DbModel{
var name:String
var address:String
var rollno:String
var `class`:String
}
var array :[DbModel] = []
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "TableViewCell", for: indexPath) as! TableViewCell
let model = array[indexPath.row]
cell.lblName.text = model.name
cell.lblFullName.text = model.address
cell.lblRollno.text = model.rollno
cell.lblClass.text = model.class
return cell
}
func Datapass(Name: String, FullName Address: String, Rollno: String, Class: String) {
let model = DbModel(name: Name, address: Address, rollno: Rollno, class: Class)
self.array.append(model)
tblView.reloadData()
}
Edited:
I'm trying to insert two arrays into one tableview with two cell labels. But not really sure how I can convert it all and make it work. Here is my code so far.
In the top of the main ViewController (Where the tableView is)
class ScoreBoardViewController: UIViewController, UITableViewDelegate, UITableViewDataSource
var countedArray: [String] = []
var nameArray: [String] = []
#IBOutlet weak var tableView: UITableView!
var list: [pointsTxt] = []
Additional info: I've added the self.tableView.delegate = self
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return list.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let listPath = list[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: "pointsCell") as! ScoreCell
cell.setCell(list: listPath)
return cell
}
func createArray() -> [pointsTxt]
{
var tempTxt: [pointsTxt] = []
let txt = pointsTxt(person: nameArray, points: countedArray)
tempTxt.append(txt)
self.list = tempTxt
self.tableView.reloadData()
return list
}
Class for the cell
class ScoreCell: UITableViewCell
{
#IBOutlet weak var person: UILabel!
#IBOutlet weak var points: UILabel!
func setCell(list: pointsTxt)
{
person.text = list.person
points.text = list.points
}
}
ERROR: Cannot assign value of type '[String]' to type 'String?'
Class for the array
class pointsTxt
{
var person: [String] = []
var points: [String] = []
init(person: [String] = [], points: [String] = [])
{
self.person = person
self.points = points
}
}
I hope you understand what I need. Thanks in advance!
You have to follow the following steps when creating a custom tableViewCell:
Subclass UITableViewCell
Add your Outlets to that subclass
register your subclass to the TableView
code Example for your rowAtIndexPath function:
.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row < nameArray.count {
// load from customerDetails array
let names = nameArray[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: "listCell", for: indexPath) as! UITableViewSubclassCell
cell.person.text = names
return cell
} else {
// load from customerDetails2 array
let points = countedArray[indexPath.row - nameArray.count]
let cell = tableView.dequeueReusableCell(withIdentifier: "listCell", for: indexPath) as! UITableViewSubclassCell
cell.person.text = points
return cell
}
}
The issue is as follows: I have a tableview with a custom cell. That cell contains a label and a UISwitch. I have set the label.text value to an array, but the UISwitch is getting reused.
Example: If I toggle the switch in the first row, the 5th row gets enabled, and if I scroll it continues to reuse the cells and cause issue.
Video : https://vimeo.com/247906440
View Controller:
class ViewController: UIViewController {
let array = ["One","Two","Three","Four","Five","Six","Seven","Eight","Nine","Ten"]
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
}
}
extension ViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! CustomTableViewCell
cell.label.text = array[indexPath.row]
return cell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return array.count
}
}
Custom Cell:
class CustomTableViewCell: UITableViewCell {
#IBOutlet weak var label: UILabel!
#IBOutlet weak var toggleSwitch: UISwitch!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
}
I realize there isn't code trying to store this data because I haven't been successful. Any ideas would be helpful. The project currently uses the MVC model and I believe that is the answer but just need some help.
I would recommend to you create cellViewModel class and keep array of it instead of just string. You cellViewModel may look like,
class CellViewModel {
let title: String
var isOn: Bool
init(withText text: String, isOn: Bool = false /* you can keep is at by default false*/) {
self.title = text
self.isOn = isOn
}
Now, build array of CellViewModel
let array =["One","Two","Three","Four","Five","Six","Seven","Eight","Nine","Ten"]
var cellViewModels = [CellViewModel]()
for text in array {
let cellViewModel = CellViewModel(withText: text)
cellViewModels.append(cellViewModel)
}
Change your tableVieDelegate function to :
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! CustomTableViewCell
let cellViewModel = cellViewModels[indexPath.row]
cell.label.text = cellViewModel.title
cell.toggleSwitch.isOn = cellViewModel.isOn
cell.delegate = self
return cell
}
In you Custom Cell class, add this protocol :
protocol CellActionDelegate: class {
func didChangeSwitchStateOnCell(_ cell: CustomTableViewCell)
}
Add delegate as property in your custom cell,
weak var delegate: CellActionDelegate?
Also, on switch change, add this line,
delegate?.didChangeSwitchStateOnCell(self)
Now, your viewController should register and listen to this delegate :
I have added line cellForRowAtIndexPath to register for delegates. To listen this delegate, add this function in your VC.
func didChangeSwitchStateOnCell(_ cell: CustomTableViewCell) {
let indexPath = tableView.indexPath(for: cell)
cellViewModels[indexPath.row].isOn = cell.toggleSwitch.isOn
}
start creating a model for example :
struct item {
var id: String
var name: String
var isActivated: Bool
init(id: String, name: String, isActivated: Bool) {
self.id = id
self.name = name
self.isActivated = isActivated
}
}
let item1 = item(id: "1", name: "One", isActivated: false)
let item2 = ...........
let item3 = ...........
let items [item1, item2, item3]
With that you can trigger the boolean if it's activated or not.
You will also have to take a look to https://developer.apple.com/documentation/uikit/uitableviewcell/1623223-prepareforreuse I think.
So I'm customizing a tableview to hold multiple columns. I want 3 columns, and am customizing the TableViewCell, except I'm at a roadblock.
Right now I have a TableView that is in a ViewController, and the TableView accurately holds one column of data. Here I am changing it to three columns and I get an error about unwrapping an optional nil value.
Here's the important parts of viewcontroller with the tableview (FinishTestController.swift):
var bestRank: [String] = ["1", "2", "3", "4", "5"]
var bestScore: [String] = ["-----", "-----", "-----", "-----", "-----"]
var bestTime: [String] = ["-----", "-----", "-----", "-----", "-----"]
override func viewDidLoad() {
super.viewDidLoad()
addhighscore()
loadhighscores()
self.tableView.registerClass(TableViewCell.self, forCellReuseIdentifier: "cell")
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return bestRank.count;;
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! TableViewCell
cell.column1.text = self.bestRank[indexPath.row]//<-- ERROR points here
cell.column2.text = self.bestScore[indexPath.row]
cell.column2.text = self.bestTime[indexPath.row]
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
println("You selected cell #\(indexPath.row)!")
}
func loadhighscores(){
var result = db.query("SELECT * from EASY_MATH5 ORDER BY Score DESC, Time ASC LIMIT 5", parameters: nil)
println("===============================")
for row in result
{
bestScore[i] = row["Score"]!.asString()
print(bestScore[i])
bestTime[i] = row["Time"]!.asString()
println(bestTime[i])
i++
}
}
Here's my cell:
class TableViewCell: UITableViewCell {
#IBOutlet weak var column1: UILabel!
#IBOutlet weak var column2: UILabel!
#IBOutlet weak var column3: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
And here's the error I get:
fatal error: unexpectedly found nil while unwrapping an Optional value
(lldb)
and points to the line that says "cell.column1.text = self.bestRank[indexPath.row]" with a line "Thread 1: EXC_BAD_INSTRUCTION".
Any idea on how to resolve?
Remove
self.tableView.registerClass(TableViewCell.self, forCellReuseIdentifier: "cell")
From viewDidLoad(), you don't need to register your UITableViewCell subclass if you're using prototype cells.
Using your code I made minor adjustments which I've noted in the comments with "// nb: " - it works fine now - once those minor points were removed.
Only other change in my code that I did, was the use of "Cell1" instead of "cell" and name of custom cell as "CustomTableViewCell" instead of "TableViewCell", but this is only from personal habit.
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet var tableView: UITableView!
var bestRank : [String] = ["1", "2", "3", "4", "5"]
var bestScore: [String] = ["-----", "-----", "-----", "-----", "-----"]
var bestTime: [String] = ["-----", "-----", "-----", "-----", "-----"]
// --------------------------------------
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.bestRank.count // nb: use of "self." and no ";;" at end
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell1", forIndexPath: indexPath) as! CustomTableViewCell
cell.column1.text = self.bestRank[indexPath.row]
cell.column2.text = self.bestScore[indexPath.row]
cell.column3.text = self.bestTime[indexPath.row]
return cell
}
// --------------------------------------
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
print("You selected cell #\(indexPath.row)!")
}
// --------------------------------------
override func viewDidLoad() {
super.viewDidLoad()
// nb: Not used: "self.tableView.registerClass(TableViewCell.self, forCellReuseIdentifier: "cell")"
// nb: Datasource + delegate already assigned with tableview in storyboard with click-drag-drop into ViewController.
}
CustomCell was done like yours...
class CustomTableViewCell: UITableViewCell {
#IBOutlet var column1: UILabel!
#IBOutlet var column2: UILabel!
#IBOutlet var column3: UILabel!
** Results = No Error **
Simulator showing table as you wanted...
You should most likely be using a UICollectionView for this type of behaviour.
With a collection view you will have more control of the layout of each cell.
https://developer.apple.com/library/ios/documentation/UIKit/Reference/UICollectionView_class/
I have created a UITableView and a UITableVIewCell in Main.storyboard and set it's dataSource and delegate to ViewController .Why UITableView didn't display texts when I run the code.
Another question is that does UITableView load before ViewLoad? If not why in func didRecieveResults() the Array of tableData can achieve datas but in func tableView() it was nil
The whole codes as following
import UIKit
class ViewController: UIViewController,UITableViewDataSource,UITableViewDelegate,HttpProtocol {
#IBOutlet weak var tv: UITableView!
#IBOutlet weak var iv: UIImageView!
#IBOutlet weak var playTime: UILabel!
#IBOutlet weak var progressView: UIProgressView!
var eHttp:HttpController = HttpController()
var tableData:NSArray = NSArray()
var channelData:NSArray = NSArray()
override func viewDidLoad() {
super.viewDidLoad()
eHttp.delegate = self
eHttp.onSearch("http://www.douban.com/j/app/radio/channels")
eHttp.onSearch("http://douban.fm/j/mine/playlist?channel=0")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
println("tableData.count:\(channelData)")
return 10
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell!{
let cell = UITableViewCell(style:UITableViewCellStyle.Subtitle,reuseIdentifier:"douban")
let rowData:NSDictionary = self.tableData[indexPath.row] as! NSDictionary
cell.textLabel!.text = "hehehehe"//rowData["title"] as! String
cell.detailTextLabel!.text = "adasdasda"//rowData["artist"] as! String
return cell
}
func didRecieveResults(results:NSDictionary){
if (results["song"] != nil){
self.tableData = results["song"] as! NSArray
println(tableData)
}else if (results["channels"] != nil){
self.channelData = results["channels"] as! NSArray
// println(channelData)
}
}
}
As Lukas points out, you need to return the UITableViewCell at the end of the method.
In fact, what you posted shouldn't even compile, so I'm wondering if you posted your sample code incorrectly.
The first thing to try, and actually return the cell, update your code to:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell!
{
let cell = UITableViewCell(style:UITableViewCellStyle.Subtitle,reuseIdentifier:"douban")
let rowData:NSDictionary = self.tableData[indexPath.row] as! NSDictionary
cell.textLabel!.text = "hehehehe"//rowData["title"] as! String
cell.detailTextLabel!.text = "adasdasda"//rowData["artist"] as! String
// YOU ARE MISSING THIS LINE
return cell
}
Also ensure that you UITableViewDatasource is set properly, and that the required methods are functioning. Specifically, both numberOfRowsInSection and numberOfSectionsInTableView need to be returning values greater than 0. (In the code you posted, you are missing numberOfSectionsInTableView)
As Lukas said in a comment, you should make sure you return a value from your cellForRowAtIndexPath method otherwise it will refuse to build. If you've done that and you still don't see any cells, it's probably because either numberOfRowsInSection or numberOfSectionsInTableView are returning 0, so you should make sure they return a positive integer.