0

I am having trouble getting my comment cells to resize properly after a comment is posted.

When the Comment View first loads, everything looks good.

However, right after a comment is posted, the cells still retain their previous size and do not automatically resize.

This leads to new comment cells having the wrong dimensions (see pictures):

enter image description here

  1. The comment view when it first loads. Comment cells are the correct size.

  2. About to post a one line comment.

  3. Oh no: The new comment is posted but the old cell sizes are used instead.


The AutoLayout constraints for the Comment Cell are done in the storyboard.

The numberOfLines attribute for the comment text view is set to 0.


In the CommentViewController, estimatedRowHeight and automaticDimension are used:

    self.tableView.estimatedRowHeight = 77
            
    self.tableView.rowHeight = UITableView.automaticDimension

Edit: layoutIfNeeded() is used as well.

In the CommentTableViewCell class, prepareForReuse is called:

    override func prepareForReuse()
    {
        super.prepareForReuse()
    }

After the new comment is posted, pulling to refresh does not readjust the cell size. The only way to fix it is to click the back button and re-enter the Comment View.

How do I fix this problem?

Thank you for your time.


Edit: Added screenshot of my storyboard, heirarchy and constraints

enter image description here


Edit: Updated constraints, made comment label purple for visibility

Top of display name label to top of content view.

Top of comment label to bottom of display name label.

Bottom of comment label to bottom of content view.

enter image description here

Added purple to the background to make it more clear what's going on.

enter image description here

After the comments are reloaded, the comment cell constraints fail to adjust to the comments properly. How do I fix this?

Thank you for your time.


Final Edits: Removed unnecessary code and posted my solution in the comments

MMK
  • 93
  • 1
  • 9
  • OK this might not help, but I find there is some kind of timing glitch with auto layout such that you might have to call `layoutIfNeeded` during `cellForRow` just for the initially visible cells – matt Apr 09 '21 at 23:10
  • Hi matt, thanks for your suggestion. I used layoutIfNeeded in a couple more places but unfortunately it didn't work – MMK Apr 09 '21 at 23:38
  • @MichaelW - what is `self.unhideViewElements()` doing? You've implemented `prepareForReuse()` but it's not doing anything... is that what you expect? Do you have any other code in your cell class that may be manipulating the cell's elements? – DonMag Apr 10 '21 at 12:51

2 Answers2

1

Let's start a little simpler... instead of dealing with your API class, let's just use an array and get the table cells working.

So, here is a view controller...

  • A text field and "Send" button at the top (so we don't have to worry about the keyboard for the moment)
  • a table view constrained below the text field
  • a prototype cell

enter image description here

This is what we'll use for the cell class - no other code needed, as the constraints will handle the layout:

class CommentsCell: UITableViewCell {
    
    @IBOutlet var userLabel: UILabel!
    @IBOutlet var commenterLabel: UILabel!
    @IBOutlet var timeLabel: UILabel!
    @IBOutlet var commentLabel: UILabel!
    
}

Now, we'll create a simple struct for sample data:

struct CommentStruct {
    var userID: String = ""
    var commenterID: String = ""
    var commentTime: String = ""
    var commentText: String = ""
}

and here's what we'll use for the view controller class - I've left your general code flow in place, even though we're not doing much with it at the moment:

class CommentsViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
    
    @IBOutlet var commentTextField: UITextField!
    @IBOutlet var tableView: UITableView!
    
    var comments: [CommentStruct] = []
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        tableView.dataSource = self
        tableView.delegate = self
        tableView.separatorStyle = .none
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return comments.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let c = tableView.dequeueReusableCell(withIdentifier: "commentsCell", for: indexPath) as! CommentsCell
        
        // get the data
        let cs = comments[indexPath.row]
        
        // set text for labels in the cell
        c.userLabel.text = cs.userID
        c.commenterLabel.text = cs.commenterID
        c.timeLabel.text = cs.commentTime
        c.commentLabel.text = cs.commentText
        
        return c
    }
    
    // Actions performed when the Send button is pressed
    @IBAction func sendButton_TouchUpInside(_ sender: UIButton)
    {
        // Post the comment
        self.postComment()
    }
    
    // This function posts a comment
    func postComment()
    {
        // Store the comment text
        //  make sure text was entered
        guard let commentText = self.commentTextField.text,
              !commentText.isEmpty
        else {
            return
        }
        
        let cs = CommentStruct(userID: "User1",
                               commenterID: "Commenter1",
                               commentTime: "1d",
                               commentText: commentText)

        // insert this data to make it the new "top" comment
        comments.insert(cs, at: 0)
        
        // Hide keyboard
        self.view.endEditing(true)
        
        // Refresh the comment feed
        self.refresh()

    }
    
    // This method refreshes comments in the comment feed
    @objc func refresh()
    {
        // Retrieve comments from the database
        loadComments()
    }
    
    // This method loads the comments for a post
    func loadComments()
    {
        // here you would do your api call to get the comment data
        //  and re-populate your dataSource array
        
        // Reload the table view data
        self.tableView.reloadData()
            
        // Unhide the images, views, text fields and buttons???
        //self.unhideViewElements()
    }
    
}

When we run this, each comment entered will be inserted at the beginning of the dataSource array, so new comments appear at the top:

enter image description here

Give that a try... when you see the cells sizing properly, then integrate this structure with your API data storage.

Here's the source to the Storyboard so you can inspect it closely:

<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="17701" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="kqQ-83-E1D">
    <device id="retina4_7" orientation="portrait" appearance="light"/>
    <dependencies>
        <deployment identifier="iOS"/>
        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17703"/>
        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
        <capability name="System colors in document resources" minToolsVersion="11.0"/>
        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
    </dependencies>
    <scenes>
        <!--Comments View Controller-->
        <scene sceneID="vQJ-Cm-Kfe">
            <objects>
                <viewController id="kqQ-83-E1D" customClass="CommentsViewController" customModule="DrawingTutorial" customModuleProvider="target" sceneMemberID="viewController">
                    <view key="view" contentMode="scaleToFill" id="LfR-oh-wEI">
                        <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                        <subviews>
                            <tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="ReB-5o-kkD">
                                <rect key="frame" x="0.0" y="62" width="375" height="585"/>
                                <color key="backgroundColor" systemColor="systemBackgroundColor"/>
                                <prototypes>
                                    <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="commentsCell" rowHeight="101" id="NdF-XY-hNL" customClass="CommentsCell" customModule="DrawingTutorial" customModuleProvider="target">
                                        <rect key="frame" x="0.0" y="28" width="375" height="101"/>
                                        <autoresizingMask key="autoresizingMask"/>
                                        <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="NdF-XY-hNL" id="eLj-wx-ThB">
                                            <rect key="frame" x="0.0" y="0.0" width="375" height="101"/>
                                            <autoresizingMask key="autoresizingMask"/>
                                            <subviews>
                                                <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="person.circle" catalog="system" translatesAutoresizingMaskIntoConstraints="NO" id="WoV-fE-nM7">
                                                    <rect key="frame" x="16" y="11.5" width="40" height="39"/>
                                                    <color key="backgroundColor" red="0.92143100499999997" green="0.92145264149999995" blue="0.92144101860000005" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                                    <constraints>
                                                        <constraint firstAttribute="width" constant="40" id="Duy-4K-awo"/>
                                                        <constraint firstAttribute="width" secondItem="WoV-fE-nM7" secondAttribute="height" multiplier="1:1" id="s3a-Ri-9u1"/>
                                                    </constraints>
                                                </imageView>
                                                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="252" verticalHuggingPriority="252" text="Display Name" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="OMA-aF-mTL">
                                                    <rect key="frame" x="64" y="11" width="105" height="21"/>
                                                    <color key="backgroundColor" red="0.45009386540000001" green="0.98132258650000004" blue="0.4743030667" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                                    <fontDescription key="fontDescription" type="system" pointSize="17"/>
                                                    <nil key="textColor"/>
                                                    <nil key="highlightedColor"/>
                                                </label>
                                                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="lII-cx-Qfb">
                                                    <rect key="frame" x="64" y="36" width="295" height="54"/>
                                                    <color key="backgroundColor" red="0.45138680930000002" green="0.99309605359999997" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                                    <fontDescription key="fontDescription" type="system" pointSize="17"/>
                                                    <nil key="textColor"/>
                                                    <nil key="highlightedColor"/>
                                                </label>
                                                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="username" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="H3q-YO-OPD">
                                                    <rect key="frame" x="177" y="12.5" width="68" height="18"/>
                                                    <color key="backgroundColor" red="0.99953407049999998" green="0.98835557699999999" blue="0.47265523669999998" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                                    <fontDescription key="fontDescription" type="italicSystem" pointSize="15"/>
                                                    <nil key="textColor"/>
                                                    <nil key="highlightedColor"/>
                                                </label>
                                                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="time" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="el2-VE-HXg">
                                                    <rect key="frame" x="332.5" y="14.5" width="26.5" height="16"/>
                                                    <color key="backgroundColor" red="1" green="0.83234566450000003" blue="0.47320586440000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                                    <fontDescription key="fontDescription" type="system" pointSize="13"/>
                                                    <nil key="textColor"/>
                                                    <nil key="highlightedColor"/>
                                                </label>
                                            </subviews>
                                            <constraints>
                                                <constraint firstAttribute="bottomMargin" secondItem="lII-cx-Qfb" secondAttribute="bottom" id="77z-al-5f6"/>
                                                <constraint firstItem="WoV-fE-nM7" firstAttribute="top" secondItem="eLj-wx-ThB" secondAttribute="topMargin" id="EBj-fZ-KPe"/>
                                                <constraint firstItem="H3q-YO-OPD" firstAttribute="firstBaseline" secondItem="OMA-aF-mTL" secondAttribute="firstBaseline" id="He3-PN-s3Q"/>
                                                <constraint firstItem="lII-cx-Qfb" firstAttribute="leading" secondItem="WoV-fE-nM7" secondAttribute="trailing" constant="8" id="J5m-6F-mMX"/>
                                                <constraint firstItem="OMA-aF-mTL" firstAttribute="leading" secondItem="WoV-fE-nM7" secondAttribute="trailing" constant="8" id="OUd-aN-EpF"/>
                                                <constraint firstAttribute="trailingMargin" secondItem="lII-cx-Qfb" secondAttribute="trailing" id="RiF-g5-Kqn"/>
                                                <constraint firstItem="el2-VE-HXg" firstAttribute="firstBaseline" secondItem="H3q-YO-OPD" secondAttribute="firstBaseline" id="Ugz-tb-PDS"/>
                                                <constraint firstItem="WoV-fE-nM7" firstAttribute="leading" secondItem="eLj-wx-ThB" secondAttribute="leadingMargin" id="WVE-gb-IZ3"/>
                                                <constraint firstItem="H3q-YO-OPD" firstAttribute="leading" secondItem="OMA-aF-mTL" secondAttribute="trailing" constant="8" id="dpO-0E-5EY"/>
                                                <constraint firstAttribute="trailingMargin" secondItem="el2-VE-HXg" secondAttribute="trailing" id="lj2-pU-CEd"/>
                                                <constraint firstItem="lII-cx-Qfb" firstAttribute="top" secondItem="OMA-aF-mTL" secondAttribute="bottom" constant="4" id="tPn-WG-oP5"/>
                                                <constraint firstItem="OMA-aF-mTL" firstAttribute="top" secondItem="WoV-fE-nM7" secondAttribute="top" id="uLn-3z-aWP"/>
                                            </constraints>
                                        </tableViewCellContentView>
                                        <color key="backgroundColor" red="0.46202266219999999" green="0.83828371759999998" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                        <connections>
                                            <outlet property="commentLabel" destination="lII-cx-Qfb" id="e6K-Oh-9GU"/>
                                            <outlet property="commenterLabel" destination="H3q-YO-OPD" id="vrd-FX-yk9"/>
                                            <outlet property="timeLabel" destination="el2-VE-HXg" id="tjS-KJ-azE"/>
                                            <outlet property="userLabel" destination="OMA-aF-mTL" id="n2P-vl-MLU"/>
                                        </connections>
                                    </tableViewCell>
                                </prototypes>
                            </tableView>
                            <textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="9T9-jd-9hp">
                                <rect key="frame" x="8" y="8" width="291" height="34"/>
                                <fontDescription key="fontDescription" type="system" pointSize="14"/>
                                <textInputTraits key="textInputTraits"/>
                            </textField>
                            <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="npH-eO-rmF">
                                <rect key="frame" x="307" y="10" width="60" height="30"/>
                                <constraints>
                                    <constraint firstAttribute="width" constant="60" id="IqL-gO-uY2"/>
                                </constraints>
                                <state key="normal" title="Send"/>
                                <connections>
                                    <action selector="sendButton_TouchUpInside:" destination="kqQ-83-E1D" eventType="touchUpInside" id="cFF-G9-YEj"/>
                                </connections>
                            </button>
                        </subviews>
                        <viewLayoutGuide key="safeArea" id="7k7-br-c0C"/>
                        <color key="backgroundColor" systemColor="systemBackgroundColor"/>
                        <constraints>
                            <constraint firstItem="7k7-br-c0C" firstAttribute="trailing" secondItem="ReB-5o-kkD" secondAttribute="trailing" id="BpD-NC-I8a"/>
                            <constraint firstItem="7k7-br-c0C" firstAttribute="bottom" secondItem="ReB-5o-kkD" secondAttribute="bottom" constant="20" id="LZX-74-J2l"/>
                            <constraint firstItem="ReB-5o-kkD" firstAttribute="leading" secondItem="7k7-br-c0C" secondAttribute="leading" id="MrF-Ev-h4l"/>
                            <constraint firstItem="9T9-jd-9hp" firstAttribute="top" secondItem="7k7-br-c0C" secondAttribute="top" constant="8" id="WSE-8f-4vd"/>
                            <constraint firstItem="9T9-jd-9hp" firstAttribute="leading" secondItem="7k7-br-c0C" secondAttribute="leading" constant="8" id="dOf-rz-39l"/>
                            <constraint firstItem="npH-eO-rmF" firstAttribute="centerY" secondItem="9T9-jd-9hp" secondAttribute="centerY" id="iKO-JR-FeT"/>
                            <constraint firstItem="npH-eO-rmF" firstAttribute="leading" secondItem="9T9-jd-9hp" secondAttribute="trailing" constant="8" id="tFt-L3-xDM"/>
                            <constraint firstItem="7k7-br-c0C" firstAttribute="trailing" secondItem="npH-eO-rmF" secondAttribute="trailing" constant="8" id="xTY-Wm-wIc"/>
                            <constraint firstItem="ReB-5o-kkD" firstAttribute="top" secondItem="9T9-jd-9hp" secondAttribute="bottom" constant="20" id="yR9-xm-4XE"/>
                        </constraints>
                    </view>
                    <connections>
                        <outlet property="commentTextField" destination="9T9-jd-9hp" id="oiN-nx-d3k"/>
                        <outlet property="tableView" destination="ReB-5o-kkD" id="0h3-yw-Kt7"/>
                    </connections>
                </viewController>
                <placeholder placeholderIdentifier="IBFirstResponder" id="MMl-5e-W3I" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
            </objects>
            <point key="canvasLocation" x="368.80000000000001" y="795.6521739130435"/>
        </scene>
    </scenes>
    <resources>
        <image name="person.circle" catalog="system" width="128" height="121"/>
        <systemColor name="systemBackgroundColor">
            <color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
        </systemColor>
    </resources>
</document>
DonMag
  • 69,424
  • 5
  • 50
  • 86
0

Solution

After several days and reading dozens of threads, I managed to solve this problem by adding a height constraint to the comment text label and manually setting the height of the label based on the comment text length every time the cell is reused.

This not only solved the commenting layout issue, but another issue I had where scrolling through a comment section caused the cell heights to wildly fluctuate.


Here are the steps I took:

  1. Add a height constraint to the comment text label in addition to the other layout constraints of the cell.

    Link the height constraint as an outlet in the cell view.

    Add a numberOfLines attribute to the cell to keep track of the number of lines in the comment label.

  2. In the prepareForReuse() method of the Comment Cell class:

    Programatically set the value of the height constraint based on the comment text length.

    In my case, the height contraint is the numberOfLines multipled by 17 (the height of the comment label with one line of text).

    A function for calculating the amount of lines in a text label can be found here.

    I named my line calculating function: calculateMaxLines().

    // Set the number of lines for the comment label
    cell.commentLabel.numberOfLines =  cell.commentLabel.calculateMaxLines()
    
    // Set the height constraint to numberOfLines * 17
    cell.commentLabelHeightConstraint.constant = CGFloat(cell.commentLabel.numberOfLines) * 17)
    
    // Layout the cell
    cell.contentView.layoutIfNeeded()
    
  3. Add the same code in the tableView’s “cellForRowAt” function in the Comment View Controller.

    This helped fix several cell layout issues throughout my program.

    Hopefully people with similar issues will find this answer useful.

MMK
  • 93
  • 1
  • 9