Reputation: 363
I am trying to set an attributed string within NSTextView. I want to increase its height based on its content, initially it is set to some default value.
So I tried this method:
I set content in NSTextView. When we set some content in NSTextView its size automatically increases. So I increased height of its super view that is NSScrollView to its height, but NSScrollView is not getting completely resized, it is showing scroller on right.
float xCoordinate = 15.0;
[xContentViewScroller setFrame:NSMakeRect(xCoordinate, 0.0, 560.0, 10.0)];
[[xContentView textStorage] setAttributedString:xContents];
float xContentViewScrollerHeight = [xfContentView frame].size.height + 2;
[xContentViewScroller setFrame:NSMakeRect(xCoordinate, 0.0, 560.0, xContentViewScrollerHeight)];
Can anyone suggest me some way or method to resolve this issue. By doing google search I found that in UITextView there is contentSize method which can get size of its content, I tried to find similar method in NSTextView but could not get any success :(
Upvotes: 23
Views: 11826
Reputation: 177
Based on @hyouuu's answer, I have built a working version of a custom NSTextView in Swift 5.x:
import AppKit
class MyTextView: NSTextView {
var heightConstraint: NSLayoutConstraint?
var contentSize: CGSize {
get {
guard let layoutManager = layoutManager, let textContainer = textContainer else {
return .zero
}
layoutManager.ensureLayout(for: textContainer)
return layoutManager.usedRect(for: textContainer).size
}
}
override init(frame frameRect: NSRect, textContainer container: NSTextContainer?) {
super.init(frame: frameRect, textContainer: container)
}
override init(frame frameRect: NSRect) {
super.init(frame: frameRect)
self.translatesAutoresizingMaskIntoConstraints = false
heightConstraint = self.heightAnchor.constraint(equalToConstant: 0)
heightConstraint?.isActive = true
}
required init?(coder: NSCoder) {
fatalError()
}
// Adjust height during layout resizing
override func layout() {
updateHeight()
super.layout()
}
// Adjust height after modifying string attributes
override var string: String {
didSet {
didChangeText()
}
}
// Adjust height while typing/texting
override func didChangeText() {
updateHeight()
super.didChangeText()
}
// Adjust the height constraint. You can call this method manually if you programmatically change the text.
func updateHeight() {
heightConstraint?.constant = self.contentSize.height + textContainerInset.height * 2
}
}
Additional autolayout constraints should be added when adding it to a view.
This works well even in NSTableView with tableView.usesAutomaticRowHeights = true
Upvotes: 1
Reputation: 2491
Based on @Peter Hosey's answer, here is an extension to NSTextView in Swift 4.2:
extension NSTextView {
var contentSize: CGSize {
get {
guard let layoutManager = layoutManager, let textContainer = textContainer else {
print("textView no layoutManager or textContainer")
return .zero
}
layoutManager.ensureLayout(for: textContainer)
return layoutManager.usedRect(for: textContainer).size
}
}
}
Upvotes: 13
Reputation: 4097
+ (float)heightForString:(NSString *)myString font:(NSFont *)myFont andWidth:(float)myWidth andPadding:(float)padding {
NSTextStorage *textStorage = [[NSTextStorage alloc] initWithString:myString];
NSTextContainer *textContainer = [[NSTextContainer alloc] initWithContainerSize:NSMakeSize(myWidth, FLT_MAX)];
NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init];
[layoutManager addTextContainer:textContainer];
[textStorage addLayoutManager:layoutManager];
[textStorage addAttribute:NSFontAttributeName value:myFont
range:NSMakeRange(0, textStorage.length)];
textContainer.lineFragmentPadding = padding;
(void) [layoutManager glyphRangeForTextContainer:textContainer];
return [layoutManager usedRectForTextContainer:textContainer].size.height;
}
I did the function using this reference: Documentation
Example:
float width = textView.frame.size.width - 2 * textView.textContainerInset.width;
float proposedHeight = [Utils heightForString:textView.string font:textView.font andWidth:width
andPadding:textView.textContainer.lineFragmentPadding];
Upvotes: 3
Reputation: 2120
NSTextView *textView = [[NSTextView alloc] init];
textView.font = [NSFont systemFontOfSize:[NSFont systemFontSize]];
textView.string = @"Lorem ipsum";
[textView.layoutManager ensureLayoutForTextContainer:textView.textContainer];
textView.frame = [textView.layoutManager usedRectForTextContainer:textView.textContainer];
Upvotes: 10