A O
A O

Reputation: 5698

NSTextView changing Insertion Point (Caret) Cursor size when there is no string

I am creating a NSTextView, and I want to change the size of the insertion point when the NSTextView is empty.

It appears that the caret cursor (I-Beam / IBeam cursor) size is based off of the current font size attribute on the attributedString property of the NSTextView.

The problem is that because the NSTextView has an empty attributedString, there can be no attributes on the string. This means that I can't apply a font size.

However, I can change the typingAttributes to have the fontSize be correct-- but the Insertion Point Cursor does not update size until I start typing

See these screenshots: Notice here the textview on the right has a small insertion point cursor:
enter image description here

I start typing, and it updates in size:
enter image description here

So my question is, is there a property that I need to set on the NSTextView when I initialize it, in order to get the initial Insertion Point Cursor the correct Size? (I want it to match the typingAttributes font)

EDIT: @Mark Bessey brought up a good question-- what is the order that I'm setting the typing attributes:

  [_textEditor setTypingAttributes:typingAttributes];
  [_textEditor setDelegate:self];

  [view addSubview:_textEditor];
  [self updateInsertionPointColor];

  [[view window] makeFirstResponder:_textEditor];
  [view setNeedsDisplay:YES];

Upvotes: 2

Views: 1279

Answers (3)

Stefan Stuckmann
Stefan Stuckmann

Reputation: 146

I just ran into the same problem and – after not finding a good solution anywhere – managed to force the NSTextView to update its pointer by inserting a character and then replacing that character with an empty string in the same cycle.

I'm doing this in textView(_:shouldChangeTextIn:replacementString:), but I guess it works elsewhere just as fine.

let attributedString = NSAttributedString(string: " ", attributes: attributes)
                    
let range = someRange
let insertLocation = range.location + range.length
textView.textStorage?.insert(attributedString, at: insertLocation)

textView.typingAttributes = attributes

textView.replaceCharacters(in: range, with: "")

Upvotes: 1

Owen Zhao
Owen Zhao

Reputation: 3365

To changed the attributes when there is no text, using typingAttributes.

Typing attributes are reset automatically whenever the selection changes. However, if you add any user actions that change text attributes, the action should use this method to apply those attributes afterwards. User actions that change attributes should always set the typing attributes because there might not be a subsequent change in selection before the next typing.

@IBOutlet var targetTextView: NSTextView!{
    didSet {
        let attributes:[NSAttributedString.Key:Any] = [
            .font:NSFont.userFont(ofSize: 16.0) ?? NSFont.systemFont(ofSize: 16.0),
            .foregroundColor:NSColor(named: "targetColor")!
        ]
        targetTextView.typingAttributes = attributes
    }
}

Upvotes: 0

Mark Bessey
Mark Bessey

Reputation: 19782

Doing this works in a simple test app, in my applicationDidFinishLaunching method:

NSFont *font = [NSFont fontWithName:@"Marker Felt" size:24.0];
NSDictionary *attr = @{
                         NSFontAttributeName: font
                       };
[self.textView setTypingAttributes:attr];

I get the large I-beam cursor that I'd expect.

This is basically the same as what TextEdit does (sample code here), so it doesn't look like you're doing anything wrong, as far as setting the typingAttributes goes.

I wonder if perhaps you're setting the attributes at the wrong time. When do you set the typingAttributes? Is the view on-screen? Is it the first responder?

Upvotes: 0

Related Questions