Change button title in section of tableview on specific row - ios

I have view controller in which there are multiple section of tableview. In section 0 I have multiple row . Each row having button named as Add Comments when I click on button it pushes me to other view controller having text field when i wrote something and press done button then through delegate I passes textfield data and set it in button title. But problem is my button present in all row changes value. I want only selected row in section changes its button title. below is my code of first viewcontroller
class MyTabViewController: UIViewController {
var addCommentsValueStore: String = "Add Comments"
#IBOutlet weak var tabTableView : ContentWrappingTableView!
#IBAction func addCommentsAction(_ sender: UIButton) {
guard let nextVC = MyCommentsRouter.getMyCommentsViewScreen() else { return }
nextVC.passAddCommentsDelegate = self
self.navigationController?.pushViewController(nextVC, animated: true)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if(indexPath.section == 0)
{
let indetifier = "MyTabTableViewCell"
let cell = tableView.dequeueReusableCell(withIdentifier: indetifier, for: indexPath) as! MyTabTableViewCell
cell.addCommentsButton.setTitle(addCommentsValueStore, for: UIControl.State.normal)
}
return cell
}
extension MyTabViewController: AddCommentsDelegate{
func passAddComments(instruction: String) {
addCommentsValueStore = instruction
print(addCommentsValueStore)
}
}
}
below is code of second view controller:
import UIKit
protocol AddCommentsDelegate{
func passAddComments(instruction: String)
}
class MyCommentsViewController: UIViewController {
#IBOutlet var addCommentsTextField: UITextField!
var passAddCommentsDelegate: AddCommentsDelegate?
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func backActionClick(_ sender: UIButton) {
self.navigationController?.popViewController(animated: true)
}
#IBAction func DoneActionClick(_ sender: Any) {
let dataToBeSent = addCommentsTextField.text
self.passAddCommentsDelegate?.passAddComments(instruction: dataToBeSent!)
self.navigationController?.popViewController(animated: true)
}
}

by using tag on the cell button, you can fix your problem.
on your cellForRowAt delegate method, put this line:
cell.addCommentsButton.tag = indexPath.item
now you know exactly which button did select, then you can use this tag to specify which row in your tableView should change its title.
your implementation has some problems:
first of all, the addCommentsValueStore have to be an array of strings.
var addCommentsValueStore: [String] = []
then tell the delegate to show the right title:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell: MyTabTableViewCell!
if (indexPath.section == 0) {
cell = tableView.dequeueReusableCell(withIdentifier: "MyTabTableViewCell", for: indexPath) as! MyTabTableViewCell
cell.tag = indexPath.item
cell.addCommentsButton.setTitle(addCommentsValueStore[indexPath.item], for: UIControl.State.normal)
}
return cell
}
the AddCommentsDelegate should return the index too:
protocol AddCommentsDelegate {
func passAddComments(instruction: String, atIndex: Int)
}
then every time you want to pass comment to another viewController, you should pass the index too.
by using this index, you will specify where you should change the row button title.
#UIBotton func DoneActionClick(_ sender: UIButton) {
let dataToBeSent = addCommentsTextField[sender.tag].text
self.passAddCommentsDelegate?.passAddComments(instruction: dataToBeSent!, atIndexPath: sender.tag)
self.navigationController?.popViewController(animated: true)
}

Related

In swift, how to manage two buttons in same custom tableview cell?

I am trying to manage two buttons in same custom tableview cell.
Added two buttons named Yes and No. If yes button is selected the No button will be inactive and Yes button became active.
Here is the image what I need
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "tableCell") as! TableViewCell
cell.yesButton.tag = 101
cell.noButton.tag = 102
cell.yesButton.addTarget(self, action: #selector(buttonClicked(sender:)), for: UIControl.Event.touchUpInside)
cell.noButton.addTarget(self, action: #selector(buttonClicked(sender:)), for: UIControl.Event.touchUpInside)
return cell
}
#objc func buttonClicked(sender: AnyObject) {
let buttonPosition = (sender as AnyObject).convert(CGPoint.zero, to: tableList)
let indexPath = tableList.indexPathForRow(at: buttonPosition)
if sender.tag == 101 {
if indexPath != nil {
print("Cell indexpath = \(String(describing: indexPath?.row))")
}
}
if sender.tag == 102 {
if indexPath != nil {
print("Cell indexpath = \(String(describing: indexPath?.row))")
}
}
}
Create a model to main the state of yesButton and noButton for each tableViewCell, i.e.
class Model {
var isYesSelected = false
var isNoSelected = false
}
Create a custom UITableViewCell with Outlets of yesButton and noButton.
Create a single #IBAction for both the buttons and handle their UI based on which button is tapped.
Also, use a buttonTapHandler to identify the row in which the button is tapped. It will be called everytime a button is tapped. We'll be setting this when creating the instance of TableViewCell in tableView(_:cellForRowAt:).
class TableViewCell: UITableViewCell {
#IBOutlet weak var yesButton: UIButton!
#IBOutlet weak var noButton: UIButton!
var buttonTapHandler: (()->())?
var model: Model?
override func prepareForReuse() {
super.prepareForReuse()
yesButton.backgroundColor = .gray
noButton.backgroundColor = .gray
}
func configure(with model: Model) {
self.model = model
self.updateUI()
}
#IBAction func onTapButton(_ sender: UIButton) {
model?.isYesSelected = (sender == yesButton)
model?.isNoSelected = !(sender == yesButton)
self.updateUI()
}
func updateUI() {
yesButton.backgroundColor = (model?.isYesSelected ?? false) ? .green : .gray
noButton.backgroundColor = (model?.isNoSelected ?? false) ? .green : .gray
}
}
UITableViewDataSource's tableView(_:cellForRowAt:) method goes like,
let numberOfCells = 10
var models = [Model]()
override func viewDidLoad() {
super.viewDidLoad()
(0..<numberOfCells).forEach { _ in
self.models.append(Model())
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return numberOfCells
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "tableCell", for: indexPath) as! TableViewCell
cell.configure(with: models[indexPath.row])
cell.buttonTapHandler = {
print(indexPath.row)
}
return cell
}
To get the totalPoints, count the models with isYesSelected = true, i.e.
let totalPoints = models.reduce(0) { (result, model) -> Int in
if model.isYesSelected {
return result + 1
}
return 0
}
print(totalPoints)
Get that Button using your Tag like below and after that, you can change the value as per you want.
var tmpButton = self.view.viewWithTag(tmpTag) as? UIButton
Simple 3 step process...!!
Define Model Class
Prepare tableView Cell & handle actions
Set up tableView in view controller
Let's start implementation:
1) Define Model Class
In UI, we have a information like question & it's answer (Yes/No). So design model respectively.
//MARK:- Class Declaration -
class Question {
let questionText: String
var answerState: Bool?
init(question: String) {
self.questionText = question
}
}
2. Prepare tableView Cell & handle actions
Create a custom tableView cell with Question Label, Yes Button & No Button. Link that view with respected #IBOutlets & #IBActions.
import UIKit
class TableViewCell: UITableViewCell {
#IBOutlet weak var questionLabel: UILabel!
#IBOutlet weak var yesButton: UIButton!
#IBOutlet weak var noButton: UIButton!
var question: Question?
var toggle: Bool? {
didSet {
question?.answerState = toggle
//Do buttons operations like...
if let isToggle = toggle {
yesButton.backgroundColor = isToggle ? .green : .gray
noButton.backgroundColor = isToggle ? .gray : .green
} else {
yesButton.backgroundColor = .gray
noButton.backgroundColor = .gray
}
}
}
func prepareView(forQuestion question: Question) {
self.question = question
questionLabel.text = question.questionText
toggle = question.answerState
}
//Yes Button - IBAction Method
#IBAction func yesButtonTapped(_ sender: UIButton) {
toggle = true
}
//No Button - IBAction Method
#IBAction func noButtonTapped(_ sender: UIButton) {
toggle = false
}
}
3. Set up tableView in view controller
class ViewController: UIViewController {
//Prepare questions model array to design our tableView data source
let arrQuestions: [Question] = [Question(question: "Do you speak English?"), Question(question: "Do you live in Chicago?")]
}
//MARK:- UITableView Data Source & Delegate Methods -
extension ViewController: UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return arrQuestions.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let tableViewCell = tableView.dequeueReusableCell(withIdentifier: "TableViewCell") as? TableViewCell else {
return UITableViewCell()
}
tableViewCell.prepareView(forQuestion: arrQuestions[indexPath.row])
return tableViewCell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 80.0
}
}
Create basic tableView and configure dataSource functions
Create tableView cell with two buttons
Create cell class with buttons outlets and actions
Result of this code
Enjoy!

index of button in custom cell

I create a custom cell that contains a button, I need to create segue from this button to other VC but first of all, I would like to push an object with that segue.
I already try to use cell.button.tag, but I did not succeed.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showMap" {
let mapVC = segue.destination as! MapViewController
//guard let indexPath = tableView.indexPathForSelectedRow else { return }
mapVC.place = places[] // <- "here I need index of button in cell"
}
}
Instead of using the segue, handle the navigation programatically through a closure in UITableViewCell.
class CustomCell: UITableViewCell {
var buttonTapHandler: (()->())?
#IBAction func onTapButton(_ sender: UIButton) {
self.buttonTapHandler?()
}
}
In the above code, I've create a buttonTapHandler - a closure, that will be called whenever the button inside the cell is tapped.
Now, in cellForRowAt method when you dequeue the cell, set the buttonTapHandler of CustomCell.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CustomCell", for: indexPath) as! CustomCell
cell.buttonTapHandler = {[weak self] in
if let mapVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "MapViewController") as? MapViewController {
mapVC.place = places[indexPath.row]
self?.navigationController?.pushViewController(mapVC, animated: true)
}
}
return cell
}
In the above code, buttonTapHandler when called will push a new instance of MapViewController along with the relevant place based on the indexPath.
if you don't want to execute your code in didSelectRowAt method, another good approach in my opinion is to create a delegate of your custom cell. See the code below
// This is my custom cell class
class MyCustomCell: UITableViewCell {
// The button inside your cell
#IBOutlet weak var actionButton: UIButton!
var delegate: MyCustomCellDelegate?
#IBAction func myDelegateAction(_ sender: UIButton) {
delegate?.myCustomAction(sender: sender, index: sender.tag)
}
// Here you can set the tag value of the button to know
// which button was tapped
func configure(index: IndexPath){
actionButton.tag = index.row
}
}
protocol MyCustomCellDelegate {
func myDelegateAction(sender: UIButton, index: Int)
}
Delegate the ViewController where you use your custom cell.
class MyViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "MyCellIdentifier", for: indexPath) as! MyCustomCell
cell.configure(index: indexPath)
cell.delegate = self
return cell
}
}
And at the end customize your method extending your custom cell delegate
extension MyViewController: MyCustomCellDelegate {
func myDelegateAction(sender: UIButton, index: Int) {
// Do your staff here
}
}
I hope I was helpful.
In the custom cell:
import UIKit
protocol CustomCellDelegate: class {
func btnPressed(of cell: CustomCell?)
}
class CustomCell: UITableViewCell {
weak var delegate: CustomCellDelegate?
#IBAction func btnTapped(_ sender: UIButton) {
delegate?.btnPressed(of: self)
}
}
And in the view controller:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: CustomCell = tableView.dequeueReusableCell(for: indexPath)
cell.delegate = self
return cell
}
extension ViewController: CustomCellDelegate {
func btnPressed(of cell: CustomCell?) {
if let cell = cell, let indexPath = tableView.indexPath(for: cell) {
// Your stuff here
}
}
}

Gather textfield text from a tableview cell (Swift)

I have a tableview with one textfield in each cell. I added a target like this:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "customLevelCell") as! LevelTableViewCell
cell.cellTextField.addTarget(self, action: #selector(ViewController.TextfieldEditAction), for: .editingDidEnd)
return cell
}
But found out that I'm not able to use the indexpath.row / sender.tag to get the specific textfield text
#objc func TextfieldEditAction(sender: UIButton) {
}
So my question is how can I get the text after the user has edited one of the textfields.
Also how can i get the indexpath.row or sender.tag which will be used to collect the text they added to that specific textfield.
The easiest way to handle this is probably to use a delegate protocol…
In your cell
protocol LevelTableViewCellDelegate: class {
func levelTableViewCell(_ levelTableViewCell: LevelTableViewCell, didEndEditingWithText: String?)
}
class LevelTableViewCell: UITableViewCell {
#IBOutlet private weak var cellTextField: UITextField!
var delegate: LevelTableViewCellDelegate?
override func awakeFromNib() {
cellTextField.addTarget(self, action: #selector(didEndEditing(_:)), for: .editingDidEnd)
}
#objc func didEndEditing(_ sender: UITextField) {
delegate?.levelTableViewCell(self, didEndEditingWithText: sender.text)
}
}
In your view controller
class TableViewController: UITableViewController {
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "LevelTableViewCell") as! LevelTableViewCell
cell.delegate = self
return cell
}
}
extension TableViewController: LevelTableViewCellDelegate {
func levelTableViewCell(_ levelTableViewCell: LevelTableViewCell, didEndEditingWithText: String?) {
let indexPath = tableView.indexPath(for: levelTableViewCell)
// Now you have the cell, indexPath AND the string
}
Also, note that the view outlet is be private. You'll find that you write cleaner code if you follow this rule
Following is the extension of UIView that can be used to get the cell or indexPath of the cell enclosing textField
extension UIView {
var tableViewCell : UITableViewCell? {
var subviewClass = self
while !(subviewClass is UITableViewCell){
guard let view = subviewClass.superview else { return nil }
subviewClass = view
}
return subviewClass as? UITableViewCell
}
func tableViewIndexPath(_ tableView: UITableView) -> IndexPath? {
if let cell = self.tableViewCell {
return tableView.indexPath(for: cell)
}
return nil
}
}
Example :-
#objc func TextfieldEditAction(sender: UITextField) {
//replace tableView with the name of your tableView
guard let indexPath = sender.tableViewIndexPath(tableView) else {return}
}

How to get table view index when click to radio button in swift 4?

I am doing project in swift 4. I created radiobutton class in tableview cell. I want to get cell index when click to which radio button. Please help me.
Edit: I just added my entire code. I could not solve my problem. All of the codes are as follows. I created a radio button class and gave it a radio button feature. I tried to integrate the radio button in the tableview and get the index of the cell when the button is clicked as below, but it gave libc++abi.dylib: terminating with uncaught exception of type NSException error.
import UIKit
class TVCIsGuvenligi: UITableViewCell {
lazy var radioButtonGroup: [RadioButton] = {
return [
btnRadioEvet,
btnRadioHayir,
]
}()
#IBOutlet var lblSoru: UILabel!
#IBOutlet var lblSoruAyrinti: UILabel!
#IBOutlet var lblEvet: UILabel!
#IBOutlet var lblHayır: UILabel!
#IBOutlet var btnRadioEvet: RadioButton!
#IBAction func btnRadioEvet(_ sender: RadioButton)
{
let indexPath = IndexPath.init(row: sender.tag, section: 0)
updateRadioButton(sender)
print("index \(indexPath)")
}
#IBOutlet var btnRadioHayir: RadioButton!
#IBAction func btnRadioHayir(_ sender: RadioButton)
{
let indexPath = IndexPath.init(row: sender.tag, section: 0)
updateRadioButton(sender)
print("index \(indexPath)")
}
override func awakeFromNib() {
super.awakeFromNib()
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
func updateRadioButton(_ sender: RadioButton){
radioButtonGroup.forEach { $0.isSelected = false }
sender.isSelected = !sender.isSelected
}
}
ViewController.swift
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cellIs", for: indexPath) as! TVCIsGuvenligi
cell.lblSoru.text = myTitle[indexPath.row]
cell.lblSoruAyrinti.text = myTitle[indexPath.row]
cell.btnRadioEvet.tag = (indexPath.row)
cell.btnRadioEvet.addTarget(self, action: #selector(getter: cell.btnRadioEvet), for: UIControlEvents.touchUpInside)
print("current cell tag \(cell.btnRadioEvet.tag)")
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
//getting the index path of selected row
let indexPath = tableView.indexPathForSelectedRow
//getting the current cell from the index path
let currentCell = tableView.cellForRow(at: indexPath!)! as! TVCIsGuvenligi
//getting the text of that cell.
let currentItem = currentCell.lblSoru.text
print("current item \(currentItem)")
}
use,
currentCell.radioButton.tag = indexPath.row
And use this tag to get index on the radio button click

How to change text of Label in UITableView based on button pressed?

I have a UITableView in ViewController which displays Default, five times on running in the simulator based on the following code.
class ViewController: UIViewController,UITableViewDataSource,UITableViewDelegate {
#IBOutlet weak var myTableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
}
func tableView(_ tableView:UITableView, numberOfRowsInSection section:Int) -> Int
{
return 5
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let myCell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! OutsideCell
myCell.titleLabel.text = "Default"
return myCell
}
#IBAction func firstButtonPressed(_ sender: Any){
}
#IBAction func secondButtonPressed(_ sender: Any) {
}
}
I also have 2 buttons at the bottom of ViewController and
I like to display text as First Button when firstButtonPressed and Second Button when secondButtonPressed instead of Default in UITableView. How is this possible?
You can loop through each cell of the table view when the button is tapped.
for cell in myTableView.visibleCells() {
cell.titleLabel?.text = "First Button"
}
You should use a generic method as eventButtonPressed and use the tag attribute to determine which button is pressed through the storyboard.
#IBAction func eventButtonPressed(_ sender: UIButton) {
if (sender.tag == 0) {
//First Button
} else if (sender.tag == 1) {
//Second Button
}
}
You can use switch operand if you have many cases.
switch sender.tag {
case 0:
print("First button pressed")
case 1:
print("Second button pressed")
default:
print("Default button pressed")
}
Hope it helps,

Resources