我尝试添加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的框架是否已更改
答案 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)
首先:我没有在您的表格中看到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
}