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
            }
        }
    }
Asad
  • 11
  • 2
  • 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. – john elemans Jul 23 '21 at 17:04

0 Answers0