How to force a cell row to update the height

Viewed 628

I have a UITableView that I am working on. One of the rows allows a user to upload a photo. Once the photo is uploaded, it changes the description alpha property to 0 (makes invisible), changes the image height to 100, and sets the UIImage.

I verified that the image height is being updated, but the cell height is not being updated even though it is set to automatic. Is there a way to let the View know it needs to update the cell height?

Before user uploads an image:




After user uploads an image:




TableView Cell Settings TableView Cell Settings

//
//  AddAssetTableViewController.swift
//  ItemizePro
//
//  Created by Tyler Wasick on 5/27/20.
//  Copyright © 2020 Tyler Wasick. All rights reserved.
//

import UIKit

class AddAssetTableViewController: UITableViewController {

    // TODO: Add image IBOutlet

    @IBOutlet weak var nameTextField: UITextField!

    @IBOutlet weak var descriptionTextField: UITextField!

    @IBOutlet weak var datePurchasedTextField: UITextField!

    @IBOutlet weak var manufactureTextField: UITextField!

    @IBOutlet weak var modelTextField: UITextField!

    @IBOutlet weak var serialNumberTextField: UITextField!

    @IBOutlet weak var costTextField: UITextField!

    @IBOutlet weak var roomTextField: UITextField!

    @IBOutlet weak var notesTextView: UITextView!

    @IBOutlet weak var imageLabel: UILabel!

    @IBOutlet weak var receiptLabel: UILabel!

    @IBOutlet weak var addUIButton: UIButton!

    @IBOutlet weak var assetImageUIImage: UIImageView!

    @IBOutlet weak var receiptImageUIImage: UIImageView!

    // TableView Rows
    @IBOutlet weak var imageTableViewCell: UITableViewCell!


    // Set variables
    var newAsset = Asset()
    let datePicker = UIDatePicker()
    var dateAdded = Date()
    var cost = 0
    var imagePickerField = ""

    // Setup number formater
    lazy var numberFormatter: NumberFormatter = {
        // Setup Currency Formatter
        let currencyFormatter = NumberFormatter()
        currencyFormatter.usesGroupingSeparator = true
        currencyFormatter.numberStyle = .currency

        // localize to your grouping and decimal separator
        currencyFormatter.locale = Locale.current
        return currencyFormatter
    }()

    override func viewDidLoad() {
        super.viewDidLoad()

        // Uncomment the following line to preserve selection between presentations
        // self.clearsSelectionOnViewWillAppear = false

        // Uncomment the following line to display an Edit button in the navigation bar for this view controller.
        // self.navigationItem.rightBarButtonItem = self.editButtonItem

        // Setup formatting


        // Setup Date Picker
        createDatePicker()

        // Use input data for cost field and update the cost data and formatting automatically
        costTextField.delegate = self
        costTextField.placeholder = updateCostField()

    }

    // MARK: - Table view data source

    /*
     override func numberOfSections(in tableView: UITableView) -> Int {
     // #warning Incomplete implementation, return the number of sections
     return 0
     }

     override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
     // #warning Incomplete implementation, return the number of rows
     return 0
     }


     override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
     let cell = tableView.dequeueReusableCell(withIdentifier: "imageCell", for: indexPath)

     // Configure the cell...

     return cell
     }
     */

    /*
     // Override to support conditional editing of the table view.
     override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
     // Return false if you do not want the specified item to be editable.
     return true
     }
     */

    /*
     // Override to support editing the table view.
     override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
     if editingStyle == .delete {
     // Delete the row from the data source
     tableView.deleteRows(at: [indexPath], with: .fade)
     } else if editingStyle == .insert {
     // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
     }
     }
     */

    /*
     // Override to support rearranging the table view.
     override func tableView(_ tableView: UITableView, moveRowAt fromIndexPath: IndexPath, to: IndexPath) {

     }
     */

    /*
     // Override to support conditional rearranging of the table view.
     override func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool {
     // Return false if you do not want the item to be re-orderable.
     return true
     }
     */

    // When user taps on the image row, initiate "Show Action Sheet"
    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        if indexPath.row == 0 {
            showActionSheet(title: "Upload an image of your item.", fieldName: Constants.InventoryVC.imageField)
        }

        if indexPath.row == 10 {
            showActionSheet(title: "Upload a receipt for your item.", fieldName: Constants.InventoryVC.receiptField)
        }
    }

    // MARK: - Navigation

    // In a storyboard-based application, you will often want to do a little preparation before navigation
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {

        //if let detinationVC = segue.description as? RoomListViewController

        // Get the new view controller using segue.destination.
        // Pass the selected object to the new view controller.
        if let target = segue.destination as? RoomListViewController
        {
            target.selectionDelegate = self
        }

    }


    // MARK: - Functions
    // Date picker
    func createDatePicker() {

        // Setup the UIToolbar
        let toolbar = UIToolbar()
        toolbar.sizeToFit()

        // Setup "Done" button
        let doneButton = UIBarButtonItem(barButtonSystemItem: .done, target: nil, action: #selector(doneTapped))
        toolbar.setItems([doneButton], animated: true)

        // Format date text field
        datePurchasedTextField.inputAccessoryView = toolbar
        datePurchasedTextField.inputView = datePicker
        datePicker.datePickerMode = .date
    }

    // Obj-C Date Picker Action
    @objc func doneTapped() {

        // Format date field
        let formatter = DateFormatter()
        formatter.dateStyle = .medium
        formatter.timeStyle = .none
        let dateString = formatter.string(from: datePicker.date)
        dateAdded = formatter.date(from: dateString)!

        // Set the date text field
        datePurchasedTextField.text = dateString

        // Close date picker
        self.view.endEditing(true)

    }

    func updateCostField() -> String {

        // Format number into currency data
        let num = cost
        let decimalNum = Double(num) / 100
        let numString = numberFormatter.string(from: NSNumber(value: decimalNum))

        // Return formatted number
        return numString!
    }

    func showActionSheet(title:String, fieldName:String) {

        // Create the action sheet
        let actionSheet = UIAlertController(title: title, message: "Select a source to upload an image", preferredStyle: .actionSheet)

        // Create camera action if available
        if UIImagePickerController.isSourceTypeAvailable(.camera) {

            let cameraAction = UIAlertAction(title: "Camera", style: .default) { (action) in

                // Set the camera action
                self.showImagePicker(type: .camera)
            }

            // Add action to the action sheet
            actionSheet.addAction(cameraAction)
        }

        // Create photo library action
        if UIImagePickerController.isSourceTypeAvailable(.photoLibrary) {

            let photoAction = UIAlertAction(title: "Select From Photo Library", style: .default) { (action) in

                // Set photo library action
                self.showImagePicker(type: .photoLibrary)

            }

            // Add action to the action sheet
            actionSheet.addAction(photoAction)
        }

        // Create cancel actions
        let cancelAction = UIAlertAction(title: "Cancel", style: .cancel)

        // Add the cancel action
        actionSheet.addAction(cancelAction)

        // Set the image field that is being manipulated to the "imagePickerField
        imagePickerField = fieldName

        // Present action sheet
        present(actionSheet, animated: true, completion: nil)

    }

    func showImagePicker(type:UIImagePickerController.SourceType) {

        // Create the image picker
        let imagePicker = UIImagePickerController()
        imagePicker.sourceType = type

        // Setup delegate
        imagePicker.delegate = self

        // Present it
        present(imagePicker, animated: true, completion: nil)
    }

    func setupFormatting() {

        // Set image previews to transparent
        assetImageUIImage.alpha = 0
        receiptImageUIImage.alpha = 0


    }

    // MARK: - IBAction outlets

    @IBAction func addButtonTapped(_ sender: UIButton) {

        // Name text field
        if nameTextField.text?.trimmingCharacters(in: .whitespacesAndNewlines) != nil {
            newAsset.name = nameTextField.text!
        }
        else if nameTextField.text?.trimmingCharacters(in: .whitespacesAndNewlines) == nil {
            newAsset.name = ""
        }

        // Description text field
        if descriptionTextField.text?.trimmingCharacters(in: .whitespacesAndNewlines) != nil {
            newAsset.description = descriptionTextField.text!
        }
        else if descriptionTextField.text?.trimmingCharacters(in: .whitespacesAndNewlines) == nil {
            newAsset.description = ""
        }

        // Date text field
        if datePurchasedTextField.text?.trimmingCharacters(in: .whitespacesAndNewlines) != nil {
            newAsset.datePurchased = dateAdded
        }
        else if datePurchasedTextField.text?.trimmingCharacters(in: .whitespacesAndNewlines) == nil {
            newAsset.datePurchased = Date()
        }

        // Manufacture text field
        if manufactureTextField.text?.trimmingCharacters(in: .whitespacesAndNewlines) != nil {
            newAsset.manufacture = manufactureTextField.text!
        }
        else if manufactureTextField.text?.trimmingCharacters(in: .whitespacesAndNewlines) == nil {
            newAsset.manufacture = ""
        }

        // Model text field
        if modelTextField.text?.trimmingCharacters(in: .whitespacesAndNewlines) != nil {
            newAsset.model = modelTextField.text!
        }
        else if modelTextField.text?.trimmingCharacters(in: .whitespacesAndNewlines) == nil {
            newAsset.model = ""
        }

        // Serial number text field
        if serialNumberTextField.text?.trimmingCharacters(in: .whitespacesAndNewlines) != nil {
            newAsset.serialnumber = serialNumberTextField.text!
        }
        else if serialNumberTextField.text?.trimmingCharacters(in: .whitespacesAndNewlines) == nil {
            newAsset.serialnumber = ""
        }

        // Cost text field
        if costTextField.text!.trimmingCharacters(in: .whitespacesAndNewlines) != "" {
            let cost = costTextField.text!
            newAsset.cost = cost
        }
        else if costTextField.text?.trimmingCharacters(in: .whitespacesAndNewlines) == "" {
            newAsset.cost = "$0.00"
        }

        // Room text field
        if roomTextField.text?.trimmingCharacters(in: .whitespacesAndNewlines) != nil {
            newAsset.room = roomTextField.text!
        }
        else if roomTextField.text?.trimmingCharacters(in: .whitespacesAndNewlines) == nil {
            newAsset.room = ""
        }

        // Notes text field
        if notesTextView.text?.trimmingCharacters(in: .whitespacesAndNewlines) != nil {
            newAsset.notes = notesTextView.text!
        }
        else if notesTextView.text?.trimmingCharacters(in: .whitespacesAndNewlines) == nil {
            newAsset.notes = ""
        }

        // TODO: - Receipt

        // TODO: - Image

        // Add to firebase
        let result = FirebaseDB.addAssets(asset: newAsset)

        print(result)
    }

}



// MARK: - Extensions
// Table delegate extension
extension AddAssetTableViewController : RoomDelegate {
    func tappedOnRoom(room: String) {
        roomTextField.text = room
    }
}

// Cost text field delegate
extension AddAssetTableViewController : UITextFieldDelegate {
    func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {

        // Check if num is <= 1 billion
        if cost < 1000000000 {

            // Add new digit to the cost var
            if let num = Int(string) {
                cost = cost * 10 + num
                print(cost)
                costTextField.text = updateCostField()
            }
        }

        // If the user removes a digit, remove it from the cost var
        if string == "" {
            cost = cost / 10
            print(cost)
            costTextField.text = updateCostField()
        }

        // Return false for return (return is unused)
        return false
    }
}

// Image picker extension
extension AddAssetTableViewController : UIImagePickerControllerDelegate & UINavigationControllerDelegate {


    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {

        // TODO: If image was selected successfully, set the image to "selectImage" object
        if let selectImage = info[UIImagePickerController.InfoKey.originalImage] as? UIImage {

            // If image selected is the asset image, set the to the asset.image field
            if imagePickerField == Constants.InventoryVC.imageField {
                newAsset.image = selectImage
                assetImageUIImage.image = selectImage
                assetImageUIImage.frame.size.height = 180
                imageLabel.alpha = 0

            }

            // If image selected is the receipt image, set the to the asset.receipt field
            else if imagePickerField == Constants.InventoryVC.receiptField {
                newAsset.receipt = selectImage
                receiptLabel.text = "Image Uploaded"
            }

        }

        // Dismiss the picker
        picker.dismiss(animated: true, completion: nil)
    }
}

1 Answers

You could do this by reloading the UITableView in the didFinishPickingMediaWithInfo method of UIImagePickerControllerDelegate and provide the custom height for the image view row if the image is not empty.

class AddAssetTableViewController: UITableViewController {

    @IBOutlet weak var assetImageUIImage: UIImageView!

    override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        if indexPath.row == 0 && assetImageUIImage.image != nil {
            return 100 // Larger height
        }
        return 44 // Usual height
    }
}
extension AddAssetTableViewController : UIImagePickerControllerDelegate & UINavigationControllerDelegate {

    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
        // ...
        tableView.reloadRows(at: [IndexPath(row: 0, section: 0)], with: .automatic)
    }
}

Note: Better approach would be to add height, top and bottom constraints to the UIImageView in the cell and the auto-layout will handle the rest.

Related