1

I have a custom text input view that adopts the UITextInput protocol. This view is embedded within a UIScrollView. The UITextinput adopting view includes UITextInteraction. When I drag the selection handles to the edges (top or bottom) of the current visible area, the view should scroll and select text automatically (like UITextView). Does anybody know how to achieve this?

I was hoping that UITextInteraction might inform me via its delegate of necessary events, but it does not (or even takes care of this functionality automatically).

I have tried to intercept closestPosition(to point: CGPoint) -> UITextPosition?, which is called whenever the user touches the UITextInput adopting view. Therefore, I can use it to track the dragging operation of a selection handle. Once, the user reaches the top of the view, I scroll up. However, I cannot detect when the user lets go of the handle (when touch ends). When this happens, the scrollView should stop scrolling. In my case, the scroll view keeps scrolling to the top.

I also tried to intercept selectionRects(for range: UITextRange) -> [UITextSelectionRect], but it is called sporadically during a scrolling.

I also cannot detect touchesEnded(). The UITextInteraction seems to block the call. Further, I cannot implement my own pan gesture. UITextInteraction blocks this as well during a selection operation.

Has anybody successfully used UITextInteraction? It seems very pre-mature at this stage.

KnutKnatter
  • 61
  • 1
  • 2

2 Answers2

0

Here's the way I got this info out of UITextInteraction for the text insertion point placement. I have not yet attempted to track the selection handles, but will update the answer if I manage it

UITextInteraction has a property called gesturesForFailureRequirements. If you dump the content of these you'll notice one named UIVariableDelayLoupeGesture which is a subclass of UILongPressGestureRecognizer. This is the one we want when the use "picks up" the cursor with their finger

I add my own target/selector to this gesture recogniser when adding my text interaction like so:

for gesture in interaction.gesturesForFailureRequirements {

    if gesture.isKind(of: UILongPressGestureRecognizer.self) {
        gesture.addTarget(self, action: #selector(longPressEdgeScrollGesture(_:)))
    }
}

Then in longPressEdgeScrollGesture you can get the coordinates of the cursor in your scroll view to activate your edge scrolling timer as necessary

@objc private func longPressEdgeScrollGesture(_ gesture: UILongPressGestureRecognizer) {

    guard gesture.numberOfTouches > 0 else {
        return
    }

    let location = gesture.location(in: gesture.view)

    print(location)

    //Start/stop your edge scrolling as required
}
simeon
  • 4,466
  • 2
  • 38
  • 42
  • 1
    Hi Simeon. Thank you very much for your answer. I just started working on the project again and this is a lot of help. – KnutKnatter Jun 12 '20 at 23:39
0

I found a solution to my original problem. It is somewhat of a work around, but hopefully Apple will improve UITextInteraction.

Trying to intercept any functions in UITextInput led nowhere. Thankfully some very clever people on Twitter figured out what to do. You need to intercept the right UIGestureRecognizer that is added automatically. Much like Simeon explained in his answer. The recognizer in question is called UITextRangeAdjustmentGestureRecognizer. But you cannot find it via gesturesForFailureRequirements. You need to become a delegate of the gestures and then you can find mentioned gesture via the delegate method of shouldRecognizeSimultaneouslyWith otherGestureRecognizer.

Once you add a target + action to that gesture you can observe the dragging and handle edge scrolling.

KnutKnatter
  • 61
  • 1
  • 2