Метод делегата iOS 13.1 UITextView должен вызываться при прокрутке вложения

15

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

func textView(_ textView: UITextView,
                  shouldInteractWith URL: URL,
                  in characterRange: NSRange,
                  interaction: UITextItemInteraction) -> Bool

В iOS 13 этот метод делегата вызывается, даже когда пользователь просто прокручивает URL, что не ожидается. Это поведение также применяется к прикреплению изображения.

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

* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 6.1 7.1
  * frame #0: 0x0000000104a54c5c ProjectS1`PostListViewController.textView(textView=0x00000001090a4600, URL=Foundation.URL @ 0x000000016b5d1200, characterRange=location=161, length=9, interaction=invokeDefaultAction, self=0x0000000109b03990) at PostListViewController.swift:610:9
    frame #1: 0x0000000104a54d70 ProjectS1`@objc PostListViewController.textView(_:shouldInteractWith:in:interaction:) at <compiler-generated>:0
    frame #2: 0x00000001b3293eec UIKitCore`-[UITextView _allowInteraction:forTextInteractableItem:] + 212
    frame #3: 0x00000001b2602160 UIKitCore`-[_UITextInteractableItem _allowInteraction:] + 140
    frame #4: 0x00000001b2601f68 UIKitCore`-[_UITextInteractableItem canInvokeDefaultAction] + 100
    frame #5: 0x00000001b31dd528 UIKitCore`-[_UITextSimpleLinkInteraction _canBeginInteractionSessionForLinkAtPoint:asTap:] + 136
    frame #6: 0x00000001b31dd3d0 UIKitCore`-[_UITextSimpleLinkInteraction interaction_gestureRecognizer:shouldReceiveTouch:] + 228
    frame #7: 0x00000001b31dc234 UIKitCore`-[UITextInteraction gestureRecognizer:shouldReceiveTouch:] + 144
    frame #8: 0x00000001b2b5f460 UIKitCore`-[UIGestureRecognizer _delegateShouldReceiveTouch:] + 452
    frame #9: 0x00000001b2b5edf4 UIKitCore`-[UIGestureRecognizer _shouldReceiveTouch:forEvent:recognizerView:] + 488
    frame #10: 0x00000001b2ffa630 UIKitCore`__56-[UITouchesEvent _addGestureRecognizersForView:toTouch:]_block_invoke + 332
    frame #11: 0x00000001b2ffa0e4 UIKitCore`__62-[UITouchesEvent _collectGestureRecognizersForView:withBlock:]_block_invoke + 408
    frame #12: 0x00000001b2ff9b58 UIKitCore`-[UITouchesEvent _collectGestureRecognizersForView:withBlock:] + 308
    frame #13: 0x00000001b2ffa4b0 UIKitCore`-[UITouchesEvent _addGestureRecognizersForView:toTouch:] + 164
    frame #14: 0x00000001b2ffa9a8 UIKitCore`-[UITouchesEvent _addTouch:forDelayedDelivery:] + 812
    frame #15: 0x00000001b300bfac UIKitCore`_AddTouchToEventAndDetermineIfNeedsCancel + 196
    frame #16: 0x00000001b300c074 UIKitCore`____updateTouchesWithDigitizerEventAndDetermineIfShouldSend_block_invoke.96 + 136
    frame #17: 0x00000001aef35b20 CoreFoundation`__NSDICTIONARY_IS_CALLING_OUT_TO_A_BLOCK__ + 24
    frame #18: 0x00000001aef360e4 CoreFoundation`____NSDictionaryEnumerate_block_invoke.11 + 56
    frame #19: 0x00000001aef07a10 CoreFoundation`CFBasicHashApply + 144
    frame #20: 0x00000001aef35c80 CoreFoundation`__NSDictionaryEnumerate + 220
    frame #21: 0x00000001b300d560 UIKitCore`__dispatchPreprocessedEventFromEventQueue + 2444
    frame #22: 0x00000001b30107dc UIKitCore`__handleEventQueueInternal + 4928
    frame #23: 0x00000001b3009960 UIKitCore`__handleHIDEventFetcherDrain + 112
    frame #24: 0x00000001aee61260 CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 28
    frame #25: 0x00000001aee611b4 CoreFoundation`__CFRunLoopDoSource0 + 84
    frame #26: 0x00000001aee60920 CoreFoundation`__CFRunLoopDoSources0 + 184
    frame #27: 0x00000001aee5b7ec CoreFoundation`__CFRunLoopRun + 1068
    frame #28: 0x00000001aee5b098 CoreFoundation`CFRunLoopRunSpecific + 480
    frame #29: 0x00000001b8fc5534 GraphicsServices`GSEventRunModal + 108
    frame #30: 0x00000001b2f7b7ac UIKitCore`UIApplicationMain + 1940
    frame #31: 0x0000000104b090d0 ProjectS1`main at AppDelegate.swift:25:7
    frame #32: 0x00000001aecdaf30 libdyld.dylib`start + 4

Поэтому мой вопрос заключается в том, есть ли способ узнать, нажимает ли пользователь на URL-адрес или просто прокручивает его?

kukushi
источник
1
Это «известная» проблема в iOS 13.1. Известно, что разработчики писали об этом в Твиттере, и некоторые из нас подали сообщения об ошибках. Начиная с iOS 13.2, бета-версия 1 все еще не исправлена. Подайте ошибку и выберите обходной путь - зависит от вас и зависит от того, как работает ваше приложение - вы можете либо ничего не делать и просто ждать, либо попробовать обходной путь (например, описанный в этой теме - twitter.com/scelis/status/ 1176676097754198017 ). В некоторых местах я ничего не делал, а в других местах я удалил UITextViewDelegate и позволил поведению по умолчанию вступить во владение (открытие ссылок в Safari).
Бапап

Ответы:

15

Похоже, в textView:shouldInteractWithURL:inRange:interaction:конечном итоге вызывается 3 раза при обычном нажатии ссылки (если вы всегда возвращаетеYES ).

Пару раз, чтобы увидеть, можно ли вызвать действие по умолчанию (при условии, что это то canInvokeDefaultAction, о чем идет речь):

  * frame #0: 0x0000000101610038 Engage`::-[BubbleMessageCell textView:shouldInteractWithURL:inRange:interaction:](self=0x00007ff19e999600, _cmd="textView:shouldInteractWithURL:inRange:interaction:", textView=0x00007ff19eaf2000, url="https://9to5mac.com/2019/09/07/imessage-for-business/", characterRange=location=235, length=33, interaction=UITextItemInteractionInvokeDefaultAction) at BubbleMessageCell.mm:623:5
    frame #1: 0x00007fff478b2902 UIKitCore`-[UITextView _allowInteraction:forTextInteractableItem:] + 532
    frame #2: 0x00007fff46b8ba1c UIKitCore`-[_UITextInteractableItem _allowInteraction:] + 135
    frame #3: 0x00007fff46b8b84d UIKitCore`-[_UITextInteractableItem canInvokeDefaultAction] + 97
    frame #4: 0x00007fff477f76f7 UIKitCore`-[_UITextSimpleLinkInteraction _canBeginInteractionSessionForLinkAtPoint:asTap:] + 127
    frame #5: 0x00007fff477f75c7 UIKitCore`-[_UITextSimpleLinkInteraction interaction_gestureRecognizer:shouldReceiveTouch:] + 217
    frame #6: 0x00007fff477f6506 UIKitCore`-[UITextInteraction gestureRecognizer:shouldReceiveTouch:] + 127
    frame #7: 0x00007fff47133669 UIKitCore`-[UIGestureRecognizer _delegateShouldReceiveTouch:] + 493
  * frame #0: 0x0000000101610038 Engage`::-[BubbleMessageCell textView:shouldInteractWithURL:inRange:interaction:](self=0x00007ff19e999600, _cmd="textView:shouldInteractWithURL:inRange:interaction:", textView=0x00007ff19eaf2000, url="https://9to5mac.com/2019/09/07/imessage-for-business/", characterRange=location=235, length=33, interaction=UITextItemInteractionInvokeDefaultAction) at BubbleMessageCell.mm:623:5
    frame #1: 0x00007fff478b2902 UIKitCore`-[UITextView _allowInteraction:forTextInteractableItem:] + 532
    frame #2: 0x00007fff46b8ba1c UIKitCore`-[_UITextInteractableItem _allowInteraction:] + 135
    frame #3: 0x00007fff46b8b84d UIKitCore`-[_UITextInteractableItem canInvokeDefaultAction] + 97
    frame #4: 0x00007fff477f77fb UIKitCore`-[_UITextSimpleLinkInteraction _beginInteractionSessionForLinkAtPoint:asTap:] + 167
    frame #5: 0x00007fff477f74d0 UIKitCore`-[_UITextSimpleLinkInteraction interaction_gestureRecognizerShouldBegin:] + 196
    frame #6: 0x00007fff477f62a1 UIKitCore`-[UITextInteraction gestureRecognizerShouldBegin:] + 307
    frame #7: 0x00007fff471339b6 UIKitCore`-[UIGestureRecognizer _shouldBegin] + 413

И, наконец, когда жест фактически распознается:

  * frame #0: 0x0000000101610038 Engage`::-[BubbleMessageCell textView:shouldInteractWithURL:inRange:interaction:](self=0x00007ff19e999600, _cmd="textView:shouldInteractWithURL:inRange:interaction:", textView=0x00007ff19eaf2000, url="https://9to5mac.com/2019/09/07/imessage-for-business/", characterRange=location=235, length=33, interaction=UITextItemInteractionInvokeDefaultAction) at BubbleMessageCell.mm:623:5
    frame #1: 0x00007fff478b2902 UIKitCore`-[UITextView _allowInteraction:forTextInteractableItem:] + 532
    frame #2: 0x00007fff46b8ba1c UIKitCore`-[_UITextInteractableItem _allowInteraction:] + 135
    frame #3: 0x00007fff46b8b8cc UIKitCore`-[_UITextInteractableItem invokeDefaultAction] + 94
    frame #4: 0x00007fff477f6f32 UIKitCore`-[_UITextSimpleLinkInteraction linkTapped:] + 188
    frame #5: 0x00007fff4712bbfb UIKitCore`-[UIGestureRecognizerTarget _sendActionWithGestureRecognizer:] + 44

При прокрутке происходит только первый звонок.

Это, по-видимому, изменение iOS 13.1, где он проверяет раньше, чтобы увидеть, может ли ссылка взаимодействовать. Если хочешьtextView:shouldInteractWithURL:inRange:interaction: иметь побочные эффекты, вы должны делать их только тогда, когда жест фактически распознается.

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

    BOOL recognizedTapGesture = NO;
    for (UIGestureRecognizer *recognizer in textView.gestureRecognizers) {
        if ([recognizer isKindOfClass:UITapGestureRecognizer.class] && recognizer.state == UIGestureRecognizerStateEnded) {
            recognizedTapGesture = YES;
            break;
        }
    }
    if (!recognizedTapGesture) {
        // Tap gesture is not being recognized, this must be an early 
        // check when touches begin. Leave the link handling alone.
        return YES;
    }

    // Do custom action here

    return NO;
Михай Парпарита
источник
1
Спасибо! Ваше решение прекрасно работает по ссылке. Как насчет вложения изображения? Прокрутка или нажатие на вложение изображения имеет тот же стек вызовов, и проверка состояния распознавателя жестов не работает с вложением.
Кукуши
5

Я только что столкнулся с той же разочаровывающей проблемой в iOS 13. Вот исправление, которое сработало для меня в Swift, вдохновленное ответом Михая выше.

func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool {

    switch interaction {
    case .invokeDefaultAction:
        if textView.gestureRecognizers?.contains(where: {$0.isKind(of: UITapGestureRecognizer.self) && $0.state == .ended}) == true {

            // Handle your custom logic here.

            return false
        }
        return true
    case .presentActions:

        // Default action.

        return true
    case .preview:

        // Default action.

        return true
    @unknown default:
        fatalError()
    }
}
de3z1e
источник
Это не работает на iPad. Решено с подключением другого UITapGestureRecognizer.
ccoroom