WKWebView - evita a rolagem automática acionada pela seleção de texto do usuário

8

Quando um usuário toca e mantém pressionado o gesto para selecionar uma palavra e, em seguida, arrasta o dedo na direção da borda superior ou inferior da tela, a página rola automaticamente para acomodar a seleção.

aqui está um pequeno clipe demonstrando

Eu gostaria de evitar esse comportamento dentro de um WKWebView.

Aqui está o que eu tentei até agora:

em um bridge.jsarquivo acessível para a visualização na web:

var shouldAllowScrolling = true;

document.addEventListener('selectionchange', e => {
    shouldAllowScrolling = getSelectedText().length === 0;
    window.webkit.messageHandlers.selectionChangeHandler.postMessage(
        {
            shouldAllowScrolling: shouldAllowScrolling
        });
    console.log('allow scrolling = ', shouldAllowScrolling);
});

e depois em uma WKScriptMessageHandlerimplementação:

public func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage)
    {
        switch message.name
        {
        case "selectionChangeHandler":
            let params = paramsDictionary(fromMessageBody: message.body)
            let shouldEnableScrolling = params["shouldAllowScrolling"] as? Bool ?? true
            cell?.webView.scrollView.isScrollEnabled = shouldEnableScrolling
            cell?.webView.scrollView.isUserInteractionEnabled = shouldEnableScrolling // not together with the line above 
        default:
            fatalError("\(#function): received undefined message handler name: \(message.name)")
        }
    }

Da mesma forma, tentei chamar a preventDefault()função diretamente no arquivo javascript para vários eventos, a saber , scrolle touchmoveassim:

document.addEventListener('touchmove', e => {
    if (!shouldAllowScrolling) {
        e.preventDefault()
    }
}, {passive: false});

ambos os métodos impedem a rolagem com êxito quando algum texto é selecionado, mas não substituem o comportamento descrito na parte superior da minha pergunta.

Posso aceitar soluções em Swift e JavaScript ou uma mistura de ambas.

banana1
fonte

Respostas:

5

Acabei resolvendo esse problema salvando a última posição de rolagem e rolando para ela quando apropriado, da seguinte maneira:

var shouldAllowScrolling = true;
var lastSavedScrollLeft = 0;
var lastSavedScrollTop = 0;

function saveScrollPosition() {
    lastSavedScrollLeft = window.pageXOffset || document.documentElement.scrollLeft;
    lastSavedScrollTop = window.pageYOffset || document.documentElement.scrollTop;
}

document.addEventListener('touchstart', e => {
    saveScrollPosition();
});

document.addEventListener('touchend', () => {
    // enable scrolling when the user lifts their finger, to allow scrolling while text selection is still present
    shouldAllowScrolling = true;
});

document.addEventListener('scroll', e => {
    if (!shouldAllowScrolling) {
        window.scrollTo(lastSavedScrollLeft, lastSavedScrollTop);
    }
});

document.addEventListener('selectionchange', e => {
    shouldAllowScrolling = getSelectedText().length === 0;
});

Se alguém puder oferecer uma solução mais elegante que evite que a rolagem fique completamente feliz em aceitá-la.

EDITAR:

esta solução pode causar tremor / tremor de luz.

isso pode ser resolvido executando-se o scroll nativamente, WKWebViewem vez de chamar window.scrollTo()o javascript.

banana1
fonte