I'm trying to populate a TableViewController with data and display it using a CustomCell so I can have two labels and a button on each row.
I have placed the two labels and the button in the PrototypeCell within the Main Storyboard.
I have created a CustomCellTableViewCell class:
import UIKit
class CustomCellTableViewCell: UITableViewCell {
#IBOutlet var customCellLabel1: [UILabel]!
#IBOutlet var customCellLabel2: [UILabel]!
#IBOutlet var CustomCellButton: [UIButton]!
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 within my UITableViewController I have:
var DevicesList: Array<AnyObject>=[]
var ii: Int = 1
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let CellID : NSString = "DeviceCell"
var cell : CustomCellTableViewCell = tableView.dequeueReusableCellWithIdentifier(CellID) as CustomCellTableViewCell
if DevicesList.count > 0 {
var data: NSManagedObject = DevicesList[indexPath.row] as NSManagedObject
cell.customCellLabel1[indexPath.row].text = data.valueForKeyPath("name") as? String
println("loading row \(ii) loaded!")
ii++
} else {
println("nothing loaded...?")
}
println("cell loaded")
return cell
}
when I run it though the first row is loaded but then I get a:
loading row 1 loaded!
cell loaded
fatal error: Array index out of range
The guilty line is shown as this one:
cell.customCellLabel1[indexPath.row].text = data.valueForKeyPath("name") as? String
I have 7 rows to load so somewhere I need to append the labels/button array and if so where/how do I do it?
Thanks!
Kostas
Just for future searches here is how it was solved:
Created new class:
import UIKit
class DeviceCustomCell: UITableViewCell {
#IBOutlet var myLabel1: UILabel!
#IBOutlet var myLabel2: UILabel!
#IBOutlet var myButton: UIButton!
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
}
#IBAction func PressButton(sender: AnyObject) {
myButton.setTitle("OFF", forState: UIControlState.Normal)
}
}
Linked the labels and button from the Main storyboard to the two variables (New Reference Outlet).
Important to have the Identifier as "DeviceCustomCell" for the Prototype Cell and then modify the function:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let CellID : NSString = "DeviceCustomCell"
var cell = tableView.dequeueReusableCellWithIdentifier("DeviceCustomCell") as DeviceCustomCell
if DevicesList.count > 0 {
var data: NSManagedObject = DevicesList[indexPath.row] as NSManagedObject
var devicename : String = data.valueForKeyPath("name") as String
var lastchanged: String = data.valueForKeyPath("lastChangedRFC822") as String
cell.myLabel1.text = devicename
cell.myLabel2.text = lastchanged
println("loading row \(ii) loading...")
ii++
return cell
} else {
println("nothing loaded...?")
}
return cell
}
This now works nicely!
Important is to understand this line:
var cell = tableView.dequeueReusableCellWithIdentifier("DeviceCustomCell") as DeviceCustomCell
as it holds the key to success! :)
Kostas
Related
I'm going to pass data from a UITableViewController to another UITableViewController. These two UITableViewController have custom cell in it.
When I pass the data by segue, I can't get the data from sourceViewController as it says that there's no member of the label I built in the Custom UITableViewCell controller.
Main Problem that find no member in menuController. Do I need to inherit or delegate the custom cell to menuController or push it?
Here is the code of ordersController:
import UIKit
struct Order {
var tilte:String
var price:String
var quantity:String
}
class ordersController: UITableViewController {
var orders = Array<Order>()
override func viewDidLoad() {
super.viewDidLoad()
}
//handle exit from Cancel back of menuController
#IBAction func cancelBack(segue: UIStoryboardSegue){
}
//handle exit from Done back of menuController
#IBAction func doneBack(segue: UIStoryboardSegue){
print("Done and Back")
//let source = segue.sourceViewController as! newMenu
//let quantity = source.burgerQuan.text
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.orders.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("orderedItem", forIndexPath: indexPath) as! ordersCustomCell
let order:Order = self.orders[indexPath.row]
cell.ordersName.text = orders[indexPath.row].title
cell.ordersPrice.text = orders[indexPath.row].price
cell.ordersQuantity.text = orders[indexPath.row].quantity
return cell
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
Here is code of Custom Cell of ordersController:
class ordersCustomCell: UITableViewCell {
#IBOutlet weak var ordersName: UILabel!
#IBOutlet weak var ordersPrice: UILabel!
#IBOutlet weak var ordersQuantity: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
Here is the code of menuController:
class menuController: UITableViewController {
var items:[String] = ["Hamburger","French Fries", "Coffee", "Lemon Tea"]
var price:[String] = ["$29", "$13", "$25", "$8"]
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.items.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("menuItem", forIndexPath: indexPath) as! menuCustomCell
cell.menuName.text = self.items[indexPath.row]
cell.menuPrice.text = self.price[indexPath.row]
return cell
}
}
And here is code of custom cell of menuController:
class menuCustomCell: UITableViewCell {
#IBOutlet weak var menuName: UILabel!
#IBOutlet weak var menuPrice: UILabel!
#IBOutlet weak var menuQuantity: UILabel!
#IBOutlet weak var stepper: UIStepper!
#IBAction func stepperValueChanged(sender: UIStepper) {
menuQuantity.text = Int(sender.value).description
}
override func awakeFromNib() {
super.awakeFromNib()
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
}
How can I do this? --> When the Add button in Orders is clicked, it shows the Menu. User click the stepper to select the quantity. Then, data pass back to Orders when user click Done. The items which quantity is not 0 will be shown on the list on Orders.
Your menuController class does not that that variable menuName, thats why it cannot find it.
All you have is items and price in that view controller.
You have menuName within your custom cell. That is different from the viewController you are pushing too.
well i create tableview and inside of it tableview cell with xib file
now i want create tableview inside xib file , the main problem is that u can't create cell inside of it to define the identifier for this cell
i keep get this error
'unable to dequeue a cell with identifier AutoCompleteRowIdentifier -
must register a nib or a class for the identifier or connect a
prototype cell in a storyboard'
this is my code
import UIKit
class TableViewCell2: UITableViewCell, UITableViewDelegate, UITableViewDataSource, UITextFieldDelegate {
#IBOutlet weak var textField: UITextField!
#IBOutlet weak var autocompleteTableView: UITableView!
var pastUrls = ["Men", "Women", "Cats", "Dogs", "Children"]
var autocompleteUrls = [String]()
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool
{
autocompleteTableView.hidden = false
let substring = (textField.text! as NSString).stringByReplacingCharactersInRange(range, withString: string)
searchAutocompleteEntriesWithSubstring(substring)
return true // not sure about this - could be false
}
func searchAutocompleteEntriesWithSubstring(substring: String)
{
autocompleteUrls.removeAll(keepCapacity: false)
for curString in pastUrls
{
var myString:NSString! = curString as NSString
var substringRange :NSRange! = myString.rangeOfString(substring)
if (substringRange.location == 0)
{
autocompleteUrls.append(curString)
}
}
autocompleteTableView.reloadData()
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return autocompleteUrls.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let autoCompleteRowIdentifier = "AutoCompleteRowIdentifier"
let cell : UITableViewCell = tableView.dequeueReusableCellWithIdentifier(autoCompleteRowIdentifier, forIndexPath: indexPath) as UITableViewCell
let index = indexPath.row as Int
cell.textLabel!.text = autocompleteUrls[index]
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let selectedCell : UITableViewCell = tableView.cellForRowAtIndexPath(indexPath)!
textField.text = selectedCell.textLabel!.text
}
override func awakeFromNib() {
super.awakeFromNib()
textField.delegate = self
autocompleteTableView.delegate = self
autocompleteTableView.dataSource = self
autocompleteTableView.scrollEnabled = true
autocompleteTableView.hidden = true
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}}
as you know you can't define identifier inside xib file
thanks
You need to register a cell with autocompleteTableView before you can dequeue it. Modify your code like this:
override func awakeFromNib() {
super.awakeFromNib()
textField.delegate = self
autocompleteTableView.delegate = self
autocompleteTableView.dataSource = self
autocompleteTableView.scrollEnabled = true
autocompleteTableView.hidden = true
// Register cell
autocompleteTableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: "AutoCompleteRowIdentifier")
}
How to use prepare for segue to transfer the images (tweetImg) and date (dateLbl) from tablecell (mainCell) to the Newviewcontroller when I select the cell? in my case I am using parse.com as my backend to retrieve the images and dates data. I am new to programming would appreciate some help, thank you.
mainVC
import UIKit
class mainVC: UIViewController, UITableViewDataSource, UITableViewDelegate, {
#IBOutlet weak var resultsTable: UITableView!
var resultsStartdateArray = [String]()
var resultsTweetImageFiles = [PFFile?]()
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return resultsTweetImageFiles.count
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return 350
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell:mainCell = tableView.dequeueReusableCellWithIdentifier("Cell") as! mainCell
cell.dateLbl.text = self.resultsStartdateArray[indexPath.row]
resultsTweetImageFiles[indexPath.row]?.getDataInBackgroundWithBlock({
(imageData:NSData?, error:NSError?) -> Void in
if error == nil {
let image = UIImage(data: imageData!)
cell.tweetImg.image = image
}
})
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
self.performSegueWithIdentifier("SendDataSegue", sender: self)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?)
{
if (segue.identifier == "SendDataSegue")
{
let destination: NewViewController = segue.destinationViewController as! NewViewController
// I believe the missing code should be here
}
}
}
mainCell
import UIKit
class mainCell: UITableViewCell {
#IBOutlet weak var tweetImg: UIImageView!
#IBOutlet weak var dateLbl: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
}
}
NewViewController
import UIKit
class NewViewController: UIViewController {
#IBOutlet weak var dateLbl: UILabel!
#IBOutlet weak var tweetImg: UIImageView!
var titleString: String!
override func viewDidLoad()
{
super.viewDidLoad()
self.dateLbl.text = self.titleString
}
Bellow i have mentioned a sample template code. organize your code according to bellow mentioned code. important part is select a cell in the prepareForSegue instead of in didSelectRowAtIndexPath. Which is let IndexPath = self.tableView.indexPathForSelectedRow()!
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
//Index path is the selected cell
let IndexPath = self.tableView.indexPathForSelectedRow()!
//Here you can assign your tweetImg and dateLbl
let newViewController = segue.destinationViewController as! NewViewController
newViewController.dateLbl.text = //your date label value
//here assign your image to destination view controller property
}
Delete the method didSelectRowAtIndexPath and connect the segue to the table view cell rather than the view controller.
When a cell is selected it's passed as the sender parameter in the prepareForSegue method.
A side note : Don't perform background tasks in the view – the cellForRowAtIndexPath method – , do it in the model – the items in the data source array – and then call reloadData()
Stepper problem when cells need scroll.
My_TableViewCell.swift
import UIKit
class My_TableViewCell: UITableViewCell {
#IBOutlet weak var My_Label1: UILabel!
#IBOutlet weak var My_Label2: UILabel!
#IBOutlet weak var My_Stepper: UIStepper!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
...
My_TableViewController.swift
import UIKit
var My_Ids = ["C01","C02","C03","C04","C05","C06","C07","C08","C09","C10"]
var My_Values = ["0","0","0","0","0","0","0","0","0","0"]
class My_TableViewController: UITableViewController {
#IBAction func My_Stepper(sender: AnyObject) {
let point = sender.convertPoint(CGPointZero, toView: tableView)
let indexPath = self.tableView.indexPathForRowAtPoint(point)!
let cell = self.tableView.cellForRowAtIndexPath(indexPath) as! My_TableViewCell
cell.My_Label2.text = "\(Int(cell.My_Stepper.value))"
}
...
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return My_Ids.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell =
self.tableView.dequeueReusableCellWithIdentifier(
"My_TableCell", forIndexPath: indexPath)
as! My_TableViewCell
let row = indexPath.row
cell.My_Label1.text = My_Ids[row]
cell.My_Label2.text = "\(Int(cell.My_Stepper.value))"
return cell
}
...
When all cells fit in a "page" the Stepper works well but if the cells are resized and you need scroll the tableview the values are repeated in other cells.
If you alter the value on the cells C06 and C07 this will to reflect C01 and C02 cells and vice-versa.
Please view this image showing the snapshots with the errors
UITableViewController intentionally reuses cells as an optimization. You need to clear the old cell settings as part of your solution. Add a prepareForReuse method to your My_TableViewCell class.
You do this:
- (void) prepareForReuse
{
[super prepareForReuse];
// clear previous cell settings
}
Additional Notes:
recommend against calling cellForRowAtIndexPath directly. See this thread.
recommend you use Pascal casing for class names: Use MyTableViewCell instead of My_TableViewCell.
I keep getting this error for some reason.
"type" viewController does not conform to protocol "UITableViewDataSource"
I know that this has been covered a million times and there are even videos explaining why this problem occurs, but for some reason I can't fix it and can't figure out exactly what it is that I have done wrong.
Here is my code:
//
// ViewController.swift
// ChatApp
//
// Created by K on 17/03/2015.
// Copyright (c) 2015 Krazy88 All rights reserved.
//
import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UITextFieldDelegate {
#IBOutlet weak var dockViewHeightConstraint: NSLayoutConstraint!
#IBOutlet weak var sendButton: UIButton!
#IBOutlet weak var messageTextField: UITextField!
#IBOutlet weak var messageTableView: UITableView!
var messagesArray:[String] = [String]()
var delegate: UITableViewDataSource? = nil
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.messageTableView.delegate = self
self.messageTableView.dataSource = self
// Set self as the delegate for the textfield
self.messageTextField.delegate = self
//Add a tap gesture recognizer to the tableview
let tapGesture:UITapGestureRecognizer = UITapGestureRecognizer(target:self, action: "tableViewTapped")
self.messageTableView.addGestureRecognizer(tapGesture)
// Retrieve messages from Parse
self.retreiveMessages()
}
override func didReceiveMemoryWarning(){
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func sendButtonTapped(sender: UIButton) {
// Send button is tapped
// Call the end editing method for the text field
self.messageTextField.endEditing(true)
//Disable the send button and textfield
self.messageTextField.enabled = false
self.sendButton.enabled = false
// Create a PFObject
var newMessageObject:PFObject = PFObject(className: "Message")
// Set the Text key to the text of the messageTextField
newMessageObject["Text"] = self.messageTextField.text
// Save the PFObject
newMessageObject.saveInBackgroundWithBlock { (success:Bool, error:NSError!) -> Void in
if (success == true) {
//Message has been saved!
// TODO: Retrieve the latest messages and reload the table
NSLog("Message saved successfully.")
}
else {
// Something bad happened.
NSLog(error.description)
}
// Enable the textfield and send button
self.sendButton.enabled = true
self.messageTextField.enabled = true
self.messageTextField.text = ""
}
}
func retreiveMessages() {
// Create a new PFQuery
var query:PFQuery = PFQuery(className: "Message")
// Call findobjectsinbackground
query.findObjectsInBackgroundWithBlock { (objects:[AnyObject]!, error:NSError!) -> Void in
// clear the messageArray
self.messagesArray = [String]()
// Loop through the objects array
for messageObject in objects {
// Retrieve the Text column of each PFObject
let messageText:String? = (messageObject as PFObject)["Text"] as? String
// Assign it into our messagesArray
if messageText != nil {
self.messagesArray.append(messageText!)
}
}
// Reload the tableview
self.messageTableView.reloadData()
// Reload the tableview
}
func tableViewTapped() {
// Force the textfield to end editing
self.messageTextField.endEditing(true)
}
// MARK: Textfield Delegate Methods
func textFieldDidBeginEditing(textField: UITextField) {
// Perform an animation to grow the dockview
self.view.layoutIfNeeded()
UIView.animateWithDuration(0.5, animations: {
self.dockViewHeightConstraint.constant = 350
self.view.layoutIfNeeded()
} , completion: nil)
}
func textFieldDidEndEditing(textField:UITextField) {
// Perform an animation to grow the dockview
self.view.layoutIfNeeded()
UIView.animateWithDuration(0.5, animations: {
self.dockViewHeightConstraint.constant = 60
self.view.layoutIfNeeded()
}, completion: nil)
}
// MARK: TableView Delegate Methods
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
// Create a table cell
let cell = self.messageTableView.dequeueReusableCellWithIdentifier("MessageCell") as UITableViewCell
// Customize the cell
cell.textLabel.text = self.messagesArray[indexPath.row]
//Return the cell
return cell
}
func TableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return messagesArray.count
}
}
}
Any helps tips or suggestion will be much appreciated as I have tried everything!
thanks for reading
Kurando
Your UITableViewDataSource #required delegates are inside retreiveMessages(). Fix the position of that second last bracket..!!
Here is your correct code :
import Foundation
import UIKit
class ViewController : UIViewController, UITableViewDataSource, UITableViewDelegate, UITextFieldDelegate {
#IBOutlet weak var dockViewHeightConstraint: NSLayoutConstraint!
#IBOutlet weak var sendButton: UIButton!
#IBOutlet weak var messageTextField: UITextField!
#IBOutlet weak var messageTableView: UITableView!
var messagesArray:[String] = [String]()
var delegate: UITableViewDataSource? = nil
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
messageTableView.delegate = self
messageTableView.dataSource = self
// Set self as the delegate for the textfield
self.messageTextField.delegate = self
//Add a tap gesture recognizer to the tableview
let tapGesture:UITapGestureRecognizer = UITapGestureRecognizer(target:self, action: "tableViewTapped")
self.messageTableView.addGestureRecognizer(tapGesture)
// Retrieve messages from Parse
self.retreiveMessages()
}
override func didReceiveMemoryWarning(){
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func sendButtonTapped(sender: UIButton) {
// Send button is tapped
// Call the end editing method for the text field
self.messageTextField.endEditing(true)
//Disable the send button and textfield
self.messageTextField.enabled = false
self.sendButton.enabled = false
// Create a PFObject
var newMessageObject:PFObject = PFObject(className: "Message")
// Set the Text key to the text of the messageTextField
newMessageObject["Text"] = self.messageTextField.text
// Save the PFObject
newMessageObject.saveInBackgroundWithBlock { (success:Bool, error:NSError!) -> Void in
if (success == true) {
//Message has been saved!
// TODO: Retrieve the latest messages and reload the table
NSLog("Message saved successfully.")
}
else {
// Something bad happened.
NSLog(error.description)
}
// Enable the textfield and send button
self.sendButton.enabled = true
self.messageTextField.enabled = true
self.messageTextField.text = ""
}
}
func retreiveMessages() {
// Create a new PFQuery
var query:PFQuery = PFQuery(className: "Message")
// Call findobjectsinbackground
query.findObjectsInBackgroundWithBlock { (objects:[AnyObject]!, error:NSError!) -> Void in
// clear the messageArray
self.messagesArray = [String]()
// Loop through the objects array
for messageObject in objects {
// Retrieve the Text column of each PFObject
let messageText:String? = (messageObject as PFObject)["Text"] as? String
// Assign it into our messagesArray
if messageText != nil {
self.messagesArray.append(messageText!)
}
}
// Reload the tableview
self.messageTableView.reloadData()
// Reload the tableview
}
}
func tableViewTapped() {
// Force the textfield to end editing
self.messageTextField.endEditing(true)
}
// MARK: Textfield Delegate Methods
func textFieldDidBeginEditing(textField: UITextField) {
// Perform an animation to grow the dockview
self.view.layoutIfNeeded()
UIView.animateWithDuration(0.5, animations: {
self.dockViewHeightConstraint.constant = 350
self.view.layoutIfNeeded()
} , completion: nil)
}
func textFieldDidEndEditing(textField:UITextField) {
// Perform an animation to grow the dockview
self.view.layoutIfNeeded()
UIView.animateWithDuration(0.5, animations: {
self.dockViewHeightConstraint.constant = 60
self.view.layoutIfNeeded()
}, completion: nil)
}
// MARK: TableView Delegate Methods
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return messagesArray.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
// Create a table cell
let cell = self.messageTableView.dequeueReusableCellWithIdentifier("MessageCell") as UITableViewCell
// Customize the cell
cell.textLabel?.text = self.messagesArray[indexPath.row]
//Return the cell
return cell
}
}
Copy from here.
And always make sure to call numberOfRowsInSection before cellForRowAtIndexPath.