当单元格的元素框架发生变化时,更新tableView

时间:2016-07-22 10:26:20

标签: ios uitableview swift2 uitextview

我尝试添加see more功能,例如this。在我UITextView tableView's cell内的 @IBDesignable class ReadMoreTextView: UITextView { override init(frame: CGRect, textContainer: NSTextContainer?) { super.init(frame: frame, textContainer: textContainer) scrollEnabled = false editable = false } convenience init(frame: CGRect) { self.init(frame: frame, textContainer: nil) } convenience init() { self.init(frame: CGRectZero, textContainer: nil) } required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) scrollEnabled = false editable = false } convenience init(maximumNumberOfLines: Int, trimText: NSString?, shouldTrim: Bool) { self.init() self.maximumNumberOfLines = maximumNumberOfLines self.trimText = trimText self.shouldTrim = shouldTrim } convenience init(maximumNumberOfLines: Int, attributedTrimText: NSAttributedString?, shouldTrim: Bool) { self.init() self.maximumNumberOfLines = maximumNumberOfLines self.attributedTrimText = attributedTrimText self.shouldTrim = shouldTrim } @IBInspectable var maximumNumberOfLines: Int = 0 { didSet { setNeedsLayout() } } @IBInspectable var trimText: NSString? { didSet { setNeedsLayout() } } var attributedTrimText: NSAttributedString? { didSet { setNeedsLayout() } } @IBInspectable var shouldTrim: Bool = false { didSet { setNeedsLayout() } } var trimTextRangePadding: UIEdgeInsets = UIEdgeInsetsZero var appendTrimTextPrefix: Bool = true var trimTextPrefix: String = "..." private var originalText: String! override var text: String! { didSet { originalText = text originalAttributedText = nil if needsTrim() { updateText() } } } private var originalAttributedText: NSAttributedString! override var attributedText: NSAttributedString! { didSet { originalAttributedText = attributedText originalText = nil if needsTrim() { updateText() } } } override func layoutSubviews() { super.layoutSubviews() needsTrim() ? updateText() : resetText() } func needsTrim() -> Bool { return shouldTrim && _trimText != nil } func updateText() { textContainer.maximumNumberOfLines = maximumNumberOfLines textContainer.size = CGSizeMake(bounds.size.width, CGFloat.max) let range = rangeToReplaceWithTrimText() if range.location != NSNotFound { let prefix = appendTrimTextPrefix ? trimTextPrefix : "" if let text = trimText?.mutableCopy() as? NSMutableString { text.insertString("\(prefix) ", atIndex: 0) textStorage.replaceCharactersInRange(range, withString: text as String) } else if let text = attributedTrimText?.mutableCopy() as? NSMutableAttributedString { text.insertAttributedString(NSAttributedString(string: "\(prefix) "), atIndex: 0) textStorage.replaceCharactersInRange(range, withAttributedString: text) } } invalidateIntrinsicContentSize() } func resetText() { textContainer.maximumNumberOfLines = 0 if originalText != nil { textStorage.replaceCharactersInRange(NSMakeRange(0, countElements(text!)), withString: originalText) print("Trim Pressed resetText") } else if originalAttributedText != nil { textStorage.replaceCharactersInRange(NSMakeRange(0, countElements(text!)), withAttributedString: originalAttributedText) } invalidateIntrinsicContentSize() // maybe this is what we're looking for } override func intrinsicContentSize() -> CGSize { textContainer.size = CGSizeMake(bounds.size.width, CGFloat.max) var intrinsicContentSize = layoutManager.boundingRectForGlyphRange(layoutManager.glyphRangeForTextContainer(textContainer), inTextContainer: textContainer).size intrinsicContentSize.width = UIViewNoIntrinsicMetric intrinsicContentSize.height += (textContainerInset.top + textContainerInset.bottom) return intrinsicContentSize } override func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? { if needsTrim() && pointInTrimTextRange(point) { shouldTrim = false maximumNumberOfLines = 0 } return super.hitTest(point, withEvent: event) } //MARK: Private methods private var _trimText: NSString? { get { return trimText ?? attributedTrimText?.string } } private var _trimTextPrefixLength: Int { get { return appendTrimTextPrefix ? countElements(trimTextPrefix) + 1 : 1 } } private var _originalTextLength: Int { get { if originalText != nil { return countElements(originalText!) } else if originalAttributedText != nil { return originalAttributedText!.length } return 0 } } private func rangeToReplaceWithTrimText() -> NSRange { let emptyRange = NSMakeRange(NSNotFound, 0) var rangeToReplace = layoutManager.characterRangeThatFits(textContainer) if NSMaxRange(rangeToReplace) == _originalTextLength { rangeToReplace = emptyRange } else { rangeToReplace.location = NSMaxRange(rangeToReplace) - _trimText!.length - _trimTextPrefixLength if rangeToReplace.location < 0 { rangeToReplace = emptyRange } else { rangeToReplace.length = textStorage.length - rangeToReplace.location } } return rangeToReplace } private func trimTextRange() -> NSRange { var trimTextRange = rangeToReplaceWithTrimText() if trimTextRange.location != NSNotFound { trimTextRange.length = _trimTextPrefixLength + _trimText!.length } return trimTextRange } private func pointInTrimTextRange(point: CGPoint) -> Bool { let offset = CGPointMake(textContainerInset.left, textContainerInset.top) var boundingRect = layoutManager.boundingRectForCharacterRange(trimTextRange(), inTextContainer: textContainer, textContainerOffset: offset) boundingRect = CGRectOffset(boundingRect, textContainerInset.left, textContainerInset.top) boundingRect = CGRectInset(boundingRect, -(trimTextRangePadding.left + trimTextRangePadding.right), -(trimTextRangePadding.top + trimTextRangePadding.bottom)) return CGRectContainsPoint(boundingRect, point) } func countElements(text: String) -> Int { return text.characters.count } } //MARK: NSLayoutManager extension extension NSLayoutManager { func characterRangeThatFits(textContainer: NSTextContainer) -> NSRange { var rangeThatFits = self.glyphRangeForTextContainer(textContainer) rangeThatFits = self.characterRangeForGlyphRange(rangeThatFits, actualGlyphRange: nil) return rangeThatFits } func boundingRectForCharacterRange(range: NSRange, inTextContainer textContainer: NSTextContainer, textContainerOffset: CGPoint) -> CGRect { let glyphRange = self.glyphRangeForCharacterRange(range, actualCharacterRange: nil) let boundingRect = self.boundingRectForGlyphRange(glyphRange, inTextContainer: textContainer) return boundingRect } } ,我使用此类基本上是TableView的子类,其中的按钮可将TextView扩展到所需的高度:

tableView.beginUpdate

如果我的textView在ViewController中,上面的工作正常,但由于我在UITableViewController中的单元格中,我无法使用新TextView的高度更新单元格的高度(更新后的textView) )任何想法如何在textView的高度更新时更新我的​​tableView?

P.S。我知道我必须使用endUpdate,{{1}},但我想问什么时候使用这个?如何知道textView的框架是否已更改

2 个答案:

答案 0 :(得分:1)

我使用this code of Ilya Puchka来解决TableViewCell的TextView中的Read More。

<强>更新

也许你可以使用tableView.heightForRowAtIndexPath()和 intrinsicContentSize ,它们会在扩展后改变

override func tableView(tableView: UITableView!, heightForRowAtIndexPath indexPath: NSIndexPath!) -> CGFloat {
     // an here use intrinsicContentSize
     return self.intrinsicContentSize().height
}

未经测试!仅调试过!已经够晚了; - )

答案 1 :(得分:1)

首先:我没有在您的表格中看到datasourcedelegate方法。

您的对象需要3个键。

"heightCell" : 53, "originalHeight" : 0, "isexpanded" : 0

你需要cellForRowAtIndexPath中的

let eachRow = faqInfo.objectAtIndex(indexPath.row)
eachRow.setValue(53, forKey: "heightCell")

var currentHeght = eachRow["heightCell"] as! CGFloat
//25 default question height
eachRow.setValue(currentHeght + (newQuestionHeight - 25), forKey: "heightCell")

var currentHeght = eachRow["heightCell"] as! CGFloat
eachRow.setValue(currentHeght, forKey: "originalHeight")

let isExpanded = eachRow["isexpanded"] as! Bool
if isExpanded == true {
    increaseTextViewInCell(cell, eachRow: eachRow)
}

Function用于增加单元格中的textview

func increaseTextViewInCell(cell: FAQTableViewCellController, eachRow: AnyObject) {
        let answer = eachRow["answer"] as? String
        let newAnswerHeight = Utils.heightForView(answer!, font: UIFont(name: "AvenirNextCondensed-Regular", size: CGFloat(15))!, width: cell.textviewAnswer.frame.size.width, xpos: cell.textviewAnswer.frame.origin.x)

        //1 default question height
        let currentHeght = eachRow["heightCell"] as! CGFloat
        eachRow.setValue(currentHeght + (newAnswerHeight - 1) + 40, forKey: "heightCell")
        cell.textviewAnswer.text = answer
    }

didSelectRowAtIndexPath

func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
    let cell = tableView.cellForRowAtIndexPath(indexPath) as! FAQTableViewCellController
    let eachRow = faqInfo.objectAtIndex(indexPath.row)

    let isExpanded = eachRow["isexpanded"] as! Bool
    if isExpanded == false {

        increaseTextViewInCell(cell, eachRow: eachRow)
        /* unncoment if need unexpand all the others cell
        var i = 0
        for eachInfo in faqInfo {
            let isExpanded = eachInfo["isexpanded"] as! Bool
            if isExpanded == true {
                eachInfo.setValue(0, forKey: "isexpanded")
                tableView.reloadRowsAtIndexPaths([NSIndexPath(forRow: i, inSection: 0)], withRowAnimation: .Fade)
                break
            }
            i += 1
        }
        */
        eachRow.setValue(1, forKey: "isexpanded")

    } else {
        let originalHeight = eachRow["originalHeight"] as! CGFloat
        eachRow.setValue(originalHeight, forKey: "heightCell")

        eachRow.setValue(0, forKey: "isexpanded")
    }

    tableView.beginUpdates()
    tableView.endUpdates()
}

heightForView函数

class func heightForView(text:String, font:UIFont, width:CGFloat, xpos:CGFloat) -> CGFloat {
        let label:UILabel = UILabel(frame: CGRectMake(xpos, 0, width, CGFloat.max))
        label.numberOfLines = 0
        label.lineBreakMode = NSLineBreakMode.ByWordWrapping
        label.font = font
        label.text = text

        label.sizeToFit()
        return label.frame.height
    }