ios - 如何在不滚动 textView 的情况下使 UITableViewCell 的高度扩展以适应 UITextView 的内容并包装文本? ( swift 5)

标签 ios swift xcode uitableview uitextview

我有一个使用自定义单元格的 UITableViewController。在自定义单元格中是一个 UITextView 和一个 UIButton。当用户键入时,我需要选择的 tableView 单元格适合用户输入的多行文本。 iPhone 预装的提醒应用程序可以完美地展示这一点。目前在我的应用程序中,tableView 单元格的高度保持不变,并且 textView 在必要时启用滚动以容纳多行文本。
我尝试在这篇文章(Dynamically change cell's height while typing text, and reload the containing tableview for resize)中实现解决方案,这似乎提出了同样的问题,但是,这篇文章已经有将近 3 年的历史了,即使答案仍然有效,也没有足够的示例代码让我了解如何实现答案。我尽了最大的努力,但没有运气,并清除了我当前的任何先前尝试的代码,因此就这个所需的功能而言,它目前是一个干净的石板。看到包含 Swift 5 中的示例代码的答案将是惊人的。
如果有用的话,这是自定义单元格类:

class newNoteTableViewCell: UITableViewCell {

    @IBOutlet weak var lyricsField: UITextView!
    @IBOutlet weak var recordButton: UIButton!
    
    override func awakeFromNib() {
        super.awakeFromNib()
        // Initialization code
    }

    override func setSelected(_ selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)
    }
    
    @IBAction func recordButtonPressed(_ sender: UIButton) {
    }
}
还有我的 cellForRowAt,这是目前除 numberOfRowsInSection 之外唯一实现的 TableView 委托(delegate)方法:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "lyricsCell", for: indexPath) as! newNoteTableViewCell
        
        cell.lyricsField.delegate = self
        
        cell.lyricsField.tag = indexPath.row
        
        if let safeLyrics = lyrics {
            if indexPath.row < safeLyrics.count {
                cell.lyricsField.text = safeLyrics[indexPath.row].text
            } else {
                cell.lyricsField.text = ""
            }
        }
        
        return cell
    }

最佳答案

我们可以通过向您的单元格添加一个“回调”闭包来做到这一点。
当 TextView 被编辑时,单元类将“回调”到 Controller ,我们可以告诉表格 View 重新计算行高(以及保存编辑的文本)。
这是一个带有约束的简单单元格布局。确保禁用 TextView 上的滚动:
enter image description here
请注意,在设计时单元格的高度并不重要......我们的约束将允许自动布局来处理它。
结果:
enter image description here
这是示例代码:

class ExampleCell: UITableViewCell, UITextViewDelegate {
    
    @IBOutlet var recordButton: UIButton!
    @IBOutlet var lyricsField: UITextView!
    
    var callback: ((String) -> ())?
    
    override func didMoveToSuperview() {
        super.didMoveToSuperview()
        // make sure scroll is disabled
        lyricsField.isScrollEnabled = false
        // make sure delegate is set
        lyricsField.delegate = self
        // if these are set in Storyboard this func is not needed
    }
    func textViewDidChange(_ textView: UITextView) {
        let str = textView.text ?? ""
        // tell the controller
        callback?(str)
    }

}

class ExampleTableViewController: UITableViewController {

    var myData: [String] = []
    
    override func viewDidLoad() {
        super.viewDidLoad()

        // start with 20 sample strings for our data
        // fill data array with 30 strings
        myData = (1...30).map { "This is row \($0)" }

        // give the second row some longer sample text
        myData[1] = "Some sample text so we see that the text view height will be automatically handled by auto-layout."
        
    }

    // MARK: - Table view data source

    override func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return myData.count
    }

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

        cell.lyricsField.text = myData[indexPath.row]
        
        // set the closure
        weak var tv = tableView
        cell.callback = { [weak self] str in
            guard let self = self, let tv = tv else { return }
            print("called back", str)
            // update our data with the edited string
            self.myData[indexPath.row] = str
            // we don't need to do anything else here
            // this will force the table to recalculate row heights
            tv.performBatchUpdates(nil)
        }

        return cell
    }

}
以下是 Storyboard 源代码供引用:
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="17156" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="UxY-Y6-LYS">
    <device id="retina3_5" orientation="portrait" appearance="light"/>
    <dependencies>
        <deployment identifier="iOS"/>
        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17125"/>
        <capability name="System colors in document resources" minToolsVersion="11.0"/>
        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
    </dependencies>
    <scenes>
        <!--Example Table View Controller-->
        <scene sceneID="HKA-46-9zH">
            <objects>
                <tableViewController id="UxY-Y6-LYS" customClass="ExampleTableViewController" customModule="Temp" customModuleProvider="target" sceneMemberID="viewController">
                    <tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" id="ZaD-4v-hEm">
                        <rect key="frame" x="0.0" y="0.0" width="320" height="480"/>
                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                        <color key="backgroundColor" systemColor="systemBackgroundColor"/>
                        <prototypes>
                            <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="exampleCell" rowHeight="141" id="1bW-gv-rnI" customClass="ExampleCell" customModule="Temp" customModuleProvider="target">
                                <rect key="frame" x="0.0" y="28" width="320" height="141"/>
                                <autoresizingMask key="autoresizingMask"/>
                                <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="1bW-gv-rnI" id="H65-Gy-hPe">
                                    <rect key="frame" x="0.0" y="0.0" width="320" height="141"/>
                                    <autoresizingMask key="autoresizingMask"/>
                                    <subviews>
                                        <button opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="251" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="5rZ-Jh-Wyd">
                                            <rect key="frame" x="96" y="11" width="128" height="30"/>
                                            <color key="backgroundColor" red="0.85215073819999998" green="0.88016217949999997" blue="0.94548028709999998" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                            <state key="normal" title="Record"/>
                                        </button>
                                        <textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" scrollEnabled="NO" keyboardDismissMode="onDrag" text="The Text View" textAlignment="natural" translatesAutoresizingMaskIntoConstraints="NO" id="ddd-0L-1pM">
                                            <rect key="frame" x="24" y="49" width="272" height="81"/>
                                            <color key="backgroundColor" red="0.99953407049999998" green="0.98835557699999999" blue="0.47265523669999998" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                            <color key="textColor" systemColor="labelColor"/>
                                            <fontDescription key="fontDescription" type="system" pointSize="14"/>
                                            <textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
                                        </textView>
                                    </subviews>
                                    <constraints>
                                        <constraint firstItem="ddd-0L-1pM" firstAttribute="top" secondItem="5rZ-Jh-Wyd" secondAttribute="bottom" constant="8" id="MHf-Yq-xQq"/>
                                        <constraint firstAttribute="trailingMargin" secondItem="ddd-0L-1pM" secondAttribute="trailing" constant="8" id="QMH-AZ-j7k"/>
                                        <constraint firstItem="5rZ-Jh-Wyd" firstAttribute="leading" secondItem="H65-Gy-hPe" secondAttribute="leadingMargin" constant="80" id="TYR-1f-iit"/>
                                        <constraint firstAttribute="trailingMargin" secondItem="5rZ-Jh-Wyd" secondAttribute="trailing" constant="80" id="Yf1-2g-dlf"/>
                                        <constraint firstItem="5rZ-Jh-Wyd" firstAttribute="top" secondItem="H65-Gy-hPe" secondAttribute="topMargin" id="gaW-td-egM"/>
                                        <constraint firstItem="ddd-0L-1pM" firstAttribute="bottom" secondItem="H65-Gy-hPe" secondAttribute="bottomMargin" id="lKj-ML-H4Q"/>
                                        <constraint firstItem="ddd-0L-1pM" firstAttribute="leading" secondItem="H65-Gy-hPe" secondAttribute="leadingMargin" constant="8" id="uxs-rD-Pdj"/>
                                    </constraints>
                                </tableViewCellContentView>
                                <connections>
                                    <outlet property="lyricsField" destination="ddd-0L-1pM" id="9Nz-Ru-psp"/>
                                    <outlet property="recordButton" destination="5rZ-Jh-Wyd" id="qNA-Up-zLK"/>
                                </connections>
                            </tableViewCell>
                        </prototypes>
                        <connections>
                            <outlet property="dataSource" destination="UxY-Y6-LYS" id="cmh-tD-hLg"/>
                            <outlet property="delegate" destination="UxY-Y6-LYS" id="xk4-oC-WNJ"/>
                        </connections>
                    </tableView>
                </tableViewController>
                <placeholder placeholderIdentifier="IBFirstResponder" id="7mc-zX-bKw" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
            </objects>
            <point key="canvasLocation" x="330" y="127.5"/>
        </scene>
    </scenes>
    <resources>
        <systemColor name="labelColor">
            <color white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
        </systemColor>
        <systemColor name="systemBackgroundColor">
            <color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
        </systemColor>
    </resources>
</document>

编辑 回应评论....
您在牢房中的约束都是错误的...
以下是它们从原始 xib 中的外观:
enter image description here
以下是它们的外观:
enter image description here
重要提示:您的堆栈 View 设置必须是:
enter image description here

关于ios - 如何在不滚动 textView 的情况下使 UITableViewCell 的高度扩展以适应 UITextView 的内容并包装文本? ( swift 5),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64143530/

相关文章:

ios - 如何将纬度和经度传递给另一个函数 - swift location

objective-c - KVO 和更改 UI 值更改

c# - Xamarin.Forms SetHasNavigationBar false 导致在 PushAsync 上跳转

ios - 从 Xcode 11.1 升级到 Xcode 11.2 后,应用程序因 _UITextLayoutView 崩溃

ios - 如何使用 swift 在 iOS 中点击谷歌地图来放置标记

ios - 在设备上测试定位服务

ios - React-native - 由于双重转换导致构建失败

ios - 如何通过菜单按钮打开新的 View Controller ?

iphone - Facebook 给出安全警告 : Please treat the URL above as you would your password and do not share it with anyone

ios - iCarousel 不在 iPhone 6+ 上呈现 4 个项目的第 3 个 View