1

I am building an app in swift which has takes user details like Name, age, gender, picture, About me. I am saving all the data in core data and and displaying it in tableView. It also has a swipe to delete feature. What I want to do is add Swipe to edit feature which takes you to EnterDetail screen and replace the old data. But I don't know how to do it. Suggest me a way to do it. I don't know how to replace data at a specific row of the tableView. I tried using setValues() but it's not working.

ItemTableViewCell

import UIKit

class ItemTableViewCell: UITableViewCell {
    
    @IBOutlet weak var name: UILabel!
    @IBOutlet weak var age: UILabel!
    @IBOutlet weak var gender: UILabel!
    @IBOutlet weak var photoView: UIImageView!
    @IBOutlet weak var aboutMe: UILabel!
    
    override func awakeFromNib() {
        super.awakeFromNib()
        photoView.layer.cornerRadius = photoView.frame.height/2 // For round imageView
        photoView.layer.masksToBounds = true
    }

    override func setSelected(_ selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)

        // Configure the view for the selected state
    }

}

FirstViewController

    import UIKit
    import CoreData
    
    class FirstViewController: UIViewController {
        

    @IBOutlet weak var dataTableView: UITableView!
    var data: NSManagedObject? = nil
    let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
    var itemArrayFVC = [Items]()
    var flag = 0
    lazy var searchBar: UISearchBar = UISearchBar()
    var filteredData = [Items]()
    var filtered = false
    let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Items")

    var editRowIndexPath: IndexPath = []
    var editClicked = false
    override func viewDidLoad() {
        super.viewDidLoad()
        navigationItem.titleView = searchBar
        searchBar.delegate = self
        searchBar.placeholder = "Search"
        let tap = UITapGestureRecognizer(target: self, action: #selector(UIInputViewController.dismissKeyboard))
        view.addGestureRecognizer(tap)
    }
    override func viewWillAppear(_ animated: Bool) {
        loadItems()
        //dataTableView.reloadData()
    }
    override func viewWillDisappear(_ animated: Bool) {
        if editClicked == true {
            print("view will disappear")
            
        }
        
    }
    @objc func dismissKeyboard() {
        //Causes the view (or one of its embedded text fields) to resign the first responder status.
        view.endEditing(true)
    }
    //MARK: - TableView methods
extension FirstViewController: UITableViewDelegate, UITableViewDataSource {
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        if !filteredData.isEmpty {
            return filteredData.count
        }
        return filtered ? 0 : itemArrayFVC.count
    }
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: Constants.cellIdentifier, for: indexPath) as! ItemTableViewCell
        let item = itemArrayFVC[indexPath.row]
        if !filteredData.isEmpty {
            let filterItem = filteredData[indexPath.row]
            cell.name.text = filterItem.fName! + " " + filterItem.lName!
            cell.age.text = filterItem.dateofbirth
            cell.gender.text = filterItem.gender
            cell.photoView.image = UIImage(data: filterItem.image!)
            cell.aboutMe.text = filterItem.aboutMe ?? "About me"
        }
        else {
            cell.name.text = item.fName! + " " + item.lName!
            cell.age.text = item.dateofbirth
            cell.gender.text = item.gender
            cell.photoView.image = UIImage(data: item.image!)
            cell.aboutMe.text = item.aboutMe ?? "About me"
        }
        return cell
    }
    // For swipe to delete gesture for deleting from database and tableView
    func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
        if editingStyle == .delete {
            context.delete(itemArrayFVC[indexPath.row])
            itemArrayFVC.remove(at: indexPath.row)
            saveItems()
            tableView.reloadData()
        }
    }
    func tableView(_ tableView: UITableView, leadingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
        editClicked = true
        let editAction = UIContextualAction(style: .normal, title: "Edit") { (action, sourceView, completionHandler) in
            self.performSegue(withIdentifier: "toEditScreen", sender: indexPath)
            self.editRowIndexPath = indexPath
            completionHandler(true)
            print(self.editRowIndexPath)
        }
        editAction.backgroundColor = UIColor.lightGray
        let configuration = UISwipeActionsConfiguration(actions: [editAction])
        return configuration
    }
    
    func loadItems(with request: NSFetchRequest<Items> = Items.fetchRequest()) {

        do {
            itemArrayFVC = try context.fetch(request)
        } catch {
            print("Error fetching data from context \(error)")
        }
        dataTableView.reloadData()
    }
    
    func saveItems() {
        do {
            try context.save()
        } catch {
            print("Error saving context \(error)")
        }
    }
}
 
    extension FirstViewController: UISearchBarDelegate {
    func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
        
        do {
            let results   = try context.fetch(fetchRequest)
            filteredData = results as! [Items]
        } catch let error as NSError {
            print("Could not fetch \(error)")
        }
        if let text = searchBar.text {
            filterText(text)
        }
        if searchBar.text?.count == 0 {
            loadItems()
            DispatchQueue.main.async {
                searchBar.resignFirstResponder()
            }
        }
    }
    func filterText(_ query: String) {
        filteredData.removeAll()
        for string in itemArrayFVC {
            if (string.fName!).lowercased().starts(with: query.lowercased()) || (string.lName!).lowercased().starts(with: query.lowercased()){
                filteredData.append(string)
            }
        }
        dataTableView.reloadData()
        filtered = true
    }
}

EnterDetailViewController

import UIKit
import CoreData

class EnterDetailsViewController: UIViewController, UINavigationControllerDelegate, 
UIImagePickerControllerDelegate {

@IBOutlet weak var fName: UITextField!
@IBOutlet weak var lName: UITextField!
@IBOutlet weak var dobPicker: UITextField!
@IBOutlet var genderButtons: [UIButton]!
@IBOutlet weak var aboutMe: UITextView!
@IBOutlet weak var imageView: UIImageView!

@IBOutlet weak var labelLastName: UILabel!
@IBOutlet weak var labelDob: UILabel!
@IBOutlet weak var labelGender: UILabel!
@IBOutlet weak var labelMale: UILabel!
@IBOutlet weak var labelFemale: UILabel!
@IBOutlet weak var labelAboutMe: UILabel!

@IBOutlet weak var btnSelectImage: UIButton!
@IBOutlet weak var btnSave: UIButton!

var itemArray = [Items]()
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext

let datePicker = UIDatePicker()
var gender: String = ""
var age:[Int] = []

override func viewDidLoad() {
    super.viewDidLoad()
    
    imageView.layer.cornerRadius = imageView.frame.height/2 // For round imageView
    imageView.layer.masksToBounds = true
    
    print(FileManager.default.urls(for: .documentDirectory, in: .userDomainMask))
    showDatePicker()
    
    self.fName.delegate = self
    self.lName.delegate = self
    self.dobPicker.delegate = self

    labelLastName.isHidden = true
    lName.isHidden = true
    labelDob.isHidden = true
    dobPicker.isHidden = true
    labelGender.isHidden = true
    genderButtons[0].isHidden = true
    labelMale.isHidden = true
    genderButtons[1].isHidden = true
    labelFemale.isHidden = true
    labelAboutMe.isHidden = true
    aboutMe.isHidden = true
    imageView.isHidden = true
    btnSelectImage.isHidden = true
    btnSave.isHidden = true
    
    let tap = UITapGestureRecognizer(target: self, action: #selector(UIInputViewController.dismissKeyboard))
    view.addGestureRecognizer(tap)
    //loadItems()
}
@objc func dismissKeyboard() {
    view.endEditing(true)
}

//MARK: - getGender
@IBAction func selectGender(_ sender: UIButton) {
    for button in genderButtons {
        button.isSelected = false
    }
    sender.isSelected = true
    gender = sender.currentTitle!
    print(gender)
}

//MARK: - Image
@IBAction func selectImage(_ sender: UIButton) {
    
    let imagePickerController = UIImagePickerController()
    imagePickerController.delegate = self
    present(imagePickerController, animated: true, completion: nil)
    
    let actionSheet = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
    
    actionSheet.addAction(UIAlertAction(title: "Camera", style: .default, handler: { (alert:UIAlertAction!) -> Void in
        self.showCamera()
    }))
    
    actionSheet.addAction(UIAlertAction(title: "Album", style: UIAlertAction.Style.default, handler: { (alert:UIAlertAction!) -> Void in
        self.showAlbum()
    }))
    
    actionSheet.addAction(UIAlertAction(title: "Cancel", style: UIAlertAction.Style.cancel, handler: nil))
    
    self.present(actionSheet, animated: true, completion: nil)
}

//To select and display image
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
    imageView.image = info[UIImagePickerController.InfoKey.originalImage] as? UIImage
    self.dismiss(animated: true, completion: nil)  // to dismiss the navigator when the image has been selected.
}

//MARK: - Date
func showDatePicker(){
    //Formate Date
    datePicker.datePickerMode = .date
    //ToolBar
    let toolbar = UIToolbar();
    toolbar.sizeToFit()
    let doneButton = UIBarButtonItem(title: "Done", style: .plain, target: self, action: #selector(donedatePicker));
    let spaceButton = UIBarButtonItem(barButtonSystemItem: UIBarButtonItem.SystemItem.flexibleSpace, target: nil, action: nil)
    let cancelButton = UIBarButtonItem(title: "Cancel", style: .plain, target: self, action: #selector(cancelDatePicker));
    toolbar.setItems([doneButton,spaceButton,cancelButton], animated: false)
    dobPicker.inputAccessoryView = toolbar
    dobPicker.inputView = datePicker
}

@objc func donedatePicker(){
    let formatter = DateFormatter()
    formatter.dateFormat = "yyyy/MM/dd"
    dobPicker.text = formatter.string(from: datePicker.date)
    self.view.endEditing(true)
    age = getAgeFromDOB(date: dobPicker.text!)
    if age[0] < 16 {
        showAlert("Age should be above 16")
        dobPicker.text = ""
    } else {
        labelGender.isHidden = false
        genderButtons[0].isHidden = false
        labelMale.isHidden = false
        genderButtons[1].isHidden = false
        labelFemale.isHidden = false
        labelAboutMe.isHidden = false
        aboutMe.isHidden = false
        imageView.isHidden = false
        btnSelectImage.isHidden = false
        btnSave.isHidden = false
    }
    print(age)
}

@objc func cancelDatePicker(){
    self.view.endEditing(true)
}
func getAgeFromDOB(date: String) -> Array<Int> {
    
    let dateFormater = DateFormatter()
    dateFormater.dateFormat = "yyyy/MM/dd"
    let dateOfBirth = dateFormater.date(from: date)
    
    let calender = Calendar.current
    
    let dateComponent = calender.dateComponents([.year, .month, .day], from:
        dateOfBirth!, to: Date())
    
    return [dateComponent.year!, dateComponent.month!, dateComponent.day!]
}

//MARK: - Save button
@IBOutlet weak var save: UIButton!
@IBAction func saveClicked(_ sender: UIButton) {
    //let text = fName.text ?? ""
    if (fName.text?.isEmpty)! {
        showAlert("Enter first name")
    }
    else if (lName.text?.isEmpty)! {
        showAlert("Enter last name")
    }
    else if (dobPicker.text?.isEmpty)! {
        showAlert("Enter D.O.B")
    }
    else if  gender.isEmpty {
        showAlert("Choose gender")
    }
    else {
        //save.isEnabled = !text.isEmpty
        let newItem = Items(context: context)
        newItem.fName = fName.text!
        newItem.lName = lName.text!
        newItem.dateofbirth = dobPicker.text!
        newItem.gender = gender
        newItem.image = (imageView.image?.pngData())!
        newItem.aboutMe = aboutMe.text!
        newItem.age = age
        saveItems()
        self.navigationController?.popViewController(animated: true)
    }
}
//MARK: - Save items function
func saveItems() {
    do {
        try context.save()
    } catch {
        print("Error saving context \(error)")
    }
}

//MARK: - Alert function
func showAlert(_ titleMessage: String) {
    let alert = UIAlertController(title: titleMessage, message: "", preferredStyle: .alert)
    alert.addAction(UIAlertAction(title: "OK", style: .cancel, handler: { action in
        print("Tapped OK")
    }))
    present(alert, animated: true)
}

//MARK: - Camera function
func showCamera() {
    if !UIImagePickerController.isSourceTypeAvailable(.camera) {
        let alertController = UIAlertController(title: nil, message: "Device has no camera.", preferredStyle: .alert)
        
        let okAction = UIAlertAction(title: "OK", style: .default, handler: { (alert: UIAlertAction!) in
        })
        
        alertController.addAction(okAction)
        self.present(alertController, animated: true, completion: nil)
    } else {
        let picker = UIImagePickerController()
        picker.sourceType = .camera
        //for camera front
        // picker.cameraDevice = .front
        picker.delegate = self
        picker.allowsEditing = false
        present(picker, animated: true)
    }
}
//MARK: - Album function
func showAlbum() {
    let imageController = UIImagePickerController()
    imageController.delegate = self
    imageController.sourceType = UIImagePickerController.SourceType.photoLibrary


    self.present(imageController, animated: true, completion: nil)
        print("Select image")
    }
}

    extension EnterDetailsViewController: UITextFieldDelegate {
        func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
            performAction(textField.tag)
            return true
        }
        func performAction(_ tag: Int) {
            switch tag {
            case 0:
                labelLastName.isHidden = false
                lName.isHidden = false
                //lName.becomeFirstResponder()
            case 1:
                labelDob.isHidden = false
                dobPicker.isHidden = false
                //dobPicker.becomeFirstResponder()
                // all other cases of Unhiding labels and text fields is written inside donedatePicker() function
            default: break
            }
        }
    }
1
  • Your data is stored in itemArrayFVC, so to replace data at a specific row of the tableView, just enter the changes into itemArrayFVC in the correct position, then update the table and it will reflect the new data. Commented Jul 23, 2021 at 17:04

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.