Swift - TableView of steppers, click one stepper in a cell and other steppers get activated? - uitableview

I am new to IOS and basically I have a tableView and whenever it has 40 cells and each cell has a stepper and a label. the label displays the stepper's value. The tableview generates the cells fine, but the problem is that whenever I click the stepper in one cell, some other random cells also have their steppers activated.This is swift by the way. Here is the code for the cell:
import UIKit
class StudentTableViewCell: UITableViewCell {
#IBOutlet weak var studentNameAndValue: UILabel!
#IBOutlet weak var studentValueChanger: UIStepper!
let name:String?
let value:Int?
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 stepperValueChanged(sender: AnyObject) {
studentNameAndValue.text = "\(name): \(Int(studentValueChanger.value))"
}
}
Here is the code for the viewcontroller:
import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
tableView.delegate = self
tableView.dataSource = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 40
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("studentCell") as StudentTableViewCell
return cell
}
}

The problem is in your table view controller. It would be better to put the value-changed method there. Alternatively, review your cellForRowAtIndexPath method. You are not updating the cells correctly when they are recycled.
You have to set the value of stepper and label explicitly in cellForRowAtIndexPath. You cannot read these values from the cell - they should be in your datasource (i.e. the table view controller should know what to display for a given index path).
Connect the stepper handler to the method in the view controller, then identify the proper index path via the sender argument.
#IBAction func stepperChanged(sender: UIStepper) {
let point = sender.convertPoint(CGPointZero, toView: tableView)
let indexPath = self.tableView.indexPathForRowAtPoint(point)!
let myData = dataArray[indexPath.row] // or whatever your datasource
// if you need to update the cell
let cell = self.tableView.cellForRowAtIndexPath(indexPath)
}

Related

UITableView always displays basic cell instead of custom cell in Swift

I spent hours of trying to fix this, but my simple app still displays the basic cell type instead of my prototype cell. I'm aware of using the identifier and registering after loading up the view, but it still displays the basic cells with just one label.
Here is my code so far:
My prototype is using this UITableViewCell:
class CoinTableViewCell: UITableViewCell {
#IBOutlet weak var coinIcon: UIImageView!
#IBOutlet weak var coinTitleLabel: UILabel!
#IBOutlet weak var holdings: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
}
UITableViewController:
class CoinTableViewController: UITableViewController {
var coins = ["Coin1","Coin2","Coin3"]
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.register(CoinTableViewCell.self, forCellReuseIdentifier: "currency_cell")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return coins.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell:CoinTableViewCell! = self.tableView.dequeueReusableCell(withIdentifier: "currency_cell", for: indexPath) as! CoinTableViewCell
let coinName = coins[indexPath.row]
cell.coinTitleLabel?.text = coinName
return cell!
}
}
I would be so grateful if someone is able the help me out with this!
You are creating your custom cell directly on the tableview in the storyboard, right ?
If this is the case then you don't need to register the cell in your viewDidLoad as the storyboard takes care of that. You just deque it and it's good to go.
If you register it manually you just override what the storyboard did and end up getting a regular cell as the cell gets instantiated from the code instead of getting instantiated from the storyboard.
Cheers

Swift 3 UISwitch in TableViewCell loses State when scrolling

That's weird: I just set up a new Single-View iOS Project and put a TableView into the Main.storyboard. In this TableView I put a TableViewCell and into this Cell, I put an UILabel and an UISwitch.
For this TableViewCell I created a CocoaTouchClass MyTableViewCell and set this for the TableViewCell's Class in Interfacebuilder.
I connected Outlets for the UILabel and UISwitch to MyTableViewCell, as well as an action for the switch.
Also I connected the TableView's dataSource and delegate to the ViewController.
So, as I think, basic stuff for setting up a table.
My ViewController looks like this:
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var tableData = [[String: Bool]]()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
for index in 1...40 {
self.tableData.append([String(index): index%2 == 0])
}
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.tableData.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "mycell", for: indexPath) as! MyTableViewCell
let object = tableData[indexPath.row].first!
cell.myLabel.text = object.key
cell.mySwitch.setOn(object.value, animated: false)
return cell
}
}
So, I populate the Table with some Rows of Data and switch every second UISwitch to on.
The MyTableViewCell-Class is nothing special as well:
class MyTableViewCell: UITableViewCell {
#IBOutlet weak var myLabel: UILabel!
#IBOutlet weak var mySwitch: UISwitch!
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 switched(_ sender: UISwitch) {
print("Switched: \(sender.isOn)")
}
}
Ok, fire up the iOS-Simulator and I see the Table as expected. 40 Table-Lines don't fit on one screen, so the TableView makes itself scrollable.
Now: when I change the state of one UISwitch, and drag the TableView so the changed UISwitch gets out of view and then drag the TableView so the changed UISwitch gets visible again, it is changed back to its initial state.
The Switch-event gets fired like it should.
So, what am I doing wrong? Am I missing something?
I recorded a 4-seconds-Screencast to demonstrate, what's going on:
http://b-bereich.de/download/swiftSwitch.mov
TableViewCells are reused.
That means you need to keep track of the data you are using to fill the content of the cells. If the cell content changes - such as when you tap a Switch in the cell - you need to update your datasource. When you scroll, and that row is displayed again, your data will know how to set the state of the Switch.
Here is a simple example:
//
// TableWithSwitchTableViewController.swift
// SWTemp2
//
// Created by Don Mag on 6/5/17.
// Copyright © 2017 DonMag. All rights reserved.
//
import UIKit
class MyTableViewCell: UITableViewCell {
#IBOutlet weak var myLabel: UILabel!
#IBOutlet weak var mySwitch: UISwitch!
var switchTapAction : ((Bool)->Void)?
#IBAction func switched(_ sender: UISwitch) {
print("Switched: \(sender.isOn)")
// send the Switch state in a "call back" to the view controller
switchTapAction?(sender.isOn)
}
}
// simple data object
class MyObject: NSObject {
var theTitle = ""
var theSwitchState = false
init(_ title: String) {
theTitle = title
}
}
class TableWithSwitchTableViewController: UITableViewController {
// array of MyObjects
var myData = [MyObject]()
override func viewDidLoad() {
super.viewDidLoad()
// just to make it a little easier to see the rows scroll
tableView.rowHeight = 60
// create 40 data objects for the table
for i in 1...40 {
let d = MyObject("Data Item: \(i)")
myData.append(d)
}
tableView.reloadData()
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return myData.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "SwitchCell", for: indexPath) as! MyTableViewCell
// Configure the cell...
let d = myData[indexPath.row]
cell.myLabel.text = d.theTitle
cell.mySwitch.isOn = d.theSwitchState
// set a "Callback Closure" in the cell
cell.switchTapAction = {
(isOn) in
// update our Data Array to the new state of the switch in the cell
self.myData[indexPath.row].theSwitchState = isOn
}
return cell
}
}
I think you need to have your IBAction change the Bool each time you flip the switch. Currently the switch changes the UI but not the underlying Bool so the cellForRowAt method uses the currently saved value when the cell scrolls back on screen.
Add function prepareForReuse() in your custom cell class and set switch state to off by default. -:
class MyTableViewCell: UITableViewCell {
#IBOutlet weak var myLabel: UILabel!
#IBOutlet weak var mySwitch: UISwitch!
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 switched(_ sender: UISwitch) {
print("Switched: \(sender.isOn)")
}
override func prepareForReuse(){
// SET SWITCH STATE OFF HERE
}

Using UITableView in detail, how to display string in TextView (Master-Detail project, Swift, iOS)

I want to use a TableView for the detail side of a Master Detail app. I have started with the standard Master Detail project in Xcode, deleted the standard app that comes with it, deleted the standard UIView detail controller, added a TableView controller, added a TextView to the prototype cell for testing, and created a new segue to the new TableView. I subclassed UITableViewCell and created an outlet (detailTextView) from the TextView to the subclass (TableViewCell). Changed the class in DetailViewController.swift from UIViewController to UITableViewController. I am successfully passing a string stringForTextView = "String for TextView" from master to the detail. But I can't figure out how to display that string in the TextView. I tried to reference the TextView text in the detail view through the outlet (detailTextView.text) but got "Use of unresolved identifier detailTextView"
Any help will be greatly appreciated.
Relevant code is shown below.
You can also download the whole project here if that would be helpful:
http://greendept.com/MasterDetailTwoTableViews/
TableViewCell.swift (subclass for prototype cell in detail)
import UIKit
class TableViewCell: UITableViewCell {
#IBOutlet weak var detailTextView: UITextView!
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
}
}
DetailViewController.swift
import UIKit
class DetailViewController: UITableViewController {
var stringForTextView : String?
var detailItem: AnyObject? {
didSet {
// Update the view.
self.configureView()
}
}
func configureView() {
// THE NEXT TWO LINES WORK: PASSED IN STRING PRINTS TO CONSOLE
let printThis = stringForTextView! as String
print("\(printThis)")
// BUT THE REFERENCE TO THE OUTLET BELOW DOES NOT WORK, GIVES
// "Use of unresolved identifier detailTextView"
detailTextView.text = printThis
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.configureView()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
MasterViewController.swift
import UIKit
class MasterViewController: UITableViewController {
var detailViewController: DetailViewController? = nil
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
if let split = self.splitViewController {
let controllers = split.viewControllers
self.detailViewController = (controllers[controllers.count-1] as! UINavigationController).topViewController as? DetailViewController
}
}
override func viewWillAppear(animated: Bool) {
self.clearsSelectionOnViewWillAppear = self.splitViewController!.collapsed
super.viewWillAppear(animated)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Segues
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showDetail" {
if let indexPath = self.tableView.indexPathForSelectedRow {
let controller = (segue.destinationViewController as! UINavigationController).topViewController as! DetailViewController
controller.stringForTextView = "String for TextView"
controller.navigationItem.leftBarButtonItem = self.splitViewController?.displayModeButtonItem()
controller.navigationItem.leftItemsSupplementBackButton = true
}
}
}
// MARK: - Table View
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)
return cell
}
override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
// Return false if you do not want the specified item to be editable.
return true
}
}
DetailViewController is a UITableViewController, and you can't access the detailTextView in the tableView controller. You defined the outlet in the cell, and that is where you can access and configure the detailTextView.
It doesn't make any sense to have the DetailViewController as a UITableViewController, if what you really want is to configure the text view there. Then you should set it back to a UIViewController, and add the text view as a single UITextView to the view controllers view.
This link below shows how you can change text in a cell label even though the outlet to the textview is in the cell subclass. It shows this with a single TableView.
creating custom tableview cells in swift
In adapting the above approach for my test project, I didn't have to change the Master at all. In the Detail view, the configureView() doesn't do the main job of updating the TextView. That happens in cellForRowAtIndexPath -- second to the last function in detail view. Another difference is I could not, and did not need to, implement #IBOutlet var tableView: UITableView! -- because tableView was already available as a stored property. I also had to add overrride in a couple of places. Finally, in the TableViewCell class, I added an outlet linked to the content view of the TextView. The result is that the TextView text is getting updated.
TableViewCell.swift:
import UIKit
class TableViewCell: UITableViewCell {
#IBOutlet weak var detailTextView: UITextView!
#IBOutlet weak var detailContentView: UIView!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
print ("awakeFromNib")
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
print("test")
}
}
DetailViewController.swift:
import UIKit
class DetailViewController: UITableViewController {
// #IBOutlet var tableView: UITableView! -- cannot override a stored property
var stringForTextView : String?
// Don't forget to enter this in IB also
let cellReuseIdentifier = "reuseIdentifier"
var detailItem: AnyObject? {
didSet {
// Update the view.
self.configureView()
}
}
func configureView() {
// Update the user interface for the detail item.
// stringForTextView
let printThis = stringForTextView! as String
print("\(printThis)")
// detailTextView.text = printThis
}
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
self.configureView()
}
// needed "override" here
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
// create a cell for each table view row
// needed "override" here
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell:TableViewCell = self.tableView.dequeueReusableCellWithIdentifier(cellReuseIdentifier) as! TableViewCell
cell.detailTextView.text = stringForTextView
print("cell.detailTextView.text: \(cell.detailTextView.text)")
print("row : \(indexPath.row)")
return cell
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}

how to handle button click for each button in each row of UITableView

I have a UITableView (with a Custom class called CellModelAllNames for each row). Each Row has a Label and a button.
My question is: When btn_addRecording (i.e. the '+' button is clicked on any/each of the rows, how do I get the lbl_name.text, the label name shown, and show a pop up in the ViewController itself. I want to get additional information in the pop up and then save all the info (including the lbl_name to a database).
CellModelAllNames for each row layout:
import UIKit
class CellModelAllNames: UITableViewCell {
#IBOutlet weak var lbl_name: UILabel!
#IBOutlet weak var btn_addRecording: UIButton!
#IBAction func btnAction_addRecording(sender: AnyObject) {
println("clicked on button in UITableViewCell")
}
override func awakeFromNib() {
super.awakeFromNib()
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
func setCell(setBabyName: String) {
self.lbl_name.text = setBabyName
}
}
Here's the code of my ViewController:
import UIKit
class SecondViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tbl_allNames: UITableView!
var arrayOfNames: [Name] = [Name]()
override func viewDidLoad() {
super.viewDidLoad()
self.tbl_allNames.delegate = self
self.tbl_allNames.dataSource = self
self.tbl_allNames.scrollEnabled = true
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell:CellModelAllNames = self.tbl_allNames.dequeueReusableCellWithIdentifier("CellModelAllNames") as! CellModelAllNames
let name = arrayOfNames[indexPath.row]
cell.setCell(name.name)
println("in tableView, cellforRowatIndex, returning new cells")
return cell
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return arrayOfNames.count
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
}
}
You can use standard UIKit methods to get the cell and its data:
func tappedButton(sender : UIButton) {
let point = sender.convertPoint(CGPointZero, toView: self.tableView)
let indexPath = self.tableView.indexPathForRowAtPoint(point)!
let name = arrayOfNames[indexPath.row]
// do something with name
}
You can add button action in your ViewController
1) In your function cellForRowAtIndexPath assign button's tag as index (ie. indexPath.row)
cell.btn_addRecording.tag = indexPath.row
2) Add target and action for your button :
cell.btn_addRecording.addTarget(self, action: "buttonPressed:", forControlEvents: .TouchUpInside)
3) Add action in ViewControler (ie. save info in database)
func buttonPressed(button: UIButton!)
{
// Add your code here
let name = arrayOfNames[button.tag]
}

Why my simple tableview app won't work?

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?

Resources