2016-07-22 4 views
0

Я пытаюсь добавить see more функциональность, что-то вроде this. на моем UITextView, который находится внутри tableView'scell и для этого я использую этот класс, который является в основном подкласс TableView с кнопкой, которая расширяет TextView его требуемую высоту:update tableView при изменении фрейма элемента ячейки

@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 
    } 

} 

вышеизложенное работает нормально, если мой textView находится в ViewController, но поскольку у меня есть это внутри ячейки, которая находится в UITableViewController, я не могу обновить высоту ячейки с высотой нового TextView (обновленный textView), любую идею, как я могу обновить свой tableView, когда textView обновления высоты?

P.S. Я знаю, что мне нужно использовать tableView.beginUpdate, endUpdate, но я спрашиваю, когда использовать это? как узнать, изменился ли кадр textView

+0

Вы пробовали установки свойства вашего Tableview в 'RequiredHeight' (я не уверен, что его точное название, но посмотреть в IntelliSense ничего с 'height'in) на' UITableView.AutomaticDimension'? –

+0

Я бы предложил следующую библиотеку: - https://github.com/Ramotion/folding-cell – MShah

+0

мужчина, я хочу только расширенную ячейку, когда пользователь забирает ячейку @Zil, поэтому установка AutomaticDimension не будет работать для меня –

ответ

1

Я использовал this code of Ilya Puchka, чтобы решить проблему в TextView для TableViewCell.

Update

Возможно, и можно использовать tableView.heightForRowAtIndexPath() и там intrinsicContentSize что изменится после расширения

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

не тестировался! только отлажен! Уже достаточно поздно ;-)

+0

well i 'm, используя то же самое, но как вы уведомляете tableview, чтобы обновить его высоту строки? –

+0

хм, извините, не знаю :-( –

+0

взгляните на мой обновленный ответ ... –

1

Первый: Я не вижу datasource и delegate методов из вашего стола.

Вам необходимо иметь 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 
    } 
Смежные вопросы