Скрыть курсор UITextField


137

Я использую a UITextFieldс a UIPickerViewвместо него inputView, так что, когда пользователь нажимает на текстовое поле, вызывается средство выбора, из которого он может выбрать вариант.

Почти все работает, но у меня есть одна проблема: курсор все еще мигает в текстовом поле, когда он активен, что некрасиво и неуместно, поскольку пользователь не должен вводить данные в поле и не представлен с помощью клавиатуры. Я знаю, что мог бы решительно решить эту проблему, установив editingдля NOнего текстовое поле и отслеживая прикосновения к нему, или заменив его кнопкой с пользовательским стилем, и вызвав средство выбора с помощью кода. Однако я хочу использовать эти UITextFieldDelegateметоды для всей обработки событий в текстовом поле, и такие хаки, как замена текстового поля на кнопку, не допускают такой подход.

Как я могу просто спрятать курсор UITextFieldвместо?

Ответы:


277

Просто создайте подкласс UITextField и переопределите caretRectForPosition

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

2
Прекрасный! Работает как шарм для меня.
Joe Strout

1
@ Джозеф Чиу: Отлично работает. Но не с iOS 4.3. Не могли бы вы помочь мне с этим?
Динеш Раджа

самый чистый и самый короткий подход , который заслуживает двойной upvote =)
Ilker Baltaci

1
Просто записка. В моем подклассе я добавил bool hideCaret, а затем в этом переопределении. Если это правда -> return CGRectZero иначе вернет результат super.
WCByrne

6
Просто имейте в виду, что пользователи с внешней клавиатурой могут изменить значение текстового поля, даже если курсор скрыт, и вы используете представление выбора.
Марк

156

Начиная с iOS 7, теперь вы можете просто установить tintColor = [UIColor clearColor]в textField, и курсор исчезнет.


1
В настоящее время это работает, однако я бы не советовал использовать его, поскольку в будущем это может измениться. Скорее выберите caretRectForPosition:переопределить решение.
lipka

@lipka Правда, это, вероятно, лучший способ.
jamone 08

2
Пока достаточно. Иногда вам просто нужно быстрое решение.
GoldenJoe

Это самое простое решение на сегодняшний день!
Джей Q.

1
Этот ответ должен быть одобрен для iOS 7+
трип

95

Вы можете просто очистить tintColor текстового поля

self.textField.tintColor = [UIColor clearColor];

Swift 3.0

self.textField.tintColor = .clear

введите описание изображения здесь


Лучший простой ответ.
Ник Ков

2
Как упоминалось выше, очистка цвета оттенка не мешает пользователям с внешней клавиатурой (iPad Pro) изменять текст.
Майкл Лонг

@MichaelLong - это не означает, что пользователь не может изменить текст, это просто выбор стиля.
JRam13

21

Возможно, вы также захотите запретить пользователю выбирать, копировать или вставлять любой текст, чтобы единственный ввод текста происходил из представления выбора.

- (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/


15

Проверьте свойство selectedTextRange из протокола UITextInput , к которому классу UITextField соответствует установленным требованиям. Несколько! Вот и урок объектно-ориентированного программирования.

Скрыть каретку

Чтобы скрыть курсор, обнулите выделенный текстовый диапазон текстового поля.

textField.selectedTextRange = nil; // hides caret

Unhide Caret

Есть два способа показать курсор.

  1. Установите выделенный текстовый диапазон текстового поля в конец документа.

    UITextPosition *end = textField.endOfDocument;
    textField.selectedTextRange = [textField textRangeFromPosition:end
                                                        toPosition:end];
  2. Чтобы курсор оставался в том же месте, сначала сохраните выделенный текстовый диапазон текстового поля в переменной экземпляра.

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

    Затем, когда вы хотите отобразить каретку, просто установите выделенный текстовый диапазон текстового поля обратно на то, что было изначально:

    textField.selectedTextRange     = _textFieldSelectedTextRange;
    _textFieldLastSelectedTextRange = nil;

3
Это конкретное решение не сработало для моей реализации. Курсор все еще мигает.
Art Geigel

Что ж, тогда, возможно, вам следует сообщить об ошибке на bugreport.apple.com, потому что в документации iOS говорится: «Если текстовый диапазон имеет длину, он указывает текущий выбранный текст. Если он имеет нулевую длину, он указывает на курсор (вставка point). Если объект текстового диапазона равен нулю, это указывает на отсутствие текущего выделения ".
ma11hew28

4
Меня не волнует достаточно, чтобы подать отчет. Если другие используют ваше «решение» и не видят, что оно работает, я хотел, чтобы они знали, что они не одиноки.
Арт Гайгель

Несмотря на комментарии @ ArtGeigel, это прекрасно работает для меня. Однако я предпочитаю решение с переопределением caretRectForPosition. Это более ясно, что он делает, и документы, которые вы процитировали, не проясняют, каким должно быть поведение курсора, когда нет «текущего выбора». Если бы утверждение @ ArtGeigel о том, что это не работает, было правильным (а это не так, по крайней мере, насколько я могу судить), не было бы ясно, что это была ошибка.
Марк Амери

У меня тоже не сработало. Карет все еще там и мигает.
CW0007007

11

Ответ предоставлен ОП, скопированный из тела вопроса, чтобы помочь очистить постоянно растущий хвост безответных вопросов.

Я нашел другое решение: создать подкласс UIButtonи переопределить эти методы

- (UIView *)inputView {
    return inputView_;
}

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

- (BOOL)canBecomeFirstResponder {
    return YES;
}

Теперь кнопка, как UIResponder, имеет то же поведение, что UITextFieldи довольно простая реализация.


5
Это не очень хорошее решение. Посмотрите этот ответ ниже: stackoverflow.com/a/13660503/1103584
DiscDev

2
Почему это не лучшее решение? Он достигает желаемого эффекта, а также предоставляет функциональность классу, который ранее не имел его. Это тоже не взлом. Я думаю, это действительно круто. Так что с этим не так?
BreadicalMD

2
@BreadicalMD Самая большая проблема, которую я вижу, заключается в том, что вы не можете использовать a UITextFieldDelegateс этим для обработки событий начала и конца редактирования. Вместо этого - если нет способа обработать те события, о которых я не знаю, - вам нужно переопределить becomeFirstResponderи resignFirstResponderв подклассе кнопки, и, возможно, создать свой собственный протокол делегата, добавить delegateсвойство и вызвать делегата из вышеупомянутого методы. Это гораздо больше, чем просто переопределение caretRectForPositionв UITextFieldподклассе.
Марк Эмери

1
@BreadicalMD Тем не менее, несмотря на то, что ответ Джозефа Чиу превосходен с любой практической точки зрения, я все же согласен с вами, что это довольно сексуально. Раньше я никогда внимательно не изучал UIResponderссылку на класс и понятия не имел, что подобный трюк возможен.
Марк Эмери

Поздно к вечеринке, но я думаю, что это очень хорошее решение, и оно кажется гораздо более подходящим и менее хакерским, чем использование UITextFieldи скрытие курсора, что, по сути, является хакерством, поскольку затем мы используем текстовое поле в качестве метки, пока не использовать какие-либо функции UITextField.
Руперт

7

Swift 5 версия сообщения Net

  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
  }

В моем случае это сработало. Добавлен подкласс UITextField с этими методами с использованием Swift 4. Спасибо!
J. Fdez

3

установите tintColor на Clear Color

textfield.tintColor = [UIColor clearColor];

и вы также можете установить из конструктора интерфейса


2
Вы только что скопировали ответ более 2 лет назад.
Эшли Миллс

1
извини мой друг, но я ничего не копировал.
Ахмад аль-Атталь

4
Это интересно, "мой друг". Ваш ответ выглядит довольно близко к ответу @ oldman от 1 мая 15 года. Можете сказать мне, чем ваш отличается?
Эшли Миллс

1
Как упоминалось выше, очистка цвета оттенка не мешает пользователям с внешней клавиатурой (iPad Pro) изменять текст.
Майкл Лонг

Профессиональные пользователи могут делать то, что им нравится. Они профи: они знают, что делают; ^)
Антон Тропашко 05

2

Если вы хотите скрыть курсор, вы можете легко использовать это! Это сработало для меня ..

[[textField valueForKey:@"textInputTraits"] setValue:[UIColor clearColor] forKey:@"insertionPointColor"]

3
Насколько я знаю, это без документов. Возможно, что при его использовании ваше приложение будет отклонено для вызова частных API-интерфейсов, если вы отправите его в магазин приложений, хотя я не знаю каких-либо представлений, использующих это, чтобы так или иначе проверить это предположение. Жаль, потому что было бы неплохо решить эту проблему без создания подклассов, и этот ответ позволяет это сделать.
Марк Эмери

1

Ответ предоставлен ОП, скопированный из тела вопроса, чтобы помочь очистить постоянно растущий хвост безответных вопросов.

Я думаю, что у меня есть правильное решение, но если оно может быть улучшено, будет приветствоваться :) Хорошо, я сделал подкласс UITextField и переопределил метод, который возвращает CGRect для границ

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

Эта проблема? Текст не отображается, потому что прямоугольник равен нулю. Но я добавил UILabel в качестве подпредставления элемента управления и переопределил метод setText, поэтому, когда мы вводим текст, как обычно, текст текстового поля равен нулю и является меткой, которая показывает текст

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

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

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

С этим все работает как положено. Вы знаете, как его улучшить?


Этот ответ в основном бесполезен, учитывая, что альтернативный подход Джозефа Чиу очень похож, но намного проще. Могу ли я предложить просто удалить его?
Марк Эмери

1

Чтобы отключить курсор и меню, я использую подкласс с этими двумя методами:

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

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

    return NO;
}

0

Я просто подкласс UITextFieldи переопределить layoutSubviewsследующим образом:

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

Это грязный взлом, и в будущем он может потерпеть неудачу (в этот момент курсор снова станет видимым - ваше приложение не выйдет из строя), но он работает.


-1

Вы можете добавить BOOL cursorlessсвойство UITextFieldв категорию через связанные объекты.

@interface UITextField (Cursorless)

@property (nonatomic, assign) BOOL cursorless;

@end

Затем используйте метод swizzling для caretRectForPosition:переключения между методом, который переключается между CGRectZeroего значением по умолчанию cursorless.

Это приводит к простому интерфейсу через выпадающую категорию. Это продемонстрировано в следующих файлах.

Просто вставьте их и получите преимущество этого простого интерфейса

UITextFieldкатегория: https://github.com/rexmas/RexDK/blob/master/RexDK/UI/UITextField%2BRXCursorless.h https://github.com/rexmas/RexDK/blob/master/RexDK/UI/UITextField%2BRXCursorless .m

Метод 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


плохое решение на многих уровнях! для начинающих: никогда не переопределять методы в категориях. Избегайте использования метода Swizziling, если он вам действительно не нужен (другие придумали хорошие решения на этот вопрос). Это делает ваш код намного сложнее.
EsbenB

Если вы на самом деле посмотрели на код, то поймете, что ни один метод не переопределяется в категории, а переключение методов реализовано правильно. Я использую эту реализацию в течение многих лет в обязательном порядке.
Великолепно,

Также объясните, что сложного в добавлении изолированной кодовой базы из ~ 150 строк для постоянного решения постоянно повторяющейся проблемы? Это не только не усложнит вашу кодовую базу, но и обеспечит максимально простой интерфейс; один логический, чтобы диктовать функциональность.
Awesome-o

взгляните на это отличное обсуждение метода swizzling stackoverflow.com/questions/5339276/… особенно по отладке. Свиззлинг метода - замечательная функция, но ИМХО следует использовать только при необходимости. Эту проблему легко решить, воспользовавшись одним из предложенных здесь советов, и он облегчит сопровождение и чтение кода другими программистами.
EsbenB

Я читал это раньше и следовал всем изложенным рекомендациям в моей реализации для корректности. Можно утверждать, что это хороший пример того, когда победит метод метания. Это изолированный, но общий случай, когда базовые библиотеки не могут реализовать столь необходимую функцию на разных платформах простым способом. Другие предложенные примеры не определяют семантически текстовое поле без курсора, они выполняют его только через запутывание либо рамки курсора, либо его цвета. Новому программисту этот интерфейс легче всего читать, и он делает именно то, что от него ожидается.
Великолепно,
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.