2

I have a UITableView with dynamically sized prototype cells using auto layout to fit their contents. In one of my prototype cells I have a UITextView which I have configured to dynamically change its height and become scrollable once the TextViews height reaches a threshold(6 lines of text). This process also works in reverse, shrinking and becoming un-scrollable when below the threshold.

As of right now, when I select the UITextView to enter text the TableView scrolls up to make room for the keyboard as it should, however the last line of text is partially hidden by the keyboard.

My initial thought is to make the TableView scroll up with each new line of text entered so that the bottom edge of the TextView is always right above the top of the keyboard.

What is the preferred/correct method of doing this? I've been reading a lot of contradictions about resizing the TextView along with these words (contentInset, scrollRectToVisible)

I have found a similar question which illustrates the problem but the answer that came out of that was it was a bug in iOS 7. I'm working with iOS 10.3

TableView.swift

class NewRecipe: UITableViewController, UIPickerViewDataSource, UIPickerViewDelegate, UITextFieldDelegate, ExpandingCellDelegate {


    @IBOutlet weak var uiTable: UITableView!

    override func viewDidLoad() {
        super.viewDidLoad()
        self.tableView.separatorStyle = .none
        self.tableView.allowsSelection = false
    }

    func updateCellHeight(_ indexPath: IndexPath, comment: String) {
        UIView.setAnimationsEnabled(false)
        self.uiTable.beginUpdates()
        self.uiTable.endUpdates()
        UIView.setAnimationsEnabled(true)
    }

    // To enable self-sizing table view cells
    override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        //print("setting automatic dim 1")
        //return UITableViewAutomaticDimension

        if (indexPath.row == 4) {
            //setting cell size for the image
            let screenSize = UIScreen.main.bounds
            let screenHeight = screenSize.height
            return screenHeight/2
        } else {
            // set each row to be self sizing!
            return UITableViewAutomaticDimension
        }

    }

    // To enable self-sizing table view cells
    override func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
        //print("setting automatic dim 2")
        return UITableViewAutomaticDimension
    }

    // determine number of rows
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        //print("returning number of rows = 7")
        return  9
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

        switch (indexPath.row) {
        case 4:
           let cell = tableView.dequeueReusableCell(withIdentifier: "Image Cell", for: indexPath) as! ImageTableViewCell
            cell.foodImage.image = UIImage(named: "defaultPhoto")
            return cell

        case 0, 1, 2, 3:
            let cell = tableView.dequeueReusableCell(withIdentifier: "Label-Text Field Split Cell", for: indexPath) as! LabelTextFieldSplitCell
            if (indexPath.row == 1) {
                cell.label.text = "Recipe Title "
            } else if (indexPath.row == 2) {
                cell.label.text = "Quantity / Servings "
            } else if (indexPath.row == 3) {
                cell.label.text = "Prep Time "
            } else {
                cell.label.text = "Cook Time "
            }
            return cell

        case 5:
            let cell = tableView.dequeueReusableCell(withIdentifier: "Stack-Label Split Cell", for: indexPath) as! StackLabelSplitCell
            return cell

        case 6:
            let cell = tableView.dequeueReusableCell(withIdentifier: "Buttons Cell", for: indexPath) as! ButtonsCell
            return cell

        case 7:
            let cell = tableView.dequeueReusableCell(withIdentifier: "Picker-Text View Split Cell", for: indexPath) as! PickerTextViewCell

            cell.picker.delegate = self
            cell.cellIndexPath = indexPath
            cell.delegate = self as ExpandingCellDelegate
            return cell

        case 8:
            let cell = tableView.dequeueReusableCell(withIdentifier: "Text View Cell", for: indexPath) as! TextViewCell

            cell.cellIndexPath = indexPath
            cell.delegate = self as ExpandingCellDelegate
            return cell

        default:
            let cell = tableView.dequeueReusableCell(withIdentifier: "Single Text Field Cell", for: indexPath) as! SingleTextFieldCell
            return cell
        }
    }

    //MARK: UIPickerViewDelegate

    // set number of columns
    func numberOfComponents(in pickerView: UIPickerView) -> Int {
        //print("measurement count is " , measurements.count)
        return measurements.count
    }

    func pickerView(_ pickerView: UIPickerView, widthForComponent component: Int) -> CGFloat {
        return 35.0
    }


    // set number of rows for each column
    func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
        return (measurements[component] as [AnyObject]).count + 1
    }


    func pickerView(_ pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusing view: UIView?) -> UIView {

        // set the first item in picker to -- then set all other items to the strings in the array @ measurements[component][row-1]
        let string = row == 0 ? "--" : "\((measurements[component] as [AnyObject])[row - 1])"

        // styling the picker labels
        let pickerLabel = UILabel()
        pickerLabel.text = string

        pickerLabel.textColor = UIColor.black

        pickerLabel.font = UIFont(name: "Helvetica Neue", size: 17)
        pickerLabel.textAlignment = .left

        return pickerLabel

    }

    // retreiving the values out of the picker
    func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {

    /*
        switch (component) {
        case (0):
            let wholeNum = row == 0 ? 0 : measurements[component][row-1] as? Int
        case (1):
            let fraction = row == 0 ? "" : measurements[component][row-1] as? String
        case (2):
            let unit = row == 0 ? "" : measurements[component][row-1] as? String
        default: ()

        } 
    */

    }

    //MARK: UITextFieldDelegate

    func textFieldDidBeginEditing(_ textField: UITextField) {
        //This code disables the Save button while the user is editing the text field.
        //saveButton.isEnabled = false
        //self.view.bounds.origin.y = 60
    }

    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        // Hide the keyboard when the user presses Done.
        textField.resignFirstResponder()
        return true
    }

    func textFieldDidEndEditing(_ textField: UITextField) {
        // The first line calls updateSaveButtonState() to check if the text field has text in it, which enables the Save button if it does. The second line sets the title of the scene in the nav bar to that text.
        //updateSaveButtonState()
        //navigationItem.title = textField.text
    }

//    func textViewDidBeginEditing(_ textView: UITextView)
//    {
//        if UIScreen.main.bounds.height < 568 {
//            UIView.animate(withDuration: 0.75, animations: {
//                self.view.bounds.origin.y = 60
//            })
//        }
//    }
//    
//    func textViewDidEndEditing(_ textView: UITextView)
//    {
//        if UIScreen.main.bounds.height < 568 {
//            UIView.animate(withDuration: 0.75, animations: {
//                self.view.bounds.origin.y = 0
//            })
//        }
//    }
}

TextViewCell.swift

    class TextViewCell: UITableViewCell, UITextViewDelegate  {

    var delegate: ExpandingCellDelegate!
    var cellIndexPath: IndexPath!
    @IBOutlet weak var textHeightConstraint: NSLayoutConstraint!

    @IBOutlet weak var textViewBottomConstraint: NSLayoutConstraint!

    override func awakeFromNib() {
        super.awakeFromNib()
        self.textView.delegate = self
    }

    @IBOutlet weak var label: UILabel! {
        didSet {
            label.font = UIFont.systemFont(ofSize: 15)
            label.textColor = UIColor.lightGray
        }
    }

    @IBOutlet weak var textView: UITextView! {
        didSet {
            textView.layer.borderWidth = 2
            textView.layer.cornerRadius = 5
            textView.layer.borderColor = UIColor(red: 0.9, green: 0.9, blue: 0.9, alpha: 1.0).cgColor
        }
    }

    func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
        //print("Content Height \(self.textView.contentSize.height) ")
        if(self.textView.contentSize.height < self.textHeightConstraint.constant) {
            self.textView.isScrollEnabled = false
        } else {
            self.textView.isScrollEnabled = true
        }

        self.delegate.updateCellHeight(self.cellIndexPath, comment: textView.text)

        return true
    } 
}

0 Answers0