Подтвердить что ты не робот

UITextView с интерактивными ссылками, но без выделения текста

У меня есть UITextView, отображающий не редактируемый текст. Я хочу, чтобы текст автоматически анализировал ссылки, номера телефонов и т.д. Для пользователя, а также те, которые можно было бы щелкнуть.

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

Для того, чтобы ссылки обрабатывались в iOS7, переключатель Selectable должен быть включен для UITextView, но Selectable также позволяет выделять, чего я не хочу.

Я попытался переопределить жест LongPress, чтобы предотвратить выделение, но, похоже, отключил обычные нажатия на ссылки...

for (UIGestureRecognizer *recognizer in cell.messageTextView.gestureRecognizers) {
    if ([recognizer isKindOfClass:[UILongPressGestureRecognizer class]]){
        recognizer.enabled = NO;
    }
    if ([recognizer isKindOfClass:[UITapGestureRecognizer class]]){
        recognizer.enabled = YES;
    }
}

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

4b9b3361

Ответ 1

Я работаю над одной и той же проблемой, и самое лучшее, что я мог сделать, это мгновенно очистить выбор, как только это будет сделано, добавив следующее в делегат UITextView:

- (void)textViewDidChangeSelection:(UITextView *)textView {
    if(!NSEqualRanges(textView.selectedRange, NSMakeRange(0, 0))) {
        textView.selectedRange = NSMakeRange(0, 0);
    }
}

Обратите внимание на проверку, чтобы предотвратить рекурсию. Это в значительной степени решает проблему, потому что только выбор отключен - ссылки будут по-прежнему работать.

Другая тангенциальная проблема заключается в том, что текстовое представление по-прежнему станет первым ответчиком, который вы можете исправить, установив желаемого первого ответчика после установки выбранного диапазона.

Примечание: единственная видимая странность, которая остается, заключается в том, что нажатие и удержание вызывает увеличительное стекло.

Ответ 2

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

Мне просто пришлось переопределить -canBecomeFirstResponder и вернуть NO.

@interface MYTextView : UITextView
@end

@implementation MYTextView

- (BOOL)canBecomeFirstResponder {
    return NO;
}

@end

Ответ 3

Как я писал на другом посту, есть еще одно решение.

После нескольких тестов я нашел решение.

Если вы хотите, чтобы ссылки активны, и вы не включили выделение, вам необходимо изменить gestureRecognizers.

Например, есть 3 указателя LongPressGestureRecognizers. Один для клика по ссылке (minimumPressDuration = 0.12), второй для увеличения редактируемого режима (минимумPressDuration = 0,5), третий для выбора (минимумPressDuration = 0,8). Это решение удаляет LongPressGestureRecognizer для выбора и второго для масштабирования в режиме редактирования.

NSArray *textViewGestureRecognizers = self.captionTextView.gestureRecognizers;
NSMutableArray *mutableArrayOfGestureRecognizers = [[NSMutableArray alloc] init];
for (UIGestureRecognizer *gestureRecognizer in textViewGestureRecognizers) {
    if (![gestureRecognizer isKindOfClass:[UILongPressGestureRecognizer class]]) {
        [mutableArrayOfGestureRecognizers addObject:gestureRecognizer];
    } else {
        UILongPressGestureRecognizer *longPressGestureRecognizer = (UILongPressGestureRecognizer *)gestureRecognizer;
        if (longPressGestureRecognizer.minimumPressDuration < 0.3) {
            [mutableArrayOfGestureRecognizers addObject:gestureRecognizer];
        }
    }
}
self.captionTextView.gestureRecognizers = mutableArrayOfGestureRecognizers;

Протестировано на iOS 9, но оно должно работать на всех версиях (iOS 7, 8, 9). Я надеюсь, что это помогает!:)

Ответ 4

Вот что сработало для меня.

Я не мог избавиться от увеличительного стекла, но это позволит вам сохранить возможность просмотра текста (так что вы можете использовать ссылки), но избавиться от всего пользовательского интерфейса, связанного с выбором. Проверено только на iOS 9.

Внимание! Swift ниже!

Сначала, подкласс UITextView и включите эту функцию:

override func canPerformAction(action: Selector, withSender sender: AnyObject?) -> Bool {
    return false
}

Это отключит меню копирования и т.д. Затем я включаю метод установки, который я вызываю из init, где я делаю кучу связанных с установкой задач. (Я использую только эти текстовые виды из раскадровки, таким образом, init init):

required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
    setup()
}

private func setup() {
    selectable = true
    editable = false
    tintColor = UIColor.clearColor()
}

Выбирается = true, чтобы ссылки были недоступными, editable = false, потому что ссылки не отображаются в редактируемом текстовом представлении. Указание четкости tintColor скрывает синие полосы, которые появляются в начале и конце выделения.

Наконец, в контроллере, использующем текстовое представление подкласса, убедитесь, что включен протокол UITextViewDelegate, что делегат установлен textView.delegate = self и реализует эту функцию делегата:

func textViewDidChangeSelection(textView: UITextView) {
    var range = NSRange()
    range.location = 0
    range.length = 0
    textView.selectedRange = range
}

Без этой функции панели выбора и контекстное меню будут отключены, но цветной фон по-прежнему останется за выбранным текстом. Эта функция избавляется от фона выбора.

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

Ответ 5

Это в значительной степени решает проблему, поскольку выбор текста отключен и скрывает увеличительное стекло - ссылки все равно будут работать.

func textViewDidChangeSelection(_ textView: UITextView) {
    if let gestureRecognizers = textView.gestureRecognizers {
        for recognizer in gestureRecognizers {
            if recognizer is UILongPressGestureRecognizer {
                if let index = textView.gestureRecognizers?.index(of: recognizer) {
                    textView.gestureRecognizers?.remove(at: index)
                }
            }
        }
    }
}

Примечание. Вместо удаления вы можете заменить распознаватель на свой желаемый.

Ответ 6

Хотя он, по общему признанию, хрупкий перед лицом возможных будущих изменений в реализации, подход Kubík Kašpar - единственный, который работал на меня.

Но (а) это может быть упрощено, если вы подклассом UITextView и (b), если единственное взаимодействие, которое вы хотите разрешить, - это нажатие ссылки, вы можете сразу же распознать кран:

@interface GMTextView : UITextView
@end

@implementation GMTextView

- (void)addGestureRecognizer:(UIGestureRecognizer *)gestureRecognizer {

  // discard all recognizers but the one that activates links, by just not calling super
  // (in iOS 9.2.3 a short press for links is 0.12s, long press for selection is 0.75s)

  if ([gestureRecognizer isMemberOfClass:UILongPressGestureRecognizer.class] &&
      ((UILongPressGestureRecognizer*)gestureRecognizer).minimumPressDuration < 0.25) {  

    ((UILongPressGestureRecognizer*)gestureRecognizer).minimumPressDuration = 0.0;
    [super addGestureRecognizer:gestureRecognizer]; 
  }
}

@end

Ответ 7

Здесь используется подкласс UITextView, который будет анализировать его распознаватели жестов и разрешать только те, которые взаимодействуют со связанным текстом (используя Swift 3).

class LinkTextView: UITextView {
    override func gestureRecognizerShouldBegin(_ gesture: UIGestureRecognizer) -> Bool {
        let tapLocation = gesture.location(in: self).applying(CGAffineTransform(translationX: -textContainerInset.left, y: -textContainerInset.top))
        let characterAtIndex = layoutManager.characterIndex(for: tapLocation, in: textContainer, fractionOfDistanceBetweenInsertionPoints: nil)
        let linkAttributeAtIndex = textStorage.attribute(NSLinkAttributeName, at: characterAtIndex, effectiveRange: nil)

        // Returns true for gestures located on linked text
        return linkAttributeAtIndex != nil
    }

    override func becomeFirstResponder() -> Bool {
        // Returning false disables double-tap selection of link text
        return false
    }
}