How do I make NSTextView grow with text?

2019-04-15 05:48发布

问题:

I am creating a simple word processor where it's possible for the user to add a text box to an NSView, similar to the function in Pages. The problem is that when it's added it will stay the same size no matter how much text the user inputs. I want it to grow as the user inputs text, I have tried with this GitHub project but when I use it the text field only expands when I have deleted all the text, as if the code doesn't react before the textDidEndEditing method. After working a bit with NSTextView I found that it would be more suitable for the task, but I still can't make it work. I'm running Mavericks and Xcode 5.0.1.

Hope someone can help me!

回答1:

This example fixed it for me: https://developer.apple.com/library/mac/documentation/cocoa/conceptual/TextUILayer/Tasks/TextInScrollView.html

You have to put it into a NSScrollView. The behavior is not the same as in UITextView (if you have a iOS background).



回答2:

The following uses an NSTextView subclass that must be created in code. For reasons of its own Xcode won't allow you to instantiate an NSTextView in a nib without an enclosing NSScrollView instance.

This class lets the only defines the text view intrinsic height - the width is left undefined which allows the view to grow with its enclosing view. I used this in an NSStackView and it seemed to work well. Trying to bludgeon NSTextField so that it could wrap multiline text, edit and support Auto Layout was too messy.

Note we have support for a focus ring as I wanted my class to act like and Uber text field. Also note that we have no support for a border. In my actual usage I create a compound view that wraps the custom text view. This wrapper view draws a border as required.

@interface BPTextViewField : NSTextView

// primitives
@property (assign, nonatomic) CGFloat borderOffsetX;
@property (assign, nonatomic) CGFloat borderOffsetY;
@end

@implementation BPTextViewField

#pragma mark -
#pragma mark Life cycle

- (instancetype)initWithFrame:(NSRect)frameRect textContainer:(nullable NSTextContainer *)container
{
    self = [super initWithFrame:frameRect textContainer:container];
    if (self) {
        [self commonInit];
    }
    return self;
}

- (nullable instancetype)initWithCoder:(NSCoder *)coder
{
    self = [super initWithCoder:coder];
    if (self) {
        [self commonInit];
    }
    return self;
}

- (void)commonInit
{
    _borderOffsetX = 1;
    _borderOffsetY = 3;
    self.usesFontPanel = NO;
    self.usesFindPanel = NO;

}
#pragma mark -
#pragma mark Auto layout

- (NSSize)intrinsicContentSize
{
    NSTextContainer* textContainer = [self textContainer];
    NSLayoutManager* layoutManager = [self layoutManager];
    [layoutManager ensureLayoutForTextContainer: textContainer];
    NSSize size = [layoutManager usedRectForTextContainer: textContainer].size;

    return NSMakeSize(NSViewNoIntrinsicMetric, size.height);
}

#pragma mark -
#pragma mark Accessors

- (void)setString:(NSString *)string
{
    [super setString:string];
    [self invalidateIntrinsicContentSize];
}

#pragma mark -
#pragma mark Text change notifications

- (void)didChangeText
{
    [super didChangeText];
    [self invalidateIntrinsicContentSize];
}

#pragma mark -
#pragma mark Drawing

- (void)drawRect:(NSRect)rect
{
    [super drawRect:rect];
}

#pragma mark -
#pragma mark Focus ring

- (void)drawFocusRingMask
{
    if (self.editable) {
        NSRectFill(self.focusRingMaskBounds);
    }
}

- (NSRect)focusRingMaskBounds {
    NSRect r = [self bounds];
    return NSMakeRect(r.origin.x - self.borderOffsetX, r.origin.y - self.borderOffsetY, r.size.width + self.borderOffsetX * 2, r.size.height + self.borderOffsetY * 2);
}


@end