2016-07-22 4 views
0

Ich versuche see more Funktionalität hinzuzufügen, etwas wie this. auf meinem UITextView das ist in einem tableView'scell und dass ich diese Klasse bin mit der im Grunde eine Unterklasse von Tableview mit einem Knopf ist, der die Textview auf seine erforderlichen Höhe erweitert:update tableView, wenn sich der Rahmen des Elements der Zelle ändert

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

} 

wenn die oben funktioniert gut mein textView ist in einem ViewController, aber da ich dies in einer Zelle habe, die in UITableViewController ist, kann ich die Höhe der Zelle mit der Höhe von neuem TextView nicht aktualisieren (aktualisiertes textView) irgendeine Idee, wie ich mein tableView aktualisieren kann, wenn textView Höhenaktualisierungen ??

P.S. Ich weiß, dass ich tableView.beginUpdate, endUpdate verwenden muss, aber ich frage, wann man das benutzt ?? wie man erkennt, wenn der Rahmen des textView geändert wird

+0

Haben Sie versucht, Ihre Tableview Eigentum 'RequiredHeight' Einstellung (i seine den genauen Namen nicht sicher bin, aber in der Intellisense etwas nachschlagen mit 'height' in it) zu' UITableView.AutomaticDimension'? –

+0

Ich würde vorschlagen, folgende Bibliothek: - https://github.com/Ramotion/folding-cell – MShah

+0

man möchte nur erweiterte Zelle, wenn der Benutzer in eine Zelle @Zil tippt, warum Einstellung AutomaticDimension wird nicht für mich arbeiten –

Antwort

1

ich this code of Ilya Puchka verwendet habe, um das Lesen mehr in einem TextView für eine TableViewCell zu lösen.

aktualisieren

Vielleicht kann u verwenden tableView.heightForRowAtIndexPath(), und es intrinsicContentSize, die nach

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

nicht getestet erweitern wird sich ändern! nur debugged! Es ist spät genug ;-)

+0

gut ich benutze das selbe, aber wie meldet du die Tabellenansicht, um ihre Zeilenhöhe zu aktualisieren? –

+0

hmm, tut mir leid, keine Ahnung :-( –

+0

schau dir meine aktualisierte Antwort an ... –

1

Erstens: Ich sehe nicht datasource und delegate Methoden aus Ihrer Tabelle.

Sie benötigen 3 Schlüssel in Ihrem Objekt.

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

in cellForRowAtIndexPath Sie brauchen:

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

und

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 für die Erhöhung der Zelltextview

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 
    } 

und in der 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() 
} 

Und die heightForView Funktion

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 
    } 
Verwandte Themen