title: UITableViewController's Cell's Content disappearing
body: <p>I am making a coin collection app which is supposed to help users maintain a portable record of their collection. There are two view controllers so far: the <code>CoinTableViewController</code> which presents a tableview of all the categories of coins, and the <code>CoinCategoryViewController</code> which is supposed to have a table view of the specific coins in this category.</p>
  <p>I obviously want my coins to be reportable based on multiple criteria (such as the same year, same country, etc. and etc.). To this effect, I have created a reusable tableview cell subclass called <code>CoinTableViewCell</code> that has 5 UILabels which represent all the information that the coins into the collection can be grouped into categories by.</p>
  <p>Here is what my storyboard looks like</p>
  <p><a href="https://i.stack.imgur.com/nrofp.jpg" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/nrofp.jpg" alt="CoinTableViewController storyboard with labels" /></a></p>
  <p>The logic is that based on my current sorting criteria, I can hide certain labels from the cell to reflect the criteria that the coins were sorted by. The settings wheel opens up a menu that appears on the left side of the screen with options for how to sort the coins, and the closing of the menu makes the Coin Categories controller resort the coins in the collection if the sorting option was changed.</p>
  <p><a href="https://i.stack.imgur.com/NilT7.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/NilT7.png" alt="Example of Menu" /></a></p>
  <p>My problem is that while my program works overall, sometimes, some of the cells do not appear completely after the program resorts them (after the user opens up the menu and selects an option).</p>
  <p>Here's what this looks like:
  <a href="https://i.stack.imgur.com/QM8Oj.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/QM8Oj.png" alt="problematic table view controller" /></a></p>
  <p>As you may see, the bottom two cells in the view controller are missing the top label even though the other cells have them. And since I have implemented the tableview controller's cells as being resizable, the table view cells should automatically resize themselves to fit the content inside.</p>
  <p>Here is my code:</p>
  <pre class="lang-swift prettyprint-override"><code>//  Controls the table view controller showing the general coins (one per each category)
  import UIKit
  import CoreData
  
  class CoinTableViewController: UITableViewController, NSFetchedResultsControllerDelegate, UISearchResultsUpdating, UITabBarControllerDelegate
  {
      //this is an array of all the coins in the collection
      //each row of this two-dimensional array represents a new category
  var coinsByCategory: [CoinCategoryMO] = []
  var fetchResultController: NSFetchedResultsController&lt;CoinCategoryMO&gt;!
  
  //we sort the coins by the category and then display them in the view controller
  //example includes [ [Iraq Dinar 1943, Iraq Dinar 1200], etc. etc.]
  
  //&lt;OTHER VARIABLES HERE&gt;
  
  //the data here is used for resorting the coins into their respective categories
  
  //the default sorting criteria is sorting the coins into categories with the same country, value, and currency
  //and the user can change the app's sorting criteria by opening the ConfiguringPopoverViewController and changing the sorting criteria there
  private var isCurrentlyResortingCoinsIntoNewCategories : Bool = false
  
  override func viewDidLoad()
  {
      super.viewDidLoad()
      
      self.tabBarController?.delegate = self
      
      //we now fetch the data
      let fetchRequest : NSFetchRequest&lt;CoinCategoryMO&gt; = CoinCategoryMO.fetchRequest()
      
      if let appDelegate = (UIApplication.shared.delegate as? AppDelegate)
      {
          let context = appDelegate.persistentContainer.viewContext
          
          let sortDescriptor = NSSortDescriptor(key: &quot;index&quot;, ascending: true)
          fetchRequest.sortDescriptors = [sortDescriptor]
          
          fetchResultController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: context, sectionNameKeyPath: nil, cacheName: nil)
          fetchResultController.delegate = self
          
          do
          {
              try fetchResultController.performFetch()
              if let fetchedObjects = fetchResultController.fetchedObjects
              {
                  self.coinsByCategory = fetchedObjects
              }
          }
          catch
          {
              print(error)
          }
      }
              
      //if there is an empty area in the table view, instead of showing
      //empty cells, we show a blank area
      self.tableView.tableFooterView = UIView()
      
      //we configure the row heights for the table view so that the cells are resizable.
      //ALSO: should the user want to adjust the text size in &quot;General&quot;-&gt;&quot;Accessibility&quot;
      //the text size in the app will be automatically adjusted for him...
      tableView.estimatedRowHeight = 120
      tableView.rowHeight = UITableViewAutomaticDimension
      
      //WE CONFIGURE THE SEARCH BAR AND NAVIGATION BAR....
  
      //if the user scrolls up, he sees a white background, not a grey one
      tableView.backgroundView = UIView()
  }
  
  override func numberOfSections(in tableView: UITableView) -&gt; Int {
      return fetchResultController.sections!.count
  }
  
  override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -&gt; Int
  {
      if searchController != nil &amp;&amp; searchController.isActive
      {
          return searchResults.count
      }
      else
      {
          if let sections = fetchResultController?.sections
          {
              return sections[section].numberOfObjects
          }
          else
          {
              return 0
          }
  
      }
  }
  
  override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -&gt; UITableViewCell {
  
      //configure the cell
      let cellIdentifier = &quot;Cell&quot;
      let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as! CoinTableViewCell
      
      //Initialize the Cell
      let category = (searchController != nil &amp;&amp; searchController.isActive) ? searchResults[indexPath.row] : coinsByCategory[indexPath.row]
      
      //we now remove the extra labels that we do not need
      cell.configureLabelsForCategoryType(theType: (category.coinCategory?.currentCategoryType)!)
      
      let sampleCoin : Coin = category.coinCategory!.getCoin(at: 0)!
      
      cell.countryLabel.text = &quot;Country: \(sampleCoin.getCountry())&quot;
      cell.valueAndDenominationLabel.text = &quot;Value &amp; Denom.: \(sampleCoin.valueAndDenomination)&quot;
      
      //now we add in the quantity
      cell.quantityLabel.text = &quot;Quantity: \(String(describing: coinsByCategory[indexPath.row].coinCategory!.count))&quot;
      
      //we now add in the denomination
      cell.denominationOnlyLabel.text = &quot;Denom.: \(sampleCoin.getDenomination())&quot;
      
      //we now add in the year
      if sampleCoin.getYear() == nil
      {
          cell.yearLabel.text = &quot;Year: &quot; + (Coin.DEFAULT_YEAR as String)
      }
      else
      {
          let yearABS = abs(Int32(sampleCoin.getYear()!))
          cell.yearLabel.text = &quot;Year: \(yearABS) \(sampleCoin.getYear()!.intValue &gt; 0 ? TimePeriods.CE.rawValue : TimePeriods.BCE.rawValue)&quot;
      }
      
      //we add in an accessory to indicate that clicking this cell will result in more information
      cell.accessoryType = .disclosureIndicator
      
      return cell
  }
  
  func deleteCoinCategory(rowPath: IndexPath)
  {
      if 0 &lt;= rowPath.row &amp;&amp; rowPath.row &lt; self.coinsByCategory.count
      {
          //we have just tested that the rowPath index is valid
          if let appDelegate = (UIApplication.shared.delegate as? AppDelegate)
          {
              let context = appDelegate.persistentContainer.viewContext
              let coinCategoryToDelete = self.fetchResultController.object(at: rowPath)
              context.delete(coinCategoryToDelete)
              
              appDelegate.saveContext()
              
              //ok we now deleted the category, now we update the indices
              updateIndices()
              appDelegate.saveContext()
          }
      }
  }
  
  func deleteCoin(c: Coin, indexOfSelectedCategory: IndexPath) -&gt; Bool
  {
      //we have a coin that we want to delete from this viewcontroller
      //and the data contained in it.
      //
      //the parameter indexOfSelectedCategory refers to the IndexPath of the
      //row in the TableView contained in THIS viewcontroller whose category
      //of coins we are modifying in this method
      //
      //Return value: a boolean that indicates whether a single coin has
      //been deleted - meaning that the user should return to the parentviewcontroller
      if 0 &lt; indexOfSelectedCategory.row &amp;&amp; indexOfSelectedCategory.row &lt; self.coinsByCategory.count &amp;&amp; self.coinsByCategory[indexOfSelectedCategory.row].coinCategory?.hasCoin(c: c) == true
      {
          //the index is valid as it refers to a category in the coinsByCategory array
          //and the examined category has the coin in question
          if self.coinsByCategory[indexOfSelectedCategory.row].coinCategory?.count == 1
          {
              //the coin &quot;c&quot; that we are going to delete is the only coin in the entire category
              //we reduce the problem to a simpler one that has been already solved (thanks mathematicians!)
              self.deleteCoinCategory(rowPath: indexOfSelectedCategory)
              
              return true
          }
          else
          {
              //there is more than one coin in the category
              self.coinsByCategory[indexOfSelectedCategory.row].coinCategory?.removeCoin(c: c)
              
              //we save the changes in the database...
              if let appDelegate = (UIApplication.shared.delegate as? AppDelegate)
              {
                  appDelegate.saveContext()
              }
              
              return false
          }
      }
      
      return false
  }
  
  func addCoin(coinToAdd: Coin)
  {
      //we check over each category to see if the coin can be added
      var addedToExistingCategory: Bool = false
      
      if let appDelegate = UIApplication.shared.delegate as? AppDelegate
      {
          for i in 0..&lt;self.coinsByCategory.count
          {
              if self.coinsByCategory[i].coinCategory?.coinFitsCategory(aCoin: coinToAdd) == true
              {
                  //we can add the coin to the category
                  self.coinsByCategory[i].coinCategory = CoinCategory(coins: self.coinsByCategory[i].coinCategory!.coinsInCategory+[coinToAdd], categoryType: coinsByCategory[i].coinCategory!.currentCategoryType)
                  addedToExistingCategory = true
                  break
              }
          }
      
          if addedToExistingCategory == false
          {
              //since the coinToAdd does not fall in the existing categories, we create a new one
              let newCategory = CoinCategoryMO(context: appDelegate.persistentContainer.viewContext)
          
              newCategory.coinCategory = CoinCategory(coins: [coinToAdd], categoryType: CoinCategory.CategoryTypes.getTheCategoryFromString(str: UserDefaults.standard.object(forKey: &quot;currentSortingCriteria&quot;) as! NSString).rawValue)
              
              //this index indicates that we are going to insert this newCategory into index &quot;0&quot; of all the categories in the table
              newCategory.index = 0
          }
          
          appDelegate.saveContext()
          
          //now since we have added the coin, we now updated the indices of each CoinCategoryMO object
          updateIndices()
      }
  }
  
  func coinFitsExistingCategory(coin: Coin) -&gt; Bool
  {
      //this function checks if the coin can be added to the existing categories
      for i in 0..&lt;self.coinsByCategory.count
      {
          if self.coinsByCategory[i].coinCategory?.coinFitsCategory(aCoin: coin) == true
          {
              //we can add the coin to the category
              return true
          }
      }
      
      return false
  }
  
  func resortCoinsInNewCategories(newCategorySetting : CoinCategory.CategoryTypes?)
  {
      //we want to resort all the coins in the category by new sorting criteria
      if newCategorySetting != nil &amp;&amp; newCategorySetting! != CoinCategory.CategoryTypes.getTheCategoryFromString(str: UserDefaults.standard.object(forKey: &quot;currentSortingCriteria&quot;) as! NSString)
  
      {
          //We have a valid CoinCategory.CategoryTypes sorting criteria that is different from the one currently used.
          //We resort the coins in the collection by the new category
          UserDefaults.standard.setValue(newCategorySetting!.rawValue, forKey: &quot;currentSortingCriteria&quot;)
          
          if self.coinsByCategory.count != 0
          {
              //we actually have some coins to resort... let's get to work!
              self.isCurrentlyResortingCoinsIntoNewCategories = true
          
              //we first get an array of all the coins in existing categories
              var allCoinsArray : [Coin] = []
          
              for i in 0..&lt;self.coinsByCategory.count
              {
                  allCoinsArray += self.coinsByCategory[i].coinCategory!.coinsInCategory
              }
          
              //now we need to delete all the categories in existence...
              let firstCategoryIndexPath = IndexPath(row: 0, section: 0)
              let numberOfCategoriesToDelete = self.coinsByCategory.count
          
              for _ in 0..&lt;numberOfCategoriesToDelete
              {
                  self.deleteCoinCategory(rowPath: firstCategoryIndexPath)
              }
          
              //OK... now that we have deleted all old categories... it is time to start to create new ones...
              for i in 0..&lt;allCoinsArray.count
              {
                  //AND we add the coin to the array!
                  //this function also automatically updates the indices, so it is not an issue there
                  self.addCoin(coinToAdd: allCoinsArray[i])
              }
          
              //we are done resorting
              self.isCurrentlyResortingCoinsIntoNewCategories = false
          }
      }
  }
  
  private func updateIndices()
  {
      //this function updates the &quot;index&quot; property so that
      //each CoinCategoryMO object in the coinsByCategory array
      //has an index corresponding to its position.
      //After this function is called, we must save the core data in the AppDelegate.
      //
      //This function is called ONLY after the changes to the CoinCategoryMO objects
      //are saved in core data and the self.coinsByCategory array is updated to have
      //the latest version of the data
      for i in 0..&lt;self.coinsByCategory.count
      {
          //the only reason why we create an entirely new CoinCategory object
          //is that the creation of an entirely new CoinCategory object
          //is the only way that the appDelegate will save the information
          self.coinsByCategory[i].index = Int16(i)
      }
      
      if let appDelegate = UIApplication.shared.delegate as? AppDelegate
      {
          appDelegate.saveContext()
      }
  }
  
  //these delegate methods control the core data database
  func controllerWillChangeContent(_ controller: NSFetchedResultsController&lt;NSFetchRequestResult&gt;)
  {
      tableView.beginUpdates()
  }
  
  func controller(_ controller: NSFetchedResultsController&lt;NSFetchRequestResult&gt;, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?)
  {
      switch type
      {
      case .insert :
          if let newIndexPath = newIndexPath
          {
              tableView.insertRows(at: [newIndexPath], with: .fade)
          }
          
      case .delete:
          if let indexPath = indexPath
          {
              tableView.deleteRows(at: [indexPath], with: .fade)
          }
              
      case .update:
          if let indexPath = indexPath
          {
              tableView.reloadRows(at: [indexPath], with: .fade)
          }
          
      default:
          tableView.reloadData()
      }
      
      if let fetchedObjects = controller.fetchedObjects
      {
          self.coinsByCategory = fetchedObjects as! [CoinCategoryMO]
      }
  }
  
  func controllerDidChangeContent(_ controller: NSFetchedResultsController&lt;NSFetchRequestResult&gt;)
  {
      tableView.endUpdates()
      
      if self.isCurrentlyResortingCoinsIntoNewCategories != true
      {
          //we let the user know if the collection is empty
          if self.coinsByCategory.count == 0
          {
              self.messageUserIfCollectionEmpty()
          }
          else
          {
              self.activateCollectionEmptyLabel(newState: false)
          }
      }
  }
  </code></pre>
  <p>And then my CoinTableViewCell class is:</p>
  <pre class="lang-swift prettyprint-override"><code>//  Represents a cell of the coin buttons
  
  import UIKit
  
  class CoinTableViewCell: UITableViewCell {
  
  @IBOutlet var countryLabel: UILabel!
  @IBOutlet var valueAndDenominationLabel: UILabel!
  @IBOutlet var quantityLabel: UILabel!
  @IBOutlet var denominationOnlyLabel : UILabel!
  @IBOutlet var yearLabel : UILabel!
  
  override func awakeFromNib() {
      super.awakeFromNib()
      // Initialization code
  }
  
  func restoreAllLabelsToCell()
  {
      //this is a function that is called when this cell is being initialized in the cellForRowAt method in a tableview..
      //we want to make all the labels visible so that the previous usage of a reusable tableview cell does not affect this usage of the cell
      countryLabel.isHidden = false
      valueAndDenominationLabel.isHidden = false
      quantityLabel.isHidden = false
      denominationOnlyLabel.isHidden = false
      yearLabel.isHidden = false
  }
  
  func configureLabelsForCategoryType(theType : NSString)
  {
      //in this function, we remove all the extra labels
      //that contain information that does not relate to the general type of the category from the stack view
      //For example, the year label is removed when the category is a country, as a year does not determine what category a coin falls into.
      
      //we restore all the labels in this cell as we do not want the reusable cell's past usage
      //which may have lead to a label dissappearing to carry over into this new usage of the cell
      self.restoreAllLabelsToCell()
      
      switch theType
      {
      case CoinCategory.CategoryTypes.COUNTRY_VALUE_AND_CURRENCY.rawValue:
          //we do not need information about the coin's denomination (without its value) or the year
          denominationOnlyLabel.isHidden = true
          yearLabel.isHidden = true
          
          //after we remove the labels, we now make the first label bold and black
          valueAndDenominationLabel.font = UIFont.boldSystemFont(ofSize: self.valueAndDenominationLabel.font.pointSize)
          valueAndDenominationLabel.textColor = UIColor.black
          
      case CoinCategory.CategoryTypes.COUNTRY.rawValue:
          //we do not need the information about the coin's value and denominations nor year
          valueAndDenominationLabel.isHidden = true
          denominationOnlyLabel.isHidden = true
          yearLabel.isHidden = true
          
          //after we remove the labels, we make the first label bold and black
          countryLabel.font = UIFont.boldSystemFont(ofSize: self.countryLabel.font.pointSize)
          countryLabel.textColor = UIColor.black
          
      case CoinCategory.CategoryTypes.CURRENCY.rawValue:
          //we do not information about the coin's value &amp; denomination (together, that is), or year
          valueAndDenominationLabel.isHidden = true
          yearLabel.isHidden = true
          
          //after we remove the labels, we make the first label bold and black
          denominationOnlyLabel.font = UIFont.boldSystemFont(ofSize: self.denominationOnlyLabel.font.pointSize)
          denominationOnlyLabel.textColor = UIColor.black
          
      case CoinCategory.CategoryTypes.YEAR.rawValue:
          //we do not information about the coin's value, denomination, or country
          valueAndDenominationLabel.removeFromSuperview()
          denominationOnlyLabel.isHidden = true
          countryLabel.isHidden = true
          
          //after we remove the labels, we make the first label bold and black
          yearLabel.font = UIFont.boldSystemFont(ofSize: self.yearLabel.font.pointSize)
          yearLabel.textColor = UIColor.black
          
      default:
          //the string does not match any of the categories available
          //we do not remove any labels
          break
      }
  }
  
  }
  </code></pre>
  <p>My CoreData implementation is a collection of CoinCategoryMO objects which have the property &quot;Index&quot; (for their position in the uitableviewcontroller) and a CoinCategory object which holds objects of the Coin Class.</p>
  <p>I have been trying to debug this for several days now, and I have no idea what is going wrong. Could anyone please help?</p>
  <p>Many many thanks in advance, and have a great day!</p>