Leaflet: O menu de contexto não é acionado no iOS13

Criado em 23 set. 2019  ·  47Comentários  ·  Fonte: Leaflet/Leaflet

Depois que o iOS foi atualizado para a versão 13, o pressionamento longo não funciona e o evento do menu de contexto não é acionado.

Comentários muito úteis

Esse problema é mencionado no bug do

Todos 47 comentários

toque duas vezes para disparar doubleClickZoom também não funcionará no iOS 13. Uma correção para o menu de contexto é muito mais importante de resolver.

Eu confirmo o mesmo problema. Após o exame inicial, parece que o iOS 13 Safari não dispara eventos de toque corretamente.

Toque longo no iOS Safari não envia nenhum evento para a biblioteca do Leaflet e é gerenciado pelo próprio iOS.

O clique duplo emite os seguintes eventos: mouseover, mousemove, mousedown, mouseup, click, o que resulta no clique do evento do folheto.

Parece-me um problema sério no Safari móvel iOS 13 e acho que tem algo a ver para tornar o Safari móvel muito semelhante ao Safari para desktop. A menos que os eventos de toque sejam enviados corretamente para o Folheto, não há muito o que consertar.

Há alguém que irá relatar isso para a Apple?

Na semana passada, enviei um relatório para o suporte da Apple. Ainda não há resposta.

Eu tenho um pouco mais de informação. Se adicionar manipulador de clique no div do mapa e controle de zoom estiver habilitado, o toque longo começou a funcionar como menu de contexto de copiar / colar.
Curtiu isso

<div id="mapid" onclick="javascript:void(0)"/>

 -webkit-user-select: none;
 -webkit-touch-callout: none;

também não ajuda, apenas desabilite o menu copiar / colar

Talvez seja útil para alguém.

Como uma solução temporária para impressão longa:
adicione esta biblioteca
https://www.cssscript.com/handle-long-press-tap-event-in-javascript-long-press-js/
e em css
-webkit-user-select: none; -webkit-touch-callout: none;

do que adicionar ouvinte para evento de toque longo. E aqui no manipulador de eventos converte coordenadas em coordenadas de folheto.

A biblioteca funciona? Até agora, verifiquei o código-fonte e a biblioteca escuta eventos de toque que não são enviados para o Safari de qualquer maneira. Apenas curioso.

Sim, funciona. Para um toque longo, pelo menos.
Eu verifiquei no iOS13 e iPadOS13

ótima dica obrigado

Esta é a solução final conforme a usamos. Ele detecta um toque longo e emula MenuEvent 'contextmenu'. Basta colocá-lo em seus códigos JS e você estará bem.

Observação: é uma boa ideia limitar a execução apenas para usuários do iOS 13, para não ter muitos ouvintes

// Copied and modified from https://github.com/john-doherty/long-press-event/

import rs from 'rootScope'
import $ from '$'

if( rs.platform === 'ios' && / OS 13_/.test( navigator.userAgent ) ) {

    // This is our main map el
    const mapEl = $('#map-container')

    // This class adds  -webkit-user-select: none; -webkit-touch-callout: none;
    // to the BODY el
    document.body.classList.add('ios13')

    let timer = null;

    const fireLongPressEvent = originalEvent => {

        clearLongPressTimer();

        const el = originalEvent.target
        , x = originalEvent.touches[0].clientX
        , y = originalEvent.touches[0].clientY

        // This will emulate contextmenu mouse event
        const event = new MouseEvent('contextmenu', {
                                    bubbles: true,
                                    cancelable: true,
                                    clientX: x,
                                    clientY: y
                                })


        // fire the long-press event
        const suppressClickEvent = el.dispatchEvent.call(el,event);

        if (suppressClickEvent) {

            // temporarily intercept and clear the next click
            mapEl.addEventListener('touchend', function clearMouseUp(e) {
                mapEl.removeEventListener('touchend', clearMouseUp, true);
                cancelEvent(e);
            }, true);
        }
    }

    const startLongPressTimer = e => {
        clearLongPressTimer(e);
        timer = setTimeout( fireLongPressEvent.bind(null,e), 1000 );
    }

    const clearLongPressTimer = () => {
        if(timer) {
            clearTimeout(timer);
            timer = null;
        }
    }

    const cancelEvent = e => {
        e.stopImmediatePropagation();
        e.preventDefault();
        e.stopPropagation();
    }

    // hook events that clear a pending long press event
    mapEl.addEventListener('touchcancel', clearLongPressTimer, true);
    mapEl.addEventListener('touchend', clearLongPressTimer, true);
    mapEl.addEventListener('touchmove', clearLongPressTimer, true);

    // hook events that can trigger a long press event
    mapEl.addEventListener('touchstart', startLongPressTimer, true); // <- start

}

deparei com este tópico enquanto examinava um problema semelhante. Descobri que chamar preventDefault em touchstart permitirá que você receba eventos de toque como de costume e, assim, verifique se há toque duplo como antes.

Eu acredito que isso se resume ao fato de que o Safari para celular mudou a forma como simula eventos de foco (para fazer sites funcionarem que dependem apenas de eventos de foco para abrir menus)

deparei com este tópico enquanto examinava um problema semelhante. Descobri que chamar preventDefault em touchstart permitirá que você receba eventos de toque como de costume e, assim, verifique se há toque duplo como antes.

Eu acredito que isso se resume ao fato de que o Safari para celular mudou a forma como simula eventos de foco (para fazer sites funcionarem que dependem apenas de eventos de foco para abrir menus)

Até agora funciona o oposto para mim, basta suprimir todos os cliques, exceto para toque longo. Você pode compartilhar um código que funcione para você?

desculpe, eu estava supondo que você estava usando eventos de toque brutos para detectar toques duplos. Se você está contando com eventos de clique / mouse, preventDefault no touchstart os impedirá.

De qualquer forma, parece ter sido um bug no iOS que foi corrigido no 13.1.

@felixhageloh Acabei de testar no iOS 13.1 executando esta demonstração:
http://aratcliffe.github.io/Leaflet.contextmenu/examples/index.html

Mas não parece que o problema foi corrigido! Há algo que estou perdendo?
Questão realmente crítica para nós, uma vez que dependemos muito do longo evento de imprensa para trabalhar, qualquer ajuda será apreciada.

Esse problema é mencionado no bug do

Esse problema é mencionado no bug do

Estamos muito felizes por você estar se metendo neste problema. Como desenvolvedores da web, ficaríamos felizes que o Safari no iOS 13.x se comportasse da mesma forma que no iOS 12.x ou Chrome no Android no caso de eventos de toque.

Feliz por você estar aqui.

@ majid701 desculpe se eu causei alguma confusão. O problema que eu estava vendo no 13.0 era:
toque duplo (rapidamente) -> um único touchstart e touchend foi disparado.

Desde 13.1 estou recebendo dois eventos touchstart e touchend novamente. O aplicativo que estou mantendo usa eventos de toque brutos para detectar toques duplos, então este zoom fixo de toque duplo para nós. Eu estava assumindo incorretamente que esse seria o seu caso também.

if (rs.platform === 'ios' && / OS 13 _ /. test (navigator.userAgent))

Observe @ilblog que isso não vai funcionar no iPadOS, onde a string do agente do usuário é a mesma do Safari no desktop e não anuncia iOS.

_ (repostagem do bug do WebKit para que as pessoas com Cc para esse bug possam ver isso.) _

O evento contextmenu Leaflet não é acionado porque o código do Leaflet usa eventos pointerdown e touchstart indistintamente e a disponibilidade do primeiro exclui o outro. No Map.Tap.js, o método _onDown tem este cheque if (!e.touches) { return; } que sempre levará a um retorno antecipado já que um evento PointerEvent não é o mesmo que TouchEvent e não tem uma propriedade touches .

Portanto, a falta de um evento de Folheto contextmenu sendo acionado é um problema de Folheto especificamente.

Na verdade, esse código nem mesmo é executado com eventos de ponteiro por causa deste código:

if (Browser.touch && !Browser.pointer) {
    Map.addInitHook('addHandler', 'tap', Tap);
}

No iOS 13, tanto Browser.touch quanto Browser.pointer são verdadeiros, então o pacote inteiro não foi inicializado.

Na semana passada, enviei um relatório para o suporte da Apple. Ainda não há resposta.

@ ginger-777 Você tem um número de bug / ID de feedback? Geralmente, a melhor maneira de chamar a atenção dos desenvolvedores do WebKit é registrar um bug diretamente em bugs.webkit.org.

@graouts Sim, percebi isso também depois de ler seu último comentário. Mas eu testei a desativação de eventos de ponteiro de Settings -> General -> Experimental Features e parecia que funciona neste site de demonstração:
http://aratcliffe.github.io/Leaflet.contextmenu/examples/index.html

Mas não funciona dentro do meu aplicativo. Estou carregando o folheto dentro de um Webview.

Quanto ao toque duplo para aumentar o zoom, isso também é um problema do Folheto. Considere este código em DomEvent.DoubleTap :

function onTouchStart(e) {
    var count;

    if (Browser.pointer) {
        if ((!Browser.edge) || e.pointerType === 'mouse') { return; }
        count = _pointersCount;
    } else {
        count = e.touches.length;
    }

    if (count > 1) { return; }

    var now = Date.now(),
        delta = now - (last || now);

    touch = e.touches ? e.touches[0] : e;
    doubleTap = (delta > 0 && delta <= delay);
    last = now;
}

Se um navegador suporta ponteiro eventos e não é o Edge então a função de saídas cedo. Este é o caso do Safari ou de qualquer navegador baseado em WebKit no iOS 13.

Pelo que eu posso dizer, ambos os problemas relatados aqui são simplesmente o Leaflet não estar pronto para um navegador que oferece suporte a eventos de toque e eventos de ponteiro (toque longo e o evento contextmenu ) ou suporte a eventos de ponteiro, mas não é o Microsoft Edge (toque duas vezes e o evento dblclick ).

Fechei o bug do WebKit, pois não há nada para corrigirmos neste estágio.

@graouts Sim, percebi isso também depois de ler seu último comentário. Mas eu testei a desativação de eventos de ponteiro de Settings -> General -> Experimental Features e parecia que funciona neste site de demonstração:
http://aratcliffe.github.io/Leaflet.contextmenu/examples/index.html

Mas não funciona dentro do meu aplicativo. Estou carregando o folheto dentro de um Webview.

Sim @ majid701 , acho que esta configuração se aplica apenas ao Safari, não a WKWebView ou qualquer outra forma de incorporar WebKit no iOS. Não tenho certeza de como você pode desativar os eventos de ponteiro fora do aplicativo Safari.

Acho que a solução será consertar a biblioteca Leaflet, que detecta o iOS 13 Safari como um navegador "pinterType" e, portanto, não lida corretamente com eventos de toque.

Muito obrigado @graouts pelo seu esforço e muita ajuda neste assunto.

Há PR apenas em movimento https://github.com/Leaflet/Leaflet/pull/6815 sobre toque duplo em dispositivos móveis, patching mesmas linhas de código, então pedi a eles para cobrir este problema também.

if (rs.platform === 'ios' && / OS 13 _ /. test (navigator.userAgent))

Observe @ilblog que isso não vai funcionar no iPadOS, onde a string do agente do usuário é a mesma do Safari no desktop e não anuncia iOS.

Bem, isso é interessante, pois pode desencadear outra série de problemas, quando os desenvolvedores não conseguem distinguir o macOS do iPad. Isso não está relacionado a esse bug do Folheto, mas acho que também deve ser resolvido pela Apple.

https://forums.developer.apple.com/thread/119186

RESOLVIDO

Graças à valiosa contribuição da Apple, consegui consertar o problema do iOS 13 pelo menos no iPhone. Como o iPad no iOS13 tem a mesma string de agente do usuário do macOS, não consegui corrigir o problema no iPad.

Basta modificar src/core/Browser.js assim:

// 'false' for all iOS devices, that (as of version iOS13 support both, touch and pointer events)
// Unfortunately as of iOS13 it is not possible to distinguish iPad from OS X by user agent string
export var pointer = !!(window.PointerEvent || msPointer) && !userAgentContains('iphone');

A solicitação de pull com a correção está aqui

https://github.com/Leaflet/Leaflet/pull/6827

if (rs.platform === 'ios' && / OS 13 _ /. test (navigator.userAgent))

Observe @ilblog que isso não vai funcionar no iPadOS, onde a string do agente do usuário é a mesma do Safari no desktop e não anuncia iOS.

Bem, isso é interessante, pois pode desencadear outra série de problemas, quando os desenvolvedores não conseguem distinguir o macOS do iPad. Isso não está relacionado a esse bug do Folheto, mas acho que também deve ser resolvido pela Apple.

Geralmente, tomar decisões com base na string do agente do usuário não é a abordagem ideal. A detecção de recursos caso a caso é adequada para a grande maioria dos casos de uso. Além disso, o Safari no iPadOS pode usar uma string de agente de usuário diferente, dependendo da classe do dispositivo (iPad mini x outros modelos de iPad de tela maior) ou dependendo do tamanho da janela em multitarefa, onde o Safari pode usar um terço do tela, ou ainda dependendo da preferência do usuário (solicitar site mobile / desktop).

Se houver casos em que a detecção de recursos não seja adequada, levante um bug em bugs.webkit.org e a equipe do WebKit será capaz de determinar a melhor solução.

Alguma atualização sobre como resolver esse problema para o iPadOS 13?
Estou usando eventos mousedown / mouseup.

Olá a todos,

Tentei uma solução melhor do que apenas farejar o UA e falsificá-lo, pois deveria ser mais estável.
https://github.com/Leaflet/Leaflet/pull/6855
Ele ainda está farejando o UA, mas só para não alterar o código de plataformas que não posso testar. A condição edge parece existir para resgatar devido a possíveis eventos de dblclick sendo acionados, ou algo assim? Parece-me (embora não seja muito versado em JS) que o teste de recurso Browser.pointer para eventos de ponteiro deve ser o único teste de que precisamos, e outros testes devem ser restritos e muito específicos, em vez de "se não Edge, salve" (faltando um "por quê?"), parece que deveria ser "se Edge, faça algo diferente" (com um motivo nos comentários).

Ainda estou tendo problemas com o iOS13 em um iPad, no entanto.

@graouts : Re: https://github.com/Leaflet/Leaflet/issues/6817#issuecomment -536581361 e https://github.com/Leaflet/Leaflet/issues/6817#issuecomment -536587962:
Estou tentando fazer isso funcionar no iPad (consertei no iPhone com o PR acima), mas não há acionamento de um segundo pointerdown se eu der um duplo toque. Enviei pontos de interrupção, adicionei log() chamadas e não consigo ver nenhuma sendo acionada. Parece que o iPad está omitindo o segundo toque em algumas situações.
Posso aumentar o zoom com um "clique triplo / quádruplo", mas não com um clique duplo. logs nos manipuladores globais (que são uma camada de compatibilidade para coisas que solicitam touchstart / touchend ) não disparam em toques duplos.
Alguma ideia do que o iPad (com iOS13) poderia fazer para omitir esses segundos toques? O limite é 250 ms, mas não consigo fazer o segundo toque para registrar se for mais rápido do que ~ 320 ms.
Tenho <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" /> no meu cabeçalho, conforme mencionado na página "Folheto para celular".

@graouts (desculpe, não
Se você tentar a seguinte página de teste do ouvinte de evento:
https://patrickhlauke.github.io/touch/tests/event-listener.html

Você pode ver a diferença entre iOS13 no iPhone e no iPad. no iPhone, posso tocar duas vezes no botão e ver duas sequências de pointerdown+pointerup . No iPad, não consigo acionar essa sequência com um toque duplo. Ele mantém eventos por um tempo.

Obrigado,
Filipe

@filcab Por favor, registre um bug em bugs.webkit.org e eu darei uma olhada! Dito isso, acredito que esse problema foi corrigido no iOS 13.2, para o qual as compilações iniciais do desenvolvedor já estão disponíveis em developer.apple.com. Mas sempre registre um bug do webkit.org para a equipe dar uma olhada.

@filcab Por favor, registre um bug em bugs.webkit.org e eu darei uma olhada! Dito isso, acredito que esse problema foi corrigido no iOS 13.2, para o qual as compilações iniciais do desenvolvedor já estão disponíveis em developer.apple.com. Mas sempre registre um bug do webkit.org para a equipe dar uma olhada.

Eu preenchi https://bugs.webkit.org/show_bug.cgi?id=203031

Olá a todos, muito obrigado por todas as contribuições valiosas - estamos analisando isso o mais rápido possível. Estou de férias, então não poderei programar, mas executei ping em alguns outros mantenedores para priorizar.

Percorrendo rapidamente a discussão, parece que a origem do problema é que o iOS13 oferece suporte à especificação de eventos de ponteiro, portanto, o Leaflet segue parcialmente os caminhos de código que otimizamos especificamente para navegadores móveis IE legados, onde os eventos de toque usuais não estavam disponíveis.

Farejar string de agente como em # 6827 não é muito confiável (pode quebrar a qualquer momento); corrigir eventos de ponteiro como em # 6855 pode ser melhor, mas ainda assim muito arriscado, considerando que nunca pretendemos que caminhos de código de ponteiro funcionem em navegadores modernos quando escrevemos esse código; talvez devêssemos tentar desabilitar eventos de ponteiro completamente se detectamos anteriormente que eventos de toque regulares funcionam corretamente (então, algo como export var pointer = !webkit && !!(window.PointerEvent || msPointer) )?

Olá a todos, muito obrigado por todas as contribuições valiosas - estamos analisando isso o mais rápido possível. Estou de férias, então não poderei programar, mas executei ping em alguns outros mantenedores para priorizar.

Percorrendo rapidamente a discussão, parece que a origem do problema é que o iOS13 oferece suporte à especificação de eventos de ponteiro, portanto, o Leaflet segue parcialmente os caminhos de código que otimizamos especificamente para navegadores móveis IE legados, onde os eventos de toque usuais não estavam disponíveis.

Farejar string de agente como em # 6827 não é muito confiável (pode quebrar a qualquer momento); corrigir eventos de ponteiro como em # 6855 pode ser melhor, mas ainda assim muito arriscado, considerando que nunca pretendemos que caminhos de código de ponteiro funcionem em navegadores modernos quando escrevemos esse código; talvez devêssemos tentar desabilitar eventos de ponteiro completamente se detectamos anteriormente que eventos de toque regulares funcionam corretamente (então, algo como export var pointer = !webkit && !!(window.PointerEvent || msPointer) )?

Isso soa bem, especialmente se o código dos eventos de ponteiro não for tão recente quanto eu esperava. Vou atualizar meu PR com o patch proposto (acabei de testar no iPad e no iPhone com iOS 13 e funciona em ambos).

Obrigado,
Filipe

Do ponto de vista dos padrões, usar eventos de ponteiro (quando disponíveis) em vez de eventos de toque é preferível, pois é um padrão W3C e não uma tecnologia específica do fornecedor.

Do ponto de vista técnico, pelo menos para a implementação do iOS, existem implicações de desempenho. Os eventos de toque são sempre despachados de forma síncrona, portanto, qualquer trabalho JS executado como resultado do manuseio deles será bloqueado e pode resultar em capacidade de resposta inferior. Os eventos de ponteiro foram projetados de forma que possam ser implementados de forma assíncrona, e no iOS o são.

Duas coisas que vale a pena considerar, talvez não para este problema em particular, mas vale a pena manter em mente para o futuro.

será que a correção nº 6855 não funciona em marcadores? Estou recebendo relatórios de que um toque longo em um marcador não aciona o menu de contexto, mas o menu de compartilhamento / cópia do sistema operacional aparece ...

Olá @eliboni ,
Parece que você precisa usar -webkit-touch-callout: none; (ou similar ... Eu sou ruim nisso 😅) nos elementos. Não tenho certeza de qual, no entanto. Adicionar * { -webkit-touch-callout: none; } funcionou no meu teste (evento contextmenu acionado em um marcador, usando um iPhone).
Ajuste conforme necessário.

Oi,
Não consigo usar o zoom de toque duplo com Leaflet.js.
Acabei de ler este post, mas não achei a solução.
Alguém pode me ajudar por favor?

Atenciosamente, Kandy.

Finalmente encontrei a solução:

Precisamos atualizar para o folheto 1.5 e habilitar de Toque em:

new L.map("mapid",{ attributionControl: false, zoomControl: false, tap: true });

e funciona bem.

Cumprimentos

@bitxenio ,

Receio que não funcione para mim usando 1.5.1

Estou de volta a escrever meu próprio manipulador agora, pois o IOS 13.2 também não parece funcionar :(

@rwillett , @Bitxenio : Qual código você está usando? Ambos mencionam versões específicas do folheto.
A correção para PR # 6855, comprometida em outubro, não está no lançamento mais recente, que foi em maio.

Você está usando master ?

Obrigado,
Filipe

Ah! Usamos o lançamento oficial. Não percebemos que o PR # 6855 não estava incluído. Nossa culpa.

Baixamos o dev branch e vamos tentar isso,

Obrigado

Roubar

Baixamos 1.6-dev e funciona bem. Tanto o toque duplo quanto o menu de contexto no ios 13.2.

Obrigado pela ajuda, agradeço.

Roubar

Lançado em 1.6.0

talvez devêssemos tentar desabilitar eventos de ponteiro completamente se detectamos anteriormente que eventos de toque regulares funcionam corretamente (então, algo como export var pointer = !webkit && !!(window.PointerEvent || msPointer) )?

@mourner
Isso era realmente arriscado e causou problemas como o # 6896.
Participe da discussão em https://github.com/Leaflet/Leaflet/issues/6977#issuecomment -577638632

Esta página foi útil?
0 / 5 - 0 avaliações