29

I've setup an NSTextField with text color as white, and the background color as (black despite not rendering the background color, so its transparent). All in Interface Builder.

The problem I am having is the cursor is black, and hardly visible. Does the cursor not represent the text color? Any ideas how I can fix this?

Otherwise, the NSTextField looks like it cannot be edited.

JJD
  • 50,076
  • 60
  • 203
  • 339
mootymoots
  • 4,545
  • 9
  • 46
  • 74

10 Answers10

37

Since in practice the NSText* returned by -currentEditor for an NSTextField is always an NSTextView*, I added the following code to my custom NSTextField subclass:

-(BOOL) becomeFirstResponder
{
    BOOL    success = [super becomeFirstResponder];
    if( success )
    {
        // Strictly spoken, NSText (which currentEditor returns) doesn't
        // implement setInsertionPointColor:, but it's an NSTextView in practice.
        // But let's be paranoid, better show an invisible black-on-black cursor
        // than crash.
        NSTextView* textField = (NSTextView*) [self currentEditor];
        if( [textField respondsToSelector: @selector(setInsertionPointColor:)] )
            [textField setInsertionPointColor: [NSColor whiteColor]];
    }
    return success;
}

So if you're already replacing this class because you're doing custom background drawing, this might be a more encapsulated solution. Maybe there's even a way to move this up into NSCell, which would be cleaner since NSCell is the one doing the drawing and knowing the colors anyway.

uliwitness
  • 8,532
  • 36
  • 58
  • 2
    This should really be marked as the correct answer; using the `textDidBegin/end:` delegate methods as proposed in other answers does not change the caret color until the user starts typing. This one changes it immediately on focus. – Bryan Oct 15 '13 at 18:47
  • 2
    You may want to add a corresponding `-(BOOL) resignFirstResponder` method that changes the color back to black, since the field editor in question is shared by all textFields in the window. Leaving it set to white is probably not a good idea, as other fields won't automatically set it to black. – Bryan Oct 15 '13 at 18:48
  • 1
    Note that resignFirstResponder will not work in this case because it will be called right after becomeFirstResponder when the field editor becomes first responder. – Jon Steinmetz Mar 25 '14 at 21:19
28

TextField Insertion Point Color

NSTextField *textField = self.textField;
NSColor *insertionPointColor = [NSColor blueColor];

NSTextView *fieldEditor = (NSTextView*)[textField.window fieldEditor:YES
                                                           forObject:textField];
fieldEditor.insertionPointColor = insertionPointColor;
Zelko
  • 3,793
  • 3
  • 34
  • 40
  • 1
    just make sure you call it in viewDidAppear cause fieldEditor will be still nil in viewDidLoad or viewWillAppear – Y.A.P. Sep 05 '17 at 14:44
21

Your best bet is probably to use NSTextView and - (void)setInsertionPointColor:(NSColor *)color.

Robert Karl
  • 7,598
  • 6
  • 38
  • 61
14

Assuming that you are wanting to set the color of the insertion caret and not the mouse cursor then the suggestion of using setInsertionPointColor: should work.

However, you do not necessarily need to change from using NSTextField to NSTextView. The field editor for window that the NSTextField is in is an NSTextView. So when your NSTextField becomes the key view you could grab the field editor and call setInsertionPointColor: on that. You may need to reset the color when your field stops being the key view.

You can get the field editor by using NSWindow's fieldEditor:forObject: or NSCell's fieldEditorForView:.

If you have a subclass of NSTextField you can have it use a custom subclass of NSTextFieldCell and override -(NSText*)setUpFieldEditorAttributes:(NSText*)textObj. In that method you can set the insertion point color once and it will stay while the field editor is active for this text field. Though when the field editor is moved to another edit field the insertion point color will remain unless you reset it.

Jon Steinmetz
  • 4,104
  • 1
  • 23
  • 21
  • This is a fantastic way to change the insertion point color on an NSSecureTextField. Thanks! – Ben Dolman Jul 02 '11 at 07:19
  • But `fieldEditor:forObject:` returns an `NSText` object. I cannot call `setInsertionPointColor:` on an `NSText`. – JJD Jan 03 '12 at 12:14
  • I no longer have access to my original code but NSTextView is a subclass of NSText and if you look at the interface for NSTextView it has a isFieldEditor method. You might try using isKindOfClass: to test if the NSText is really a NSTextView. Another thing would be to try to use NSCell's fieldEditorForView: like in JJD's answer. – Jon Steinmetz Jan 06 '12 at 14:37
  • This is not correct. `textDidBeginEditing:` and `textDidEndEditing:` do NOT inform you of changes in the NSTextField's key state. Those methods will only fire once the user starts typing text into the field. If you simply click on the field to make it key, those methods do NOT fire and you will therefore see a black caret until you type the first character. – Bryan Oct 15 '13 at 18:42
5

I've called insertionPointColor in viewDidLoad and app crashes.

I fixed this by calling insertionPointColor on viewDidAppear.

For Swift developers:

Set insertionPointColor method into extension:

extension NSTextField {
    public func customizeCursorColor(_ cursorColor: NSColor) {
        let fieldEditor = self.window?.fieldEditor(true, for: self) as! NSTextView
        fieldEditor.insertionPointColor = cursorColor
    }
}

and call

 override func viewDidAppear() {
        super.viewDidAppear()
        textField.customizeCursorColor(NSColor.red)
    }
Gent
  • 6,215
  • 1
  • 37
  • 40
3

Swift 4 Solution

override func viewDidAppear() {
    super.viewDidAppear()
    guard let window = _textField.window, let fieldEditor = window.fieldEditor(true, for: _textField) as? NSTextView else { return }
    fieldEditor.insertionPointColor = .white
}
Charlton Provatas
  • 2,184
  • 25
  • 18
2

Inspired by the great answer of Jon Steinmetz I created the following example.

I added a NSSecureTextField to the application view and connected it to the IBOutlet of the member variable I placed into AppDelegate.

@implementation AppDelegate

@synthesize password = m_password;

- (void)awakeFromNib {
    assert(m_password);
    self.password.backgroundColor = [NSColor blackColor];
}

Then I created a custom NSSecureTextField class. I noticed that is in some cases not enough to set the colors in awakeFromNib but I cannot give a reason for this.

@implementation CustomSecureTextField

- (void)customize {
    // Customize the text and caret color.
    NSColor* foregroundColor = [NSColor whiteColor];
    self.textColor = foregroundColor;
    [[self.cell fieldEditorForView:self] setInsertionPointColor:foregroundColor];   
}

- (void)awakeFromNib {
    [self customize];
}

- (void)textDidBeginEditing:(NSNotification*)notification {
    // Called when the user inputs a character.
    [self customize];
}

- (void)textDidEndEditing:(NSNotification*)notification {
    // Called when the user clicks into the field for the first time.
    [self customize];   
}

- (void)textDidChange:(NSNotification*)notification {
    // Just in case ... for the paranoid programmer!
    [self customize];
}


@end

Note: Though, I do not understand why the background color cannot be set when I do this in the derived class like with the textColor. That would allow to get rid of the IBOutlet and the member variable.

Community
  • 1
  • 1
JJD
  • 50,076
  • 60
  • 203
  • 339
  • it's probably worth calling `super`'s implementation of the functions you are overriding. – Vince Feb 01 '18 at 07:38
1

easiest way is

 override func viewDidAppear() {
    if let fieldEditor = self.view.window?.fieldEditor(true, for: self) as? NSTextView{
        fieldEditor.insertionPointColor = NSColor.black
    }
 }
Awais Mobeen
  • 733
  • 11
  • 19
1

I use this code in swift

 if let editor = textField.currentEditor() as? NSTextView{
     editor.insertionPointColor = myColor ?? .black
 }
0

If you use Objective-C runtime selector capture combined with uliwitness's solution, you can achieve it without subclassing NSTextField, here I use RxCocoa's methodInvoked as an example:

import Cocoa
import RxCocoa

extension NSTextField {
    func withCursorColor(_ color: NSColor) {
        rx.methodInvoked(#selector(becomeFirstResponder))
            .subscribe(onNext: { [unowned self] _ in
                guard let editor = self.currentEditor() as? NSTextView else { return }
                editor.insertionPointColor = color
            })
            .disposed(by: rx.disposeBag)
    }
}

duan
  • 8,515
  • 3
  • 48
  • 70