srsstyle
srsstyle

Reputation: 99

Dynamic tableview cell with editable textfield and textview data coming from server end

There is a scenario where I need to display data from a JSON in my table view cell with multiple data in the label, text field, text view..etc, after displaying I need to edit the text field and text view data from any cell, and once completed I need to send that values to the server side.

Please note: I do not know how many cells, as the data was fetched from the server end.

Please go through the image for a detailed description. These are the values in the cell like sl.no, date, time, record of the investigation.

I need to update the time and record of investigation in each cell where ever required.

Text field : Time & Textview : Record of investigation.

In submit button which is at the bottom, I need an array of updated data from each cell.

cellforRowatIndexpath looks like this :

    cell.sl.text = "Sl No."
    cell.Date.text = "Date:"
    cell.time.text = "Time:"
    cell.some.text = "Some"
    cell.record.text = "Record"
    cell.txtVw.layer.borderColor = UIColor.lightGray.cgColor
    cell.txtVw.layer.borderWidth = 1
    cell.nosl.text = "\(indexPath.row + 1)"
    cell.Datee.text = "28/04/2023"
    cell.timeetxt.text = "12:00"
    cell.somee.text = "Some content"
    cell.txtVw.text = "Some editable text"
    return cell

This is how my tableview looks after loading :

After Load

Please guide me how to modify the time "12:00" to "12:30" and "Some editable text" to "Something" and save it in each cell.

Advance thanks.

Upvotes: 0

Views: 318

Answers (2)

srsstyle
srsstyle

Reputation: 99

With the help of chatGPT, I am able to resolve this. I am posting here because maybe it help others.

My ViewController class looks like this: -

class ViewController{ 
var dataSource: [DataModel] = []
@IBOutlet weak var tableView: UITableView!

override func viewDidLoad() {
    super.viewDidLoad()
    // Fetch data from API
           fetchData()
           
           // Register table view cell
           tableView.register(CustomTableViewCell.self, forCellReuseIdentifier: "cell")
    // Do any additional setup after loading the view.
}

func fetchData() {
        // Simulating API call with hardcoded data
        let apiData = [
            ["id": 1, "title": "Title 1", "description": "Description 1"],
            ["id": 2, "title": "Title 2", "description": "Description 2"],
            ["id": 3, "title": "Title 3", "description": "Description 3"],
            ["id": 1, "title": "Title 1", "description": "Description 1"],
            ["id": 2, "title": "Title 2", "description": "Description 2"],
            ["id": 3, "title": "Title 3", "description": "Description 3"],
            ["id": 1, "title": "Title 1", "description": "Description 1"],
            ["id": 2, "title": "Title 2", "description": "Description 2"],
            ["id": 3, "title": "Title 3", "description": "Description 3"],
            ["id": 1, "title": "Title 1", "description": "Description 1"],
            ["id": 2, "title": "Title 2", "description": "Description 2"],
            ["id": 3, "title": "Title 3", "description": "Description 3"]
        ]
        
        // Parse API data into DataModel objects
        dataSource = apiData.compactMap { DataModel(dictionary: $0) }
        
        // Reload table view
        tableView.reloadData()
    }

// Save button action
@objc func saveButtonTapped(_ sender: UIButton) {
       guard let cell = sender.superview?.superview as? CustomTableViewCell,
             let indexPath = tableView.indexPath(for: cell) else {
           return
       }
      
       // Update data source with the latest text from the text field and text view
    let newData = DataModel(id: dataSource[indexPath.row].id,
                            title: cell.titleTextField.text ?? "",
                            description: cell.descriptionTextView.text ?? "")
       dataSource[indexPath.row] = newData
       
       // Reload the table view to reflect the changes
       tableView.reloadData()
   
   }


}


extension ViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, heightForRowAt indexPath: 
IndexPath) -> CGFloat {
    return 200
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: 
Int) -> Int {
    return dataSource.count
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: 
IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "cell", 
for: indexPath) as! CustomTableViewCell
    
    // Configure the cell
    let data = dataSource[indexPath.row]
    cell.configure(with: data)
    cell.saveButton.addTarget(self, action: #selector(saveButtonTapped), 
for: .touchUpInside)
    
    return cell
}
}
}

My TableViewCell : --

import UIKit

class CustomTableViewCell: UITableViewCell {

let titleTextField: UITextField = {
        let textField = UITextField()
        textField.translatesAutoresizingMaskIntoConstraints = false
        textField.placeholder = "Title"
        return textField
    }()
    
    let descriptionTextView: UITextView = {
        let textView = UITextView()
        textView.translatesAutoresizingMaskIntoConstraints = false
        textView.isScrollEnabled = false
        textView.font = UIFont.systemFont(ofSize: 14)
        return textView
    }()
    
    let saveButton: UIButton = {
        let button = UIButton(type: .system)
        button.translatesAutoresizingMaskIntoConstraints = false
        button.setTitle("Save", for: .normal)
        return button
    }()
    
    var data: DataModel?
    
    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        
        setupSubviews()
        setupConstraints()
    }
    
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        
        setupSubviews()
        setupConstraints()
    }
    
    func setupSubviews() {
        contentView.addSubview(titleTextField)
        contentView.addSubview(descriptionTextView)
        contentView.addSubview(saveButton)
    }
    
    func setupConstraints() {
        NSLayoutConstraint.activate([
            titleTextField.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 16),
            titleTextField.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 8),
            titleTextField.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -16),
            titleTextField.heightAnchor.constraint(equalToConstant: 30),
            
            descriptionTextView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 16),
            descriptionTextView.topAnchor.constraint(equalTo: titleTextField.bottomAnchor, constant: 8),
            descriptionTextView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -16),
            descriptionTextView.heightAnchor.constraint(equalToConstant: 80),
            
            saveButton.centerXAnchor.constraint(equalTo: contentView.centerXAnchor),
            saveButton.topAnchor.constraint(equalTo: descriptionTextView.bottomAnchor, constant: 8)
        ])
    }
    
    func configure(with data: DataModel) {
        self.data = data
        titleTextField.text = data.title
        descriptionTextView.text = data.description
    }
}

My Model : -

import Foundation
struct DataModel {
let id: Int
let title: String
let description: String

init(id: Int, title: String, description: String) {
    self.id = id
    self.title = title
    self.description = description
}

init?(dictionary: [String: Any]) {
    guard let id = dictionary["id"] as? Int,
          let title = dictionary["title"] as? String,
          let description = dictionary["description"] as? String else {
        return nil
    }
    
    self.init(id: id, title: title, description: description)
}
}

Upvotes: 0

OneCommentMan
OneCommentMan

Reputation: 169

The code I'm about to paste is a rough draft, but should point you in the right direction. Don't expect this to work if you copy and paste.

    //Store your data.
                                                                                                                                      
 struct Item {
        var time: String
        var record: String
    }
    

 class CustomCell: UITableViewCell {
    
    // MARK: - Properties
    
    private let textField = UITextField() // or outlet
    private let textView = UITextView() // or outlet
    
    
    var textFieldTextDidChange: ((String?) -> Void)?
    var textViewTextDidChange: ((String?) -> Void)?
    
    // MARK: - Initialization
    
    override func awakeFromNib() {
        
        // Configure the text field
        textField.addTarget(self, action: #selector(textFieldDidChange(_:)), for: .editingChanged)
        
        textView.delegate = self
        
    }
    
    // MARK: - Actions
    
    @objc private func textFieldDidChange(_ textField: UITextField) {
        textFieldTextDidChange?(textField.text)
    }
    
    // MARK: - UITextViewDelegate
    
    func textViewDidChange(_ textView: UITextView) {
        textViewTextDidChange?(textView.text)
    }
}


class TestViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
    
    var dataDictionary = [Int: Item]()
    
    var tableView = UITableView()
    
    override func viewDidLoad() {
        tableView.delegate = self
        tableView.dataSource = self
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        
        let cell = CustomCell()
        
        cell.textFieldTextDidChange = { [weak self] text in
            
            dataDictionary[indexPath.row]?.time = text
            
        }
        
        cell.textViewTextDidChange = { [weak self] text in
            self?.dataDictionary[indexPath.row]?.record = text ?? ""
        }
        
        return cell
    }
}

Upvotes: 0

Related Questions