iosswiftuitableviewtableviewdidset

Stuck at reassigning TableView Cell property


I'm working on an App where you can track your reading progress for Books.

The HomeViewController contains a TableView that lists the books you have added. It has a progress Bar and shows what page you're on. The AddBookController is for adding Data about a book that gets delegated to the HomeViewController and is then listed as a TableView Row. The BookDetailController is shown when you select a Row and is for updating the Page you're on. I'll provide some screenshots.

I'm stuck at changing the "currentPage" property of the TableView Cell once you update it in the BookDetailViewController. I'm able to send the updatedPage to the HomeViewController (where the TableView is) but I don't know how to align the values of the Cells with the new value. My idea was to use an didSet-observer but I can't figure out how to set it up the right way.

Here is my code:

HomeViewController

    class HomeViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, SendingBookDataProtocol {
    
        var updatedPage = String() {
            didSet {    // probably at the wrong place but everything I've tried didn't work
                print(updatedPage)
                tableView?.reloadData()
            }
        }
    
        
        var items = [BookItem]()
        
        var item: BookItem?
    
        override func viewDidLoad() {
            super.viewDidLoad(){
    
            tableView?.delegate = self
            tableView?.dataSource = self
            
            let nib = UINib(nibName: "BookCell", bundle: nil)
            tableView?.register(nib, forCellReuseIdentifier: "BookCell")
    
    }
    
        func sendDataToHomeController(bookEntry item:BookItem) {
            items.append(item)
            tableView.reloadData()
        }
    
        func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
            items.count
        }
    
      func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
          let bookDetailVc = self.storyboard?.instantiateViewController(withIdentifier: "BookDetailView") as? BookDetailViewController
              let item = items[indexPath.row]
              
              let currentPageInt = Float(item.currentPage)!
              let totalPagesInt = Float(item.totalPages)!
              let result = Int(totalPagesInt - currentPageInt)
              let percentageRead = Int((currentPageInt / totalPagesInt) * 100)
              
            bookDetailVc?.lblName = item.title
            bookDetailVc?.lblCurrentPage = Int(Float(item.currentPage)!)
     // ...some more Code. Basically just sending the Data to BookDetailViewController
       
            self.navigationController!.pushViewController(bookDetailVc!, animated: true)
                                         
      }
    
        func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
            let cell = tableView.dequeueReusableCell(withIdentifier: "BookCell", for: indexPath) as! BookCell
            item = items[indexPath.row]
        
            cell.title.text = item?.title
    //... and so on
                cell.pageNumbers.text = "S. " + item!.currentPage + " / " + item!.totalPages //here currentPage needs to be updated
                return cell
            }
        }

BookDetailViewController

class BookDetailViewController: HomeViewController, UIPickerViewDelegate, UIPickerViewDataSource{

    @IBOutlet weak var bookTitle: UILabel!
//...
    @IBOutlet weak var numberPicker: UIPickerView!
    
    var lblName = String()
//...
    var lblCurrentPage = Int()

    override func viewDidLoad() {
        super.viewDidLoad()

        self.numberPicker.delegate = self
        self.numberPicker.dataSource = self
//...
}

    func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {

            let valueSelected = pickerData[row] as String
        
        if let homeVc = storyboard?.instantiateViewController(withIdentifier: "getBookData") as? HomeViewController{
            homeVc.updatedPage = valueSelected
            homeVc.tableView?.reloadData()
        }
     }
    }

AddBookController

protocol SendingBookDataProtocol {
    func sendDataToHomeController(bookEntry: BookItem)
}

struct BookItem {
    let title,author,currentPage,totalPages:String
    let image: UIImage?
}

class AddBookController: UIViewController, UITextFieldDelegate, UIImagePickerControllerDelegate, UINavigationControllerDelegate {

var delegate: SendingBookDataProtocol? = nil

 @IBAction func buttonSave(_ sender: Any) {

        let bookEntry = BookItem(title: textfieldTitle.text!, author: textfieldAuthor.text!, currentPage: fieldCurrentPage.text!, totalPages: fieldTotalPages.text!, image: bookImage)
        
        self.delegate?.sendDataToHomeController(bookEntry: bookEntry)
                  dismiss(animated: true, completion: nil)
     }
}

// rest should be irrelevant

Here are the screenshots:

HomeViewController

AddBookController

BookDetailViewController


Solution

  • You have to create a delegate in BookDetailViewController, like this :

    protocol BookDetailDelegate: AnyObject {
        func updatePage(for bookItem : BookItem)
    }
    
    
    class BookDetailViewController: HomeViewController, UIPickerViewDelegate, UIPickerViewDataSource {
        var item: BookItem
        var delegate: BookDetailDelegate? 
        //...
        }
        
        func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
        
           let valueSelected = pickerData[row] as String
           delegate?.updatePage(valueSelected)
         }
    }
    

    And then in the HomeViewController:

    class HomeViewController {
    }
    //....
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
              let bookDetailVc = self.storyboard?.instantiateViewController(withIdentifier: "BookDetailView") as? BookDetailViewController
                  let item = items[indexPath.row]
                bookDetailVc?.item = item
                bookDetailVc?.delegate = self
           
                self.navigationController!.pushViewController(bookDetailVc!, animated: true)
                                             
          }
    //.....
    
    extension HomeViewController: BookDetailDelegate {
        func updatePage(for bookItem: BookItem) {
            let index = item.firstIndex(where: {$0.id = bookItem.id}) //here you should have a UUID or something
            item[index] = bookItem
            tableView.performBatchUpdates({
                    self.tableView.reloadRows(at: IndexPath(row: index, section: 0, with: .none)}
                }, completion: nil))
        }