Como detecto corretamente as alterações de orientação usando o Phonegap no iOS?

178

Encontrei este código de teste de orientação abaixo procurando material de referência do JQTouch. Isso funciona corretamente no simulador iOS no Safari móvel, mas não é tratado corretamente no Phonegap. Meu projeto está executando o mesmo problema que está matando esta página de teste. Existe uma maneira de detectar a mudança de orientação usando JavaScript no Phonegap?

window.onorientationchange = function() {
  /*window.orientation returns a value that indicates whether iPhone is in portrait mode, landscape mode with the screen turned to the
    left, or landscape mode with the screen turned to the right. */
  var orientation = window.orientation;
  switch (orientation) {
    case 0:
      /* If in portrait mode, sets the body's class attribute to portrait. Consequently, all style definitions matching the body[class="portrait"] declaration
         in the iPhoneOrientation.css file will be selected and used to style "Handling iPhone or iPod touch Orientation Events". */
      document.body.setAttribute("class", "portrait");

      /* Add a descriptive message on "Handling iPhone or iPod touch Orientation Events"  */
      document.getElementById("currentOrientation").innerHTML = "Now in portrait orientation (Home button on the bottom).";
      break;

    case 90:
      /* If in landscape mode with the screen turned to the left, sets the body's class attribute to landscapeLeft. In this case, all style definitions matching the
         body[class="landscapeLeft"] declaration in the iPhoneOrientation.css file will be selected and used to style "Handling iPhone or iPod touch Orientation Events". */
      document.body.setAttribute("class", "landscape");

      document.getElementById("currentOrientation").innerHTML = "Now in landscape orientation and turned to the left (Home button to the right).";
      break;

    case -90:
      /* If in landscape mode with the screen turned to the right, sets the body's class attribute to landscapeRight. Here, all style definitions matching the
         body[class="landscapeRight"] declaration in the iPhoneOrientation.css file will be selected and used to style "Handling iPhone or iPod touch Orientation Events". */
      document.body.setAttribute("class", "landscape");

      document.getElementById("currentOrientation").innerHTML = "Now in landscape orientation and turned to the right (Home button to the left).";
      break;
  }
}
ajpalma
fonte

Respostas:

297

Isto é o que eu faço:

function doOnOrientationChange() {
    switch(window.orientation) {  
      case -90: case 90:
        alert('landscape');
        break; 
      default:
        alert('portrait');
        break; 
    }
}
  
window.addEventListener('orientationchange', doOnOrientationChange);
  
// Initial execution if needed
doOnOrientationChange();


Atualização de maio de 2019: window.orientation é um recurso descontinuado e não é suportado pela maioria dos navegadores, de acordo com o MDN . O orientationchangeevento está associado ao window.orientation e, portanto, provavelmente não deve ser usado.

Benny Neugebauer
fonte
3
Essa é a única solução que funciona para mim nesta discussão :) +1
Atadj
9
Eu fizwindow.onorientationchange = function() { setTimeout(functionName, 0); };
Kirk Strobeck
11
Por interesse, por que você usou o setTimeout Kirk?
backdesk
10
Seja cuidadoso! Isso é específico do dispositivo - os graus referem-se à diferença da orientação padrão do dispositivo. Em outras palavras, se o tablet foi projetado para ser usado na paisagem, 90 significa que está no modo retrato. Como uma solução alternativa para isso, eu inicialmente verifico a altura x largura da janela para armazenar a orientação, e uso o directionchange para atualizá-lo, se houver uma alteração.
precisa saber é o seguinte
15
Além disso, descobri que a mudança de orientação é acionada antes que a largura e a altura sejam alteradas; portanto, é necessário um pouco de atraso para detectar a orientação corretamente usando a largura e a altura. Para isso, armazeno a orientação atual e, quando uma alteração é detectada, verifico a alteração na orientação em incrementos de 50ms até 250ms. Quando uma diferença é encontrada, atualizo a página de acordo. No meu Nexus 5, ele geralmente detecta a diferença entre largura e altura após 150ms.
22416 benallansmith
24

Eu uso o window.onresize = function(){ checkOrientation(); } E no checkOrientation, você pode empregar a verificação window.orientation ou a largura do corpo, mas a ideia é que "window.onresize" seja o método mais compatível com vários navegadores, pelo menos na maioria dos navegadores móveis e de mesa que eu já tive. oportunidade de testar.

hndcrftd
fonte
1
Este é um ótimo ponto. Se você estiver desenvolvendo usando uma tecnologia voltada para a Web, esse método permitirá depurar e testar mais facilmente em um navegador, em vez de implantar / simular a cada vez.
Matt Ray
2
Isso não está ok ... porque quando o teclado aparece, ele é redimensionado (e esse não é o único caso). Então, um grande não por isso!
HellBaby
2
@HellBaby Quando o teclado aparecer, a função será chamada, dependendo do método usado para a detecção de orientação, ele detectará que a orientação NÃO FOI ALTERADA, como seria o caso da janela.orientação. Então, eu ainda mantenho minha resposta.
precisa saber é o seguinte
1
@ Raine Eu usei esse método em um iPad 4 e fui forçado a mudar a causa desse problema. Então, talvez ele está trabalhando para alguns dispositivos, mas não para todos ..
HellBaby
1
@MattRay Você pode testar facilmente as alterações de orientação com os kits de ferramentas de desenvolvimento mais recentes, capazes de emular esse tipo de comportamento do dispositivo.
kontur
14
if (window.matchMedia("(orientation: portrait)").matches) {
   // you're in PORTRAIT mode
}

if (window.matchMedia("(orientation: landscape)").matches) {
  // you're in LANDSCAPE mode
}
Alyssa Reyes
fonte
1
Você pode anexá-lo a um resizeevento. No entanto, IIRC, essas consultas não funcionarão corretamente com o teclado para cima.
Adi518
12

Sou muito novo no iOS e no Phonegap também, mas consegui fazer isso adicionando um eventListener. Fiz a mesma coisa (usando o exemplo que você menciona) e não consegui fazê-lo funcionar. Mas isso parecia fazer o truque:

// Event listener to determine change (horizontal/portrait)
window.addEventListener("orientationchange", updateOrientation); 

function updateOrientation(e) {
switch (e.orientation)
{   
    case 0:
        // Do your thing
        break;

    case -90:
        // Do your thing
        break;

    case 90:
        // Do your thing
        break;

    default:
        break;
    }
}

Você pode ter alguma sorte pesquisando no Grupo PhoneGap do Google o termo "orientação" .

Um exemplo que li sobre como detectar orientação foi Pie Guy: ( jogo , arquivo js ). É semelhante ao código que você postou, mas como você ... Eu não consegui fazê-lo funcionar.

Uma ressalva: o eventListener funcionou para mim, mas não tenho certeza se essa é uma abordagem excessivamente intensiva. Até agora, essa foi a única maneira que funcionou para mim, mas não sei se existem maneiras melhores e mais simplificadas.


UPDATE corrigiu o código acima, ele funciona agora

avoision
fonte
plese corrigir o nome do evento
Dan
5

Enquanto trabalhava com o orientationchangeevento, precisava de um tempo limite para obter as dimensões corretas dos elementos na página, mas o matchMedia funcionou bem. Meu código final:

var matchMedia = window.msMatchMedia || window.MozMatchMedia || window.WebkitMatchMedia || window.matchMedia;

if (typeof(matchMedia) !== 'undefined') {
  // use matchMedia function to detect orientationchange
  window.matchMedia('(orientation: portrait)').addListener(function() {
    // your code ...
  });
} else {
  // use orientationchange event with timeout (fires to early)
  $(window).on('orientationchange', function() {
    window.setTimeout(function() {
      // your code ...
    }, 300)
  });
}
oncode
fonte
3

Acredito que a resposta correta já foi postada e aceita, mas há um problema que eu já experimentei e que alguns outros mencionaram aqui.

Em certas plataformas, várias propriedades, como as dimensões da janela ( window.innerWidth, window.innerHeight) e a window.orientationpropriedade, não serão atualizadas quando o evento "orientationchange"for disparado. Muitas vezes, a propriedade window.orientationfica undefinedpor alguns milissegundos após o disparo "orientationchange"(pelo menos no Chrome no iOS).

A melhor maneira que encontrei para lidar com esse problema foi:

var handleOrientationChange = (function() {
    var struct = function(){
        struct.parse();
    };
    struct.showPortraitView = function(){
        alert("Portrait Orientation: " + window.orientation);
    };
    struct.showLandscapeView = function(){
        alert("Landscape Orientation: " + window.orientation);
    };
    struct.parse = function(){
        switch(window.orientation){
            case 0:
                    //Portrait Orientation
                    this.showPortraitView();
                break;
            default:
                    //Landscape Orientation
                    if(!parseInt(window.orientation) 
                    || window.orientation === this.lastOrientation)
                        setTimeout(this, 10);
                    else
                    {
                        this.lastOrientation = window.orientation;
                        this.showLandscapeView();
                    }
                break;
        }
    };
    struct.lastOrientation = window.orientation;
    return struct;
})();
window.addEventListener("orientationchange", handleOrientationChange, false);

Estou verificando se a orientação é indefinida ou se a orientação é igual à última orientação detectada. Se qualquer um for verdadeiro, espero dez milissegundos e analiso a orientação novamente. Se a orientação for um valor adequado, chamo as showXOrientationfunções. Se a orientação for inválida, continuo repetindo minha função de verificação, aguardando dez milissegundos por vez, até que seja válida.

Agora, eu faria um JSFiddle para isso, como costumava fazer, mas o JSFiddle não está funcionando para mim e meu bug de suporte foi fechado, pois ninguém mais está relatando o mesmo problema. Se alguém quiser transformar isso em um JSFiddle, vá em frente.

Obrigado! Eu espero que isso ajude!

WebWanderer
fonte
Para sua informação, nos testes iniciais, vi um problema: giro no sentido horário algumas vezes e um alerta dizia "Paisagem Orientação: 180", mesmo que meu dispositivo estivesse fisicamente orientado como retrato.
MarsAndBack
2

aqui está o que eu fiz:

window.addEventListener('orientationchange', doOnOrientationChange);

function doOnOrientationChange()
{
      if (screen.height > screen.width) {
         console.log('portrait');
      } else {
         console.log('landscape');
      }
}
Raul Gomez
fonte
2

Embora a pergunta se refira apenas ao uso do PhoneGap e iOS, e embora já tenha sido respondida, posso acrescentar alguns pontos à questão mais ampla de detectar a orientação da tela com JS em 2019:

  1. window.orientationpropriedade é obsoleto e não suportado por navegadores Android .Não é um hotel novo que fornece mais informações sobre a orientação - screen.orientation. Mas ainda é experimental e não é suportado pelo iOS Safari . Assim, para obter o melhor resultado, você provavelmente precisará usar a combinação dos dois: const angle = screen.orientation ? screen.orientation.angle : window.orientation.

  2. Como @benallansmith mencionou em seu comentário , o window.onorientationchangeevento é disparado antes window.onresize, portanto, você não obterá as dimensões reais da tela, a menos que adicione algum atraso após o evento de mudança de orientação.

  3. Existe um plug-in de orientação de tela Cordova para oferecer suporte a navegadores móveis mais antigos, mas acredito que não há necessidade de usá-lo atualmente.

  4. Houve também um screen.onorientationchangeevento, mas está obsoleto e não deve ser usado. Adicionado apenas para completar a resposta.

No meu caso de uso, não me importei muito com a orientação real, mas com a largura e altura reais da janela, o que obviamente muda com a orientação. Então, usei resizeevent para evitar lidar com atrasos entre as orientationchangedimensões do evento e a atualização da janela:

window.addEventListener('resize', () => {
  console.log(`Actual dimensions: ${window.innerWidth}x${window.innerHeight}`);
  console.log(`Actual orientation: ${screen.orientation ? screen.orientation.angle : window.orientation}`);
});

Nota 1: Usei a sintaxe do EcmaScript 6 aqui, certifique-se de compilá-la no ES5, se necessário.

Nota 2: o window.onresizeevento também é acionado quando o teclado virtual é alternado , não apenas quando a orientação é alterada.

Alexey Grinko
fonte
1

Encontrei este código para detectar se o dispositivo está na orientação paisagem e, neste caso, adicione uma página inicial dizendo "alterar orientação para ver o site". Está funcionando nos telefones iOS, Android e Windows. Eu acho que isso é muito útil, pois é bastante elegante e evita definir uma paisagem para o site para celular. O código está funcionando muito bem. A única coisa que não é totalmente satisfatória é que, se alguém carregar a página em modo paisagem, a página inicial não aparecerá.

<script>
(function() {
    'use strict';

    var isMobile = {
        Android: function() {
            return navigator.userAgent.match(/Android/i);
        },
        BlackBerry: function() {
            return navigator.userAgent.match(/BlackBerry/i);
        },
        iOS: function() {
            return navigator.userAgent.match(/iPhone|iPad|iPod/i);
        },
        Opera: function() {
            return navigator.userAgent.match(/Opera Mini/i);
        },
        Windows: function() {
            return navigator.userAgent.match(/IEMobile/i);
        },
        any: function() {
            return (isMobile.Android() || isMobile.BlackBerry() || isMobile.iOS() || isMobile.Opera() || isMobile.Windows());
        }
    };
    if (isMobile.any()) {
        doOnOrientationChange();
        window.addEventListener('resize', doOnOrientationChange, 'false');
    }

    function doOnOrientationChange() {
        var a = document.getElementById('alert');
        var b = document.body;
        var w = b.offsetWidth;
        var h = b.offsetHeight;
        (w / h > 1) ? (a.className = 'show', b.className = 'full-body') : (a.className = 'hide', b.className = '');
    }
})();
</script>

E o HTML: <div id="alert" class="hide"> <div id="content">This site is not thought to be viewed in landscape mode, please turn your device </div> </div>

Luigi
fonte
1
if (window.DeviceOrientationEvent) {
    // Listen for orientation changes
    window.addEventListener("orientationchange", orientationChangeHandler);
    function orientationChangeHandler(evt) {
        // Announce the new orientation number
        // alert(screen.orientation);
        // Find matches
        var mql = window.matchMedia("(orientation: portrait)");

        if (mql.matches)  //true
    }
}
M Fauzi Riozi
fonte
-2

O seguinte funcionou para mim:

function changeOrientation(){
switch(window.orientation) {
case 0: // portrait, home bottom
case 180: // portrait, home top
 alert("portrait H: "+$(window).height()+" W: "+$(window).width());       
 break;
          case -90: // landscape, home left
          case 90: // landscape, home right
        alert("landscape H: "+$(window).height()+" W: "+$(window).width());
            break;
        }
    }

 window.onorientationchange = function() { 
            //Need at least 800 milliseconds
            setTimeout(changeOrientation, 1000);
        }

Eu precisava do tempo limite porque o valor de window.orientationnão é atualizado imediatamente

catalyst294
fonte
-3

Estou criando um aplicativo jQTouch no PhoneGap para o iPhone. Estou lutando com esse problema há dias. Vi a solução eventlistener sugerida algumas vezes, mas não consegui fazê-la funcionar.

No final, eu vim com uma solução diferente. Basicamente, verifica a largura do corpo periodicamente usando settimeout. Se a largura for 320, a orientação é retrato; se 480, é paisagem. Então, se a orientação tiver sido alterada desde a última verificação, ela disparará uma função de material de retrato ou de paisagem, onde você poderá fazer o que quiser para cada orientação.

Código (note, eu sei que há alguma repetição no código, mas não se preocupe em reduzi-lo ainda!):

// get original orientation based on body width
deviceWidth = $('body').width();
if (deviceWidth == 320) {
    currentOrientation = "portrait";
}
else if (deviceWidth == 480) {
    currentOrientation = "landscape";
}

// fire a function that checks the orientation every x milliseconds
setInterval(checkOrientation, 500);

// check orientation
function checkOrientation() {
    deviceWidth = $('body').width();
    if (deviceWidth == '320') {
        newOrientation = "portrait";
    }
    else if (deviceWidth == '480') {
        newOrientation = "landscape";
    }
    // if orientation changed since last check, fire either the portrait or landscape function
    if (newOrientation != currentOrientation) {
        if (newOrientation == "portrait") {
            changedToPortrait();
        }
        else if (newOrientation == "landscape") {
            changedToLandscape();
        }
        currentOrientation = newOrientation;
    }
}

// landscape stuff
function changedToLandscape() {
    alert('Orientation has changed to Landscape!');
}

// portrait stuff
function changedToPortrait() {
    alert('Orientation has changed to Portrait!');
}
Danny Connell
fonte
8
A codificação embutida do dispositivo para 320 ou 480 não funcionará no futuro ou com os telefones de alta resolução atuais.
Fresheyeball 18/10/11
1
1) Você deve usar um onresizeevento que será acionado imediatamente, não em 500 milissegundos 2) Este código é específico para o Android, use onorientationchangepara o iPhone 3) Teste se é suportado no navegador:"onorientationchange" in window
Dan