UITableViewCell with Save button. textField and a label - swift

i want create in swift a tableView with 2 prototype cells, in one cell i've a label and a text field, in other cell i've a save button, the #IBOutlet and #IBAction are in a separated UITableViewCell file, i want write something in Text Field and when click the save button, must change the label.text with textField.text, but i've an issue, when i click the button the value of textField is nil. I'm now in swift, how can resolve this issue?
Thanks

I've resolved the issue with the tag
cell.propertyName.tag = indexPath.row
and this delegate
func textField(textField: UITextField, shouldChangeCharactersInRange range:NSRange, replacementString string: String) -> Bool {
if textField.tag == 0 {
CoreDataController.sharedIstanceCData.matsDataField = "" + textField.text!+string
} else {
CoreDataController.sharedIstanceCData.commentDataField = "" + textField.text!+string
}
return true
}
it resolve my problem, tnx all

The scenario is this in pic
have two files, TableViewController.swift with this inside
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 2
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if indexPath.row == 0 {
let cell = tableView.dequeueReusableCellWithIdentifier("cella", forIndexPath: indexPath) as! TableViewCell
return cell
}
let cell = tableView.dequeueReusableCellWithIdentifier("saveCell", forIndexPath: indexPath) as! TableViewCell
return cell
}
and second file named TableViewCell.swift
import UIKit
class TableViewCell: UITableViewCell {
#IBOutlet weak var textField: UITextField!
#IBOutlet weak var label: 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
}
#IBAction func saveBtn(sender: AnyObject) {
self.label.text = textField.text
}
}

Related

Get and pass text from a TextField which is in a custom TableViewCell subclass

How can I use the text in a textField from a custom Cell?
This is the Receiving Controller:
class ShowName: UIViewController {
#IBOutlet weak var showName: UILabel!
#IBAction func unwindToShowNameData(_ unwindSegue: UIStoryboardSegue) {
let sourceViewController = unwindSegue.source as! enterName
showName.text = sourceViewController.name
}
#IBAction func unwindToShowName(_ unwindSegue: UIStoryboardSegue) {
}
}
This is the Sending Controller:
class enterName: UIViewController, UITableViewDataSource, UITableViewDelegate {
var name: String?
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "nameCell", for: indexPath) as! nameCell
return cell
}
#IBAction func clickSave(_ sender: UIBarButtonItem) {
let cell = nameCell()
name = cell.nameText.text
performSegue(withIdentifier: "passData", sender: self)
}
}
This is the class Cell with TextField:
class NameCell: UITableViewCell {
#IBOutlet weak var nameText: UITextField!
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
}
}
Any ideas?
Actually that's the same solution as my suggestion in your previous question.
Add a callback in the cell and call it when the text field delegate method is called. Don't forget to connect the text field delegate in Interface Builder.
And please name classes and structs always with starting uppercase letter
class NameCell: UITableViewCell, UITextFieldDelegate {
#IBOutlet weak var nameText: UITextField!
var callback : ((UITableViewCell, String) -> Void)?
func textFieldDidEndEditing(_ textField: UITextField) {
callback?(self, nameText.text)
}
}
If you have more than one row you need to declare a data source array to maintain the values of the text fields. This is an example for 4 rows
var values = ["", "", "", ""]
These are the data source methods, in cellForRow the callback updates the model
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return values.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "nameCell", for: indexPath) as! NameCell
cell.nameText.text = values[indexPath.row]
cell.callback = { [unowned self] cCell, cName in
let currentIndexPath = tableView.indexPath(for: cCell)!
self.values[currentIndexPath.row] = cName
}
return cell
}
The benefit is you are independent of the cells in the clickSave method. Get the values from the data source array. And you can pass the name as sender parameter and hand it over in prepare(for segue
#IBAction func clickSave(_ sender: UIBarButtonItem) {
let name = values[0]
performSegue(withIdentifier: "passData", sender: name)
}

TableViewController issue: won't save user input from textView

Update:
so goal of this to save the text that user enters in a UITextView in cell, so that the text is saved for that particular cell number and does not duplicate, move or remove the text.
as suggested I am trying to handle the textViewdidChange func inside of the custom cell, by doing the following:
var onTextEntered: ((String?) -> ())!
func setup() {
notesTextView.delegate = self
}
func textViewDidChange(_ textView: UITextView) {
onTextEntered(notesTextView.text)
}
making a string that holds the text and then adding the text to the String every time textViewDidChange is called (trying to explain this to myself as we go along, so please correct me if my explanation needs it).
next in CellForRowAt
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellIdentifier = "TableViewNotesCell"
let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as! TableViewNotesCell
cell.setup()
cell.onTextEntered = {input in
if let text = input {
self.cellText[indexPath.row] = text // here is the error (index out of range)
}
if indexPath.row < self.cellText.count {
cell.notesTextView.text = self.cellText[indexPath.row] ?? "placeholder"
}
}
return cell
}
when I do the above code, as soon as textViewDidChange is called (when i type a single letter or digit in textView) the I get the error : "Fatal error: Index out of range" on the line which I use the array of cellText[indexPath.row] = text. please help or let me know if my understanding of the process is wrong would love to learn!
You can try to save for every single edit
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// inside cellForRowAt
cell.textView.delegate = self
cell.textView.tag = indexPath.row
return cell
}
#objc func textViewDidChange(_ tex: UITextView) {
cellEndEdit[tex.tag] = tex.text!
}
class VC:UIViewController,UITextViewDelegate {
give default values for the array
var cellEndEdit = [String](repeating: "", count: numberOfRows)
Assuming, that you tableView has a variable number of cells, and all of them have a UITextView, whose content should be recorded and indexed, I would recommend to create a custom UITableViewCell, that handles the textView itself.
class CustomTableViewCell: UITableViewCell, UITextViewDelegate {
#IBOutlet weak var textView: UITextView()
var onTextEntered: ((String?) -> ()) //that's a callback
func setup() {
textView.delegate = self
}
override func textViewDidChange(textView: UITextView) {
onTextEntered(textView.text)
}
}
Since you're working with a sorted list of user inputs, you should have your array at hands, in which you can store and from which you can retrieve data. So if some data already exists, grep through your array and fill the cells that deserve it. Also define the onTextEntered callback here to tell the cell what to do, if it gets called (in your case, store the text of the UITextView in your array).
//your carrier, if you store the already existing user inputs some where, map them in here
//depending on how you calculate the number of cells, this array has to have the same size so use the same logic for this
var yourStringCarrierArray: [String?] = []
override func tableView(_ tableview: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "YourTableViewCell", for: indexPath) as! CustomTableViewCell
cell.setup()
cell.onTextEntered = { input in
if let text = input {
self.yourStringCarrierArray[indexPath.row] = text
}
if indexPath.row < yourStringCarrierArray.count {
cell.textView.text = yourStringCarrierArray[indexPath.row] ?? "placeholder string, because there's no input here so far"
}
}
I hope this will help or at least give you a new perspective, it has been a while, i coded in Swift. Feel free to ask me, if something is unclear.
Use an object to save the string value, since String in swift is value type. Here is an example:
class TestViewController: UIViewController {
#IBOutlet weak var tableView: UITableView!
var dataArray: [CellData] = []
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
tableView.delegate = self
tableView.dataSource = self
for _ in 0..<20 {
dataArray.append(CellData())
}
}
}
extension TestViewController: UITableViewDelegate, UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 20
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "TestTableViewCell", for: indexPath) as! TestTableViewCell
cell.setData(data: dataArray[indexPath.row])
return cell
}
}
class TestTableViewCell: UITableViewCell {
#IBOutlet weak var textView: UITextView!
var data: CellData!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
textView.text = ""
textView.delegate = self
}
func setData(data: CellData) -> Void {
self.data = data
self.textView.text = data.stringValue
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
extension TestTableViewCell: UITextViewDelegate {
func textViewDidChange(_ textView: UITextView) {
self.data.stringValue = textView.text
}
}
class CellData {
var stringValue: String = ""
}

How To get values of textfields from a UITableViewCell?

So I have thisUITableView cell that has 4 UITextField, and I want to get their values on button click.
This code does not retrieve any values.
#IBAction func printBtnAction(_ sender: Any) {
let cell = self.myTableView.dequeueReusableCell(withIdentifier: "manualAddC1") as! AddFriendC1Cell
let alias = cell.aliasTextField.text!
let primaryPhone = cell.primaryPhoneTextField.text!
let secondaryPhone = cell.seondaryPhoneTextField.text!
let email = cell.emailAddressTextField.text!
print("Alias: \(alias), Phone: \(primaryPhone), Phone2: \(secondaryPhone), Email: \(email)")
}
This is the screenshot of the TableView
I finally figured it out with this simple solution.
#IBAction func saveBtnAction(_ sender: Any) {
let index = IndexPath(row: 0, section: 0)
let cell: AddFriendC1Cell = self.myTableView.cellForRow(at: index) as! AddFriendC1Cell
self.alias = cell.aliasTextField.text!
self.primaryPhone = cell.primaryPhoneTextField.text!
self.secondaryPhone = cell.seondaryPhoneTextField.text!
self.email = cell.emailAddressTextField.text!
print("Alias: \(self.alias), Phone: \(self.primaryPhone), Phone2: \(self.secondaryPhone), Email: \(self.email)")
}
OK. You are dequeueing a tableViewCell when you press on a button.
let cell = self.myTableView.dequeueReusableCell(withIdentifier: "manualAddC1") as! AddFriendC1Cell
I believe that you want to get a reference to an existing tableViewCell like this:
if let cell = tableView.cellForRow(at: indexPath) as? AddFriendC1Cell {
// do what you need with cell
}
Even though this would work, I wouldn't suggest this approach. What if user is using a smaller-screen phone like iPhone 4 and some cells are not visible on the screen? I think that in that case tableView.cellForRow would return nil for non-visible cell.
I would suggest implementing textField:shouldChange in each cell with text field with a delegate to the tableView's viewController. When text is changed in the cell, delegate should propagate the change to the viewController which would save the value in an instance variable.
Then, when you press on the button, you would simply take values from instance variables.
// 1st step
In UITableViewCell
import UIKit
#objc protocol TableViewDelegate: NSObjectProtocol{
func afterClickingReturnInTextField(cell: ThirdTableCell)
}
class TableViewCell: UITableViewCell, UITextFieldDelegate {
#IBOutlet weak var enterTextField: UITextField!
weak var tableViewDelegate: TableViewDelegate?
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
enterTextField.delegate = self
}
// #IBAction Of UITextfiled
#IBAction func tapHerre(_ sender: UITextField) {
tableViewDelegate?.responds(to: #selector(TableViewDelegate.afterClickingReturnInTextField(cell:)))
tableViewDelegate?.afterClickingReturnInTextField(cell: self)
}
// UITextField Defaults delegates
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
enterTextField.resignFirstResponder()
return true
}
func textFieldDidEndEditing(_ textField: UITextField) {
enterTextField = textField
}
}
// 2nd step
In UITableViewCell
extension ViewController: UITableViewDelegate, UITableViewDataSource, TableViewDelegate {
var valueToPass : String?
func afterClickingReturnInTextField(cell: ThirdTableCell) {
valueToPass = cell.enterTextField.text
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return youArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableview.dequeueReusableCell(withIdentifier: "TableViewCell") as! TableViewCell
cell.enterTextField.text = youArray[indexPath.row]
cell.tableViewDelegate = (self as TableViewDelegate)
return cell
}
}
When the keyboard disappears you can find TextField value in valueToPass
I suppose your tableViewCells are not dynamic.
Your cell method will looks like this.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "YourCellID") as! YourClass
cell.yourTextField.tag = indexPath.row
//
// Alias will be tag 0
// Primary Phone will be tag 1
// Secondary Phone will be tag 2
// Email Address will be tag 3, so on and so forth
// Then, attach to each delegate
//
cell.yourTextField.delegate = self
}
Inside your UITableView handling class (which conforms to UITextFieldDelegate protocol), write this.
func textField(_ textField: UITextField, shouldChangeCharactersIn range:NSRange, replacementString string: String) -> Bool {
let kActualText = (textField.text ?? "") + string
switch textField.tag
{
case 0:
aliasValueHolder = kActualText;
case 1:
primaryPhoneValueHolder = kActualText;
case 2:
secondaryPhoneValueHolder = kActualText;
case 3:
emailAddressValueHolder = kActualText;
default:
print("It is nothing");
}
return true;
}
Then you can submit it.
func textField(_ textField: UITextField, shouldChangeCharactersIn range:NSRange, replacementString string: String) -> Bool {
// let kActualText = (textField.text ?? "") + string
var kActualText = (textField.text! as NSString).replacingCharacters(in: range, with: string)
kActualText = kActualText.trimmingCharacters(in: .whitespaces)
if textField.tag == 0
{
email_address = kActualText;
}
else if textField.tag == 3
{
password = kActualText;
}
else
{
print("It is nothing")
}
return true;
}
In my opinion, you should implement TextField delegates methods in the tableCell class and manage all the validations and input text in tableCell class.
Now, you will need to fetch all entered values, for this, you have to use Protocol in tableCell class and send entered value using the delegate to viewController also with the tag to identify that which value has entered like the Phone number or email.
Using this way, your code will be reduced and structured.
Happy Coding !!

Selecting row in tableview, removes custom cell

I have a Viewcontroller with a Searchbar at the top with a tableview below. The tableview has a custom cell with 2 labels in it. My problem is that when i run the app and i select a row/cell everything inside the cell disappears. I then force the blank cell outside the visible area of the tableview, so it will be re-used. That's when everything inside the cell is back. Does anyone know why it behaves like this?
My Custom cell class (ContactCell.swift):
import UIKit
class ContactCell: UITableViewCell {
#IBOutlet var lblContactName: UILabel!
#IBOutlet var lblContactTitle: 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
}
}
My ViewDidLoad function:
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
tableView.delegate = self
}
My Delegate and Datasource:
extension contactsTabelViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 6
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("contactCell", forIndexPath: indexPath) as! ContactCell
if let label = cell.lblContactName{
label.text = "This is a name"
}
if let label3 = cell.lblContactTitle{
label3.text = "This is a title"
}
return ContactCell()
}
}
The problem that caused this problem was that i returned ContactCell() instead of the variable cell
Solution was:
Change this:
return ContactCell()
to this:
return cell
in the cellForRowAtIndexPath function.

Swift: How to get the value of a slider in a custom cell in a dynamic UITableView?

I have a problem with the following scenario:
I have an UITableView with dynamic cells. Among others, I use a custom UITableViewCell with a UISlider. How can I get the values of the sliders?
I created the following example: (The project file can be downloaded here: Link)
SliderClass.swift
import UIKit
class SliderClass: NSObject {
var title: String
var subtitle: String
var sliderMinimum: Float
var sliderMaximum: Float
init(title: String, subtitle: String, sliderMinimum: Float, sliderMaximum: Float) {
self.title = title
self.subtitle = subtitle
self.sliderMinimum = sliderMinimum
self.sliderMaximum = sliderMaximum
}
}
SliderTableViewCell.swift
import UIKit
class SliderTableViewCell: UITableViewCell {
#IBOutlet weak var cellTextLabel: UILabel!
#IBOutlet weak var cellDetailTextLabel: UILabel!
#IBOutlet weak var cellSlider: UISlider!
override func awakeFromNib() {
super.awakeFromNib()
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
}
TableViewController.swift
import UIKit
class TableViewController: UITableViewController {
var slider: [SliderClass] = []
override func viewDidLoad() {
super.viewDidLoad()
title = "Slider"
let slider1 = SliderClass(title: "Title1", subtitle: "Subtitle1", sliderMinimum: 0, sliderMaximum: 100)
let slider2 = SliderClass(title: "Title2", subtitle: "Subtitle2", sliderMinimum: 0, sliderMaximum: 200)
slider = [slider1, slider2]
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return slider.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("sliderCell", forIndexPath: indexPath) as! SliderTableViewCell
let slider: SliderClass
slider = self.slider[indexPath.row]
cell.cellTextLabel.text = slider.title
cell.cellDetailTextLabel?.text = slider.subtitle
cell.cellSlider.minimumValue = slider.sliderMinimum
cell.cellSlider.maximumValue = slider.sliderMaximum
return cell
}
func sliderValueChange() {
// I need help here:
// Which slider changed to which value?
// var slider = ...
// var sliderValue = ...
// NSLog("Value of slider:%# changed to:%i", slider, sliderValue)
}
}
Here is a screenshot of the UITableView created with the code above.
Try adding this to your cellForRowAtIndexPath function:
cell.cellSlider.tag = indexPath.row
cell.cellSlider.addTarget(self, action: "sliderValueChange:", forControlEvents: .ValueChanged)
And then outside of the table function add something like:
func sliderValueChange(sender: UISlider) {
// Get the sliders value
var currentValue = Int(sender.value)
var sliderRow = sender.tag
// Do whatever you want with the value :)
// And now the row of the slider!
}
Make sure the action name for cellSlider's target is the same as the function's name! And since it takes one argument, don't forget the colon at the end.
So just to summarise, here's a really simply example of the above in action:
import UIKit
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 2 // 2 rows in the cell, for demo purposes
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! TableViewCell
cell.slider.tag = indexPath.row
cell.slider.addTarget(self, action: "sliderChange:", forControlEvents: .ValueChanged)
return UITableViewCell()
}
func sliderChange(sender: UISlider) {
let currentValue = sender.value // get slider's value
let row = sender.tag // get slider's row in table
print("Slider in row \(row) has a value of \(currentValue)")
// example output - Slider in row 1 has a value of 0.601399
}
}
class TableViewCell: UITableViewCell {
#IBOutlet var slider: UISlider!
}
To get the value of a slider in a custom cell .
Hope this helps to you , Please go through the GitHub Code in Swift and refer screenshot
https://github.com/appcoda/expandable-table-view
Another option is to use ASValueTrackingSlider. Note that is is written in ObjectiveC.