164

I am using a UITextField with a UIPickerView for its inputView, so that when the user taps the text field, a picker is summoned for them to select an option from.

Nearly everything works, but I have one problem: the cursor still flashes in the text field when it is active, which is ugly and inappropriate, since the user is not expected to type into the field and is not presented with a keyboard. I know I could hackily solve this by setting editing to NO on the text field and tracking touches on it, or by replacing it with a custom-styled button, and summoning the picker via code. However, I want to use the UITextFieldDelegate methods for all the event handling on the text field and hacks such as replacing the text field with a button do not permit this approach.

How can I simply hide the cursor on the UITextField instead?

pkamb
  • 33,281
  • 23
  • 160
  • 191
emenegro
  • 6,901
  • 10
  • 45
  • 68

15 Answers15

288

Simply subclass UITextField and override caretRectForPosition

- (CGRect)caretRectForPosition:(UITextPosition *)position
{
    return CGRectZero;
}
DiscDev
  • 38,652
  • 20
  • 117
  • 133
Joseph Chiu
  • 2,904
  • 1
  • 13
  • 2
  • 1
    @Joseph Chiu It works great. But not with iOS 4.3. Could you help me with this? – Dinesh Raja Mar 29 '13 at 05:52
  • the cleanest and shortest approach which deserves a double upvote =) – Ilker Baltaci Jun 13 '14 at 12:45
  • 1
    Just a note. In my subclass I added a bool hideCaret then in this override If it is true -> return CGRectZero else return the result of super. – WCByrne Aug 05 '14 at 04:28
  • How do you trigger the system to ask you for a new caret position? i.e. how could I toggle the caret on and off without changing the contents of the text field? – Zev Eisenberg Jun 17 '15 at 18:56
  • 6
    Just be aware that users with an external keyboard can change the text field's value even if the cursor is hidden and you use a picker view. – Mark Oct 02 '15 at 09:46
  • yep doesn't prevent input via hardware keyboard so not really a solution – malhal Jan 15 '18 at 21:56
  • @malhal, hiding the cursor was all the OP asked for. – Andrew Kirna Jul 25 '19 at 13:45
  • You can combine this solution with `UITextFieldDelegate`'s `textField(_:shouldChangeCharactersIn:replacementString:)` and always return false. – Andrew Kirna Jul 25 '19 at 13:51
171

As of iOS 7 you can now just set the tintColor = [UIColor clearColor] on the textField and the caret will disappear.

jamone
  • 17,253
  • 17
  • 63
  • 98
  • 2
    This works at the moment, however I would advise against using it since it might change in the future. Rather opt for the `caretRectForPosition:` override solution. – lipka Feb 24 '14 at 11:59
  • @lipka True, that is probably a better way. – jamone Sep 08 '14 at 15:55
  • 2
    Good enough for now. Sometimes you just need a quick solution. – GoldenJoe Feb 16 '15 at 11:34
  • This is the easiest solution by far! – Jay Q. Aug 10 '15 at 11:38
  • 1
    This answer should be approved for iOS 7+ – tryp Nov 09 '17 at 11:08
  • Good solution and very easy to use, Swift 4 update: textField.tintColor = .clear – Lance Samaria Oct 14 '18 at 04:56
  • @lipka why would this behavior change in the future? The tintColor is an inherit property of the textField control and is meant to be used this way. By this logic, you should never change any properties of any ui object because they might change in the future. This answer is just as appropriate as the accepted answer. – JRam13 Jun 15 '20 at 14:00
  • It works but the caret will show once a user make a selection. Overriding a caretRect is more universal solution – Blazej SLEBODA Aug 06 '20 at 20:08
116

You can just clear the textfield's tintColor

self.textField.tintColor = [UIColor clearColor];

Swift 3.0

self.textField.tintColor = .clear

enter image description here

Antony Raphel
  • 2,036
  • 2
  • 25
  • 45
oldman
  • 4,286
  • 2
  • 20
  • 15
21

You might also want to stop the user from selecting, copying or pasting any text so that the only text input comes from the picker view.

- (CGRect) caretRectForPosition:(UITextPosition*) position
{
    return CGRectZero;
}

- (NSArray *)selectionRectsForRange:(UITextRange *)range
{
    return nil;
}

- (BOOL)canPerformAction:(SEL)action withSender:(id)sender
{
    if (action == @selector(copy:) || action == @selector(selectAll:) || action == @selector(paste:))
    {
        returnNO;
    }

    return [super canPerformAction:action withSender:sender];
}

http://b2cloud.com.au/tutorial/disabling-the-caret-and-text-entry-in-uitextfields/

Nat
  • 231
  • 2
  • 2
15

Check out the property selectedTextRange of the protocol UITextInput, to which the class UITextField conforms. Few! That's a lesson in object-oriented programing right there.

Hide Caret

To hide the caret, nil out the text field's selected text range.

textField.selectedTextRange = nil; // hides caret

Unhide Caret

Here are two ways to unhide the caret.

  1. Set the text field's selected text range to the end of the document.

    UITextPosition *end = textField.endOfDocument;
    textField.selectedTextRange = [textField textRangeFromPosition:end
                                                        toPosition:end];
    
  2. To keep the caret in the same spot, first, store the text field's selected text range to an instance variable.

    _textFieldSelectedTextRange = textField.selectedTextRange;
    textField.selectedTextRange = nil; // hides caret
    

    Then, when you want to unhide the caret, simply set the text field's selected text range back to what it was originally:

    textField.selectedTextRange     = _textFieldSelectedTextRange;
    _textFieldLastSelectedTextRange = nil;
    
ma11hew28
  • 121,420
  • 116
  • 450
  • 651
  • 4
    This particular solution didn't work for my implementation. The cursor still blinks. – Art Geigel Jun 11 '13 at 04:59
  • Well, then, perhaps you should file a bug at [bugreport.apple.com](https://bugreport.apple.com/) because the iOS docs say: "If the text range has a length, it indicates the currently selected text. If it has zero length, it indicates the caret (insertion point). If the text-range object is nil, it indicates that there is no current selection." – ma11hew28 Jun 12 '13 at 02:35
  • 6
    I don't care enough to file a report. If others use your "solution" and don't see it working I wanted them to know they're not alone. – Art Geigel Jun 13 '13 at 03:26
  • Despite @ArtGeigel's comments, this works perfectly for me. However, I kind of prefer the solution involving overriding `caretRectForPosition`. It's more explicit what it's doing, and the docs that you've quoted don't make clear what the behaviour of the caret should be when there is 'no current selection'. If @ArtGeigel's claim that this doesn't work were correct (which it isn't, at least as far as I can see) it wouldn't be clear that that were a bug. – Mark Amery Aug 02 '13 at 16:50
  • Didn't work for me either. Caret is still there and blinking. – CW0007007 Aug 22 '13 at 09:50
  • Worked great for me too! and simpler than subclassing, especially when you need to remove the caret on demand – matehat Jan 18 '14 at 18:18
  • There are a couple of gotchas which might affect your usage: 1) The user can still bring the cursor onscreen if they want by tapping on the field 2) Any time you programatically change the field's text, it'll want to turn the caret back on; you can avoid this by turning it off again straight after – Mike Abdullah Sep 04 '14 at 17:31
  • I can assure you guys this solution WORKS! Thanks. – Benjamin Jun 30 '16 at 13:55
13

Swift 5 version of Net's post

  override func caretRect(for position: UITextPosition) -> CGRect {
    return .zero
  }
  
  override func selectionRects(for range: UITextRange) -> [UITextSelectionRect] {
    return []
  }
  
  override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
    return false
  }
STerrier
  • 3,755
  • 1
  • 16
  • 41
Rom.
  • 2,800
  • 2
  • 17
  • 19
  • In my case it worked. Added a subclass of UITextField with these methods using Swift 4. Thanks! – J. Fdez Jul 31 '18 at 14:18
11

Answer provided by the OP, copied from the question body to help clean up the ever growing tail of unanswered questions.

I found another solution: subclass UIButton and override these methods

- (UIView *)inputView {
    return inputView_;
}

- (void)setInputView:(UIView *)anInputView {
    if (inputView_ != anInputView) {
        [inputView_ release];
        inputView_ = [anInputView retain];
    }
}

- (BOOL)canBecomeFirstResponder {
    return YES;
}

Now the button, as a UIResponder, have a similar behavior than UITextField and an implementation pretty straightforward.

Mike Abdullah
  • 14,933
  • 2
  • 50
  • 75
Robert Höglund
  • 10,010
  • 13
  • 53
  • 70
  • 5
    This is not really a great solution. Check out this answer below: http://stackoverflow.com/a/13660503/1103584 – DiscDev Mar 12 '13 at 19:31
  • 2
    Why isn't this a great solution? It achieves the intended effect and also provides functionality to a class that previously didn't have it. It's also not a hack. I think it's really cool. So what's wrong with it? – BreadicalMD Aug 19 '13 at 14:57
  • 2
    @BreadicalMD The biggest problem I can see is that you can't use a `UITextFieldDelegate` with this to handle begin and end editing events. Instead - unless there's a way to handle those events that I don't know about - you'll need to override `becomeFirstResponder` and `resignFirstResponder` in the button subclass, and possibly create your own delegate protocol, add in a `delegate` property, and call the delegate from the aforementioned methods. This is all far more work than just overriding `caretRectForPosition` in a `UITextField` subclass. – Mark Amery Oct 01 '13 at 22:18
  • 1
    @BreadicalMD That said, despite Joseph Chiu's answer being superior from any practical standpoint, I still agree with you that this is pretty sexy. I'd never looked carefully at the `UIResponder` class reference before, and had no idea that a trick like this was possible. – Mark Amery Oct 01 '13 at 22:19
  • Late to the party, but I think this is a very good solution and it feels much more appropriate and less of a hack than using a `UITextField` and hiding the cursor, which is basically a hack since we are then using the text field as a label while not using any of the functionality of the `UITextField`. – Rupert Oct 20 '16 at 10:30
  • This also works in Swift, on iOS 11. I also had to override `-touchesBegan:withEvent` and call `-becomeFirstResponder`. – joerick Oct 24 '17 at 15:13
4

set the tintColor to Clear Color

textfield.tintColor = [UIColor clearColor];

and you can also set from the interface builder

Ahmad Al-Attal
  • 435
  • 4
  • 13
2

If you want to hide cursor, you can easily use this! It worked for me..

[[textField valueForKey:@"textInputTraits"] setValue:[UIColor clearColor] forKey:@"insertionPointColor"]
Göktuğ Aral
  • 1,409
  • 1
  • 13
  • 28
  • 3
    This is undocumented, as far as I know. It's possible that using this will get your app rejected for calling private APIs if you submit it to the app store, though I don't know of any submissions using this to test that speculation one way or another. It's a pity, because it'd be nice to be able to solve this problem without subclassing, and this answer enables that. – Mark Amery Oct 01 '13 at 22:30
1

To both disable cursor and menu I use subclass with these 2 methods:

- (CGRect)caretRectForPosition:(UITextPosition *)position {
    return CGRectZero;
}

- (BOOL)canPerformAction:(SEL)action withSender:(id)sender {
    [UIMenuController sharedMenuController].menuVisible = NO;
    self.selectedTextRange = nil;

    return NO;
}
Nat
  • 12,032
  • 9
  • 56
  • 103
1

Answer provided by the OP, copied from the question body to help clean up the ever growing tail of unanswered questions.

I think I have the correct solution but If it can be improved will be welcome :) Well, I made a subclass of UITextField and overriden the method that returns the CGRect for the bounds

-(CGRect)textRectForBounds:(CGRect)bounds {
    return CGRectZero;
}

The problem? The text doesn't show because the rect is zero. But I added an UILabel as a subview of the control and overridden the setText method so, as we enter a text as usual, the text field text is nil and is the label which shows the text

- (void)setText:(NSString *)aText {
    [super setText:nil];

    if (aText == nil) {
        textLabel_.text = nil;
    }

    if (![aText isEqualToString:@""]) {
        textLabel_.text = aText;
    }
}

With this the thing works as expected. Have you know any way to improve it?

Robert Höglund
  • 10,010
  • 13
  • 53
  • 70
  • 1
    This answer is basically worthless now given that Joseph Chiu's alternative approach is very similar but much simpler. May I suggest just deleting it? – Mark Amery Oct 01 '13 at 22:39
0

If you like cleaner = less code, use the interface builder:

Clear color

(Attributes inspector, view section.)

Display Name
  • 4,502
  • 2
  • 47
  • 63
0

In my case, overriding the caret rect wasn't enough. On iOS 15, the caret didn't appear, effectively, but the selection handles did.

Solved it with: override var canBecomeFirstResponder: Bool { return false } on the UITextView subclass.

0

I simply subclass UITextField, and override layoutSubviews as follows:

- (void)layoutSubviews
{
    [super layoutSubviews];
    for (UIView *v in self.subviews)
    {
        if ([[[v class] description] rangeOfString:@"UITextSelectionView"].location != NSNotFound)
        {
            v.hidden = YES;
        }
    }
}

It's a dirty hack, and may fail in the future (at which point the cursor will be visible again - your app won't crash), but it works.

Mark Beaton
  • 2,657
  • 3
  • 23
  • 33
-1

You can add a BOOL cursorless property to UITextField in a category via associated objects.

@interface UITextField (Cursorless)

@property (nonatomic, assign) BOOL cursorless;

@end

Then use method swizzling to swizzle caretRectForPosition: with a method that toggles between CGRectZero and its default value using cursorless.

This leads to a simple interface via a drop-in category. This is demonstrated in the following files.

Simply drop them in and get the benefit of this simple interface

UITextField category: https://github.com/rexmas/RexDK/blob/master/RexDK/UI/UITextField%2BRXCursorless.h https://github.com/rexmas/RexDK/blob/master/RexDK/UI/UITextField%2BRXCursorless.m

Method Swizzling: https://github.com/rexmas/RexDK/blob/master/RexDK/Foundation/NSObject%2BRXRuntimeAdditions.h https://github.com/rexmas/RexDK/blob/master/RexDK/Foundation/NSObject%2BRXRuntimeAdditions.m

Awesome-o
  • 2,002
  • 1
  • 26
  • 38
  • a bad solution on SO many levels! for starters: Never override methods in categories. Avoid Method Swizziling unless you really, really need it (other have come up with good solutions to this question). It's makes your code so much more complicated. – EsbenB Aug 07 '14 at 14:15
  • If you actually looked at the code you'd realize that no method is overridden in the category and method swizzling is implemented correctly. I've been using this implementation for years without fail. – Awesome-o Aug 07 '14 at 18:43
  • Also, explain what's complicated about adding an isolated code base of ~150 lines to permanently solve a continuously recurring problem? Not only will this NOT complicate your codebase but it provides the simplest possible interface; a single boolean to dictate the functionality. – Awesome-o Aug 07 '14 at 18:48
  • take a look at this excellent discussion about method swizzling http://stackoverflow.com/questions/5339276/what-are-the-dangers-of-method-swizzling-in-objective-c Especially on debugging. Method swizzling is an amazing feature, but IMHO should only be used when needed. This problem is easy to solve using one of the suggestions provided here, and will provide code easier to maintain and read for other programmers. – EsbenB Aug 07 '14 at 18:54
  • I've read that before and I followed all the outlined guidelines in my implementation for correctness. Arguably, this is a solid example of when method swizzling wins out. It's an isolated, yet, common case of where the basic libraries fail to implement a widely needed feature across platforms in a simple manner. The other examples suggested do not semantically define a cursorless textfield, they only perform it through obfuscation of either the frame of the cursor or it's color. To a new programmer, this interface is the easiest to read and does exactly what is expected of it. – Awesome-o Aug 07 '14 at 19:04
  • To follow up on another one of your points, the code base is arguably very small and thus easy to debug. And you can simply ignore the implementation and everything still works as-is. – Awesome-o Aug 07 '14 at 19:05
  • Take a look at Joseph Chiu's answer. He is providing the same functionality, just as a subclass. It is then clear and concise for other programmers what's going on, when the read your code. – EsbenB Aug 07 '14 at 19:11
  • Yes, and this option doesn't require a whole new subclass, on top of that it's not leaky so you can subclass to your hearts content and still get the value of this interface. Composition vs Inheritance, I'm sure you're already aware of the benefits. – Awesome-o Aug 07 '14 at 19:15