Come si ripristina la scala / lo zoom di un'app Web su un cambio di orientamento su iPhone?


96

Quando avvio la mia app in modalità verticale, funziona bene. Quindi ruoto in orizzontale e viene ridimensionato. Per farlo ridimensionare correttamente per la modalità orizzontale devo toccare due volte qualcosa due volte, prima per ingrandire completamente (il normale comportamento del doppio tocco) e di nuovo per ingrandire completamente (di nuovo, il normale comportamento del doppio tocco) . Quando si rimpicciolisce, si rimpicciolisce fino alla NUOVA scala corretta per la modalità orizzontale.

Il ritorno al ritratto sembra funzionare in modo più coerente; ovvero, gestisce lo zoom in modo che la scala sia corretta quando l'orientamento torna in verticale.

Sto cercando di capire se questo è un bug? o se questo è qualcosa che può essere risolto con JavaScript?

Con il meta contenuto viewport, sto impostando la scala iniziale su 1.0 e NON sto impostando la scala minima o massima (né voglio). Sto impostando la larghezza sulla larghezza del dispositivo.

Qualche idea? So che molte persone sarebbero grate di avere una soluzione in quanto sembra essere un problema persistente.


1
Una soluzione perfetta: niente javascript! stackoverflow.com/a/8727440/805787
Steeven

Risposte:


89

Jeremy Keith ( @adactio ) ha una buona soluzione per questo sul suo blog Orientamento e scala

Mantieni il markup scalabile non impostando una scala massima nel markup.

<meta name="viewport" content="width=device-width, initial-scale=1">

Quindi disabilita la scalabilità con javascript al caricamento fino all'avvio del gesto quando consenti nuovamente la scalabilità con questo script:

if (navigator.userAgent.match(/iPhone/i) || navigator.userAgent.match(/iPad/i)) {
    var viewportmeta = document.querySelector('meta[name="viewport"]');
    if (viewportmeta) {
        viewportmeta.content = 'width=device-width, minimum-scale=1.0, maximum-scale=1.0, initial-scale=1.0';
        document.body.addEventListener('gesturestart', function () {
            viewportmeta.content = 'width=device-width, minimum-scale=0.25, maximum-scale=1.6';
        }, false);
    }
}

Aggiornamento 22-12-2014:
su un iPad 1 non funziona, non funziona sul listener di eventi. Ho scoperto che la rimozione .bodyrisolve che:

document.addEventListener('gesturestart', function() { /* */ });

4
Sicuramente questo è meglio che disabilitare lo zoom ?! La migliore soluzione che ho trovato finora :)
danwellman

Hmm, questo disabilita ancora la capacità di zoomare. Qualcuno ha una soluzione semplice che non lo fa?
Brad Swerdfeger

Funziona, tuttavia ho notato che il problema ricomincia se uso il gesto di zoom pizzico e poi ruoto lo schermo. Non sono sicuro di come risolverlo.
Nilesh

3
Funziona. Tuttavia, ho notato che l'utente deve pizzicare due volte per eseguire lo zoom. Immagino che ciò sia dovuto al fatto che maximum-scale=1.0rimane in vigore dopo l'inizio del gesto. Esiste un modo per risolvere questo problema?
LandonSchropp

3
Questo non funziona per 2 motivi: 1) disabilita il numero 1 di inizio dei gesti, costringendo l'utente a eseguire due gesti. 2) si interrompe dopo che l'utente raddoppia il primo gesto, quindi funziona davvero solo se l'utente non esegue mai alcun gesto. - tutti dovrebbero esaminare la soluzione di Andrew Ashbacher di seguito. Funziona veramente.
tmsimont

18

Scott Jehl ha escogitato una soluzione fantastica che utilizza l'accelerometro per anticipare i cambiamenti di orientamento. Questa soluzione è molto reattiva e non interferisce con i gesti di zoom.

https://github.com/scottjehl/iOS-Orientationchange-Fix

Come funziona: questa correzione funziona ascoltando l'accelerometro del dispositivo per prevedere quando sta per verificarsi un cambio di orientamento. Quando ritiene imminente un cambio di orientamento, lo script disabilita lo zoom dell'utente, consentendo al cambio di orientamento di avvenire correttamente, con lo zoom disabilitato. Lo script ripristina di nuovo lo zoom una volta che il dispositivo è orientato vicino alla verticale o dopo che il suo orientamento è cambiato. In questo modo, lo zoom dell'utente non viene mai disabilitato mentre la pagina è in uso.

Fonte minimizzata:

/*! A fix for the iOS orientationchange zoom bug. Script by @scottjehl, rebound by @wilto.MIT License.*/(function(m){if(!(/iPhone|iPad|iPod/.test(navigator.platform)&&navigator.userAgent.indexOf("AppleWebKit")>-1)){return}var l=m.document;if(!l.querySelector){return}var n=l.querySelector("meta[name=viewport]"),a=n&&n.getAttribute("content"),k=a+",maximum-scale=1",d=a+",maximum-scale=10",g=true,j,i,h,c;if(!n){return}function f(){n.setAttribute("content",d);g=true}function b(){n.setAttribute("content",k);g=false}function e(o){c=o.accelerationIncludingGravity;j=Math.abs(c.x);i=Math.abs(c.y);h=Math.abs(c.z);if(!m.orientation&&(j>7||((h>6&&i<8||h<8&&i>6)&&j>5))){if(g){b()}}else{if(!g){f()}}}m.addEventListener("orientationchange",f,false);m.addEventListener("devicemotion",e,false)})(this);

Bello! Sembra una soluzione elegante.
Elisabeth

1
questa dovrebbe essere la risposta accettata !!!! Vorrei averlo visto prima prima di sprecare un'ora sulle soluzioni sopra :)
tmsimont

1
dopo ulteriori test questa è una specie di soluzione inaffidabile :( è incoerente e dopo aver guardato il codice posso vedere perché ... la "soglia" di movimento definita non è sempre raggiunta, specialmente se stai tenendo l'ipad su un angolo durante la rotazione
tmsimont

Potrebbe avere conseguenze spiacevoli per chiunque usi il blocco della rotazione ... potrebbe tenere il telefono a una certa angolazione e perdere la capacità di zoomare - l'utente non avrebbe idea del perché
1owk3y

14

Ho avuto lo stesso problema e l'impostazione della scala massima = 1.0 ha funzionato per me.

Modifica: come menzionato nei commenti, questo disabilita lo zoom dell'utente tranne quando il contenuto supera la risoluzione di larghezza. Come accennato, questo potrebbe non essere saggio. Potrebbe anche essere desiderato in alcuni casi.

Il codice viewport:

    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0;">

Bella soluzione. Fa un buon lavoro mantenendo la pagina a un livello di zoom costante (relativo alla larghezza del dispositivo) attraverso i cambiamenti di orientamento. Grazie per averlo condiviso!
Luke Stevenson

17
lo svantaggio è che gli utenti disabili non possono ingrandire il tuo sito!
Jess Jacobs

Ho notato che tutti questi metodi sembrano impedire ai CSS basati su media query di registrare correttamente la nuova larghezza del dispositivo (es: @media all e (max-width: 479px)
mheavers

2
uccidere lo zoom dell'utente è una pessima idea. vedere la soluzione di Andrew Ashbacher di seguito
tmsimont

Non sono sicuro dell'iPhone, ma su iPad questo non risolve il problema, impedisce semplicemente all'utente di essere in grado di eseguire lo zoom indietro manualmente quando il browser ingrandisce il cambio di orientamento.
Alejo

3

Se hai impostato la larghezza nel viewport:

<meta name = "viewport" content = "width=device-width; initial-scale=1.0;
 maximum-scale=1.0;" />

E poi cambia l'orientamento a volte ingrandirà casualmente (specialmente se stai trascinando sullo schermo) per risolvere questo problema non impostare una larghezza qui che ho usato:

<meta id="viewport" name="viewport" content="initial-scale=1.0; user-scalable=0;
minimum-scale=1.0; maximum-scale=1.0" />

Questo risolve lo zoom qualunque cosa accada, quindi puoi usare l'evento window.onorientationchange o se vuoi che sia indipendente dalla piattaforma (utile per testare) il metodo window.innerWidth .


1

MobileSafari supporta l' orientationchangeevento windowsull'oggetto. Purtroppo non sembra esserci un modo per controllare direttamente lo zoom tramite JavaScript. Forse potresti scrivere / modificare dinamicamente il metatag che controlla la visualizzazione, ma dubito che funzioni, influisce solo sullo stato iniziale della pagina. Forse potresti utilizzare questo evento per ridimensionare effettivamente i tuoi contenuti utilizzando CSS. In bocca al lupo!


3
Grazie! Sì, ho provato a modificare dinamicamente i valori della visualizzazione del metatag e non ha fatto nulla. Mi sembra che se ruoti in Orizzontale vuoi che si ingrandisca correttamente per mantenere la scala in modo che la pagina si adatti alla finestra di Safari. Mi sembra molto strano che questo non sia il comportamento predefinito!
Elisabeth


1

Ho utilizzato questa funzione nel mio progetto.

function changeViewPort(key, val) {
    var reg = new RegExp(key, "i"), oldval = document.querySelector('meta[name="viewport"]').content;
    var newval = reg.test(oldval) ? oldval.split(/,\s*/).map(function(v){ return reg.test(v) ? key+"="+val : v; }).join(", ") : oldval+= ", "+key+"="+val ;
    document.querySelector('meta[name="viewport"]').content = newval;
}

quindi basta aggiungereEventListener:

if( /iPad|iPhone|iPod|Android/i.test(navigator.userAgent) ){
    window.addEventListener("orientationchange", function() { 
        changeViewPort("maximum-scale", 1);
        changeViewPort("maximum-scale", 10);
    }
}

0

Ho trovato una nuova soluzione alternativa, diversa da qualsiasi altra che ho visto, disabilitando lo zoom nativo di iOS e implementando invece la funzionalità di zoom in JavaScript.

Uno sfondo eccellente sulle varie altre soluzioni al problema di zoom / orientamento è di Sérgio Lopes: una correzione al famoso bug di zoom iOS sul cambio di orientamento in verticale .

<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <meta name="viewport" id="viewport" content="user-scalable=no,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0" />
    <title>Robocat mobile Safari zoom fix</title>
    <style>
        body {
            padding: 0;
            margin: 0;
        }
        #container {
            -webkit-transform-origin: 0px 0px;
            -webkit-transform: scale3d(1,1,1);
            /* shrink-to-fit needed so can measure width of container http://stackoverflow.com/questions/450903/make-css-div-width-equal-to-contents */
            display: inline-block;
            *display: inline;
            *zoom: 1;
        }
        #zoomfix {
            opacity: 0;
            position: absolute;
            z-index: -1;
            top: 0;
            left: 0;
        }
    </style>
</head>

<body>
    <input id="zoomfix" disabled="1" tabIndex="-1">
    <div id="container">
        <style>
            table {
                counter-reset: row cell;
                background-image: url(http://upload.wikimedia.org/wikipedia/commons/3/38/JPEG_example_JPG_RIP_010.jpg);
            }
            tr {
                counter-increment: row;
            }
            td:before {
                counter-increment: cell;
                color: white;
                font-weight: bold;
                content: "row" counter(row) ".cell" counter(cell);
            }
        </style>
        <table cellspacing="10">
            <tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>
            <tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>
            <tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>
            <tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>
            <tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>
            <tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>
            <tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>
            <tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>
            <tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>
            <tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>
            <tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>
            <tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>
            <tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>
            <tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>
            <tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>
            <tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>
            <tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>
            <tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>
            <tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>
            <tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>
            <tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>
            <tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>
            <tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>
            <tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>
            <tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>
            <tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>
            <tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>
            <tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>
            <tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>
            <tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>
            <tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>
            <tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>
            <tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>
            <tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>
            <tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>
            <tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>
            <tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>
            <tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>
            <tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>
            <tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>
            <tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>
            <tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>
            <tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>
            <tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>
            <tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>
            <tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>
        </table>
    </div>

    <script>
    (function() {
        var viewportScale = 1;
        var container = document.getElementById('container');
        var scale, originX, originY, relativeOriginX, relativeOriginY, windowW, windowH, containerW, containerH, resizeTimer, activeElement;
        document.addEventListener('gesturestart', function(event) {
            scale = null;
            originX = event.pageX;
            originY = event.pageY;
            relativeOriginX = (originX - window.pageXOffset) / window.innerWidth;
            relativeOriginY = (originY - window.pageYOffset) / window.innerHeight;
            windowW = window.innerWidth;
            windowH = window.innerHeight;
            containerW = container.offsetWidth;
            containerH = container.offsetHeight;
        });
        document.addEventListener('gesturechange', function(event) {
            event.preventDefault();
            if (originX && originY && event.scale && event.pageX && event.pageY) {
                scale = event.scale;
                var newWindowW = windowW / scale;
                if (newWindowW > containerW) {
                    scale = windowW / containerW;
                }
                var newWindowH = windowH / scale;
                if (newWindowH > containerH) {
                    scale = windowH / containerH;
                }
                if (viewportScale * scale < 0.1) {
                    scale = 0.1/viewportScale;
                }
                if (viewportScale * scale > 10) {
                    scale = 10/viewportScale;
                }
                container.style.WebkitTransformOrigin = originX + 'px ' + originY + 'px';
                container.style.WebkitTransform = 'scale3d(' + scale + ',' + scale + ',1)';
            }
        });
        document.addEventListener('gestureend', function() {
            if (scale && (scale < 0.95 || scale > 1.05)) {
                viewportScale *= scale;
                scale = null;
                container.style.WebkitTransform = '';
                container.style.WebkitTransformOrigin = '';
                document.getElementById('viewport').setAttribute('content', 'user-scalable=no,initial-scale=' + viewportScale + ',minimum-scale=' + viewportScale + ',maximum-scale=' + viewportScale);
                document.body.style.WebkitTransform = 'scale3d(1,1,1)';
                // Without zoomfix focus, after changing orientation and zoom a few times, the iOS viewport scale functionality sometimes locks up (and completely stops working).
                // The reason I thought this hack would work is because showing the keyboard is the only way to affect the viewport sizing, which forces the viewport to resize (even though the keyboard doesn't actually get time to open!).
                // Also discovered another amazing side effect: if you have no meta viewport element, and focus()/blur() in gestureend, zoom is disabled!! Wow!
                var zoomfix = document.getElementById('zoomfix');
                zoomfix.disabled = false;
                zoomfix.focus();
                zoomfix.blur();
                setTimeout(function() {
                    zoomfix.disabled = true;
                    window.scrollTo(originX - relativeOriginX * window.innerWidth, originY - relativeOriginY * window.innerHeight);
                    // This forces a repaint. repaint *intermittently* fails to redraw correctly, and this fixes the problem.
                    document.body.style.WebkitTransform = '';
                }, 0);
            }
        });
    })();
    </script>
</body>
</html>

Potrebbe essere migliorato, ma per le mie esigenze evita i maggiori inconvenienti che si presentano con tutte le altre soluzioni che ho visto. Finora l'ho testato solo utilizzando Safari mobile su un iPad 2 con iOS4.

La messa a fuoco () / sfocatura () è una soluzione alternativa per prevenire il blocco occasionale della funzionalità di zoom che può verificarsi dopo aver cambiato l'orientamento e ingrandito alcune volte.

L'impostazione di document.body.style impone un ridisegno a schermo intero, che evita problemi occasionali intermittenti in cui il ridisegno fallisce gravemente dopo lo zoom.


0

Elisabeth puoi modificare dinamicamente il contenuto della visualizzazione aggiungendo la proprietà "id" al metatag:

<meta name="viewport" id="view" content="user-scalable=yes, width=device-width minimum-scale=1, maximum-scale=1" />

Quindi puoi semplicemente chiamare con javascript:

document.getElementById("view").setAttribute('content','user-scalable=yes, width=device-width, minimum-scale=1, maximum-scale=10');

@bridgestew se vuoi cambiare zoom o viewport in modo dinamico usa la sottoview scrollview contenuta nella uiwebview. Ho aggiunto uno snipet di esempio su un altro thread: link
M Penades

4
@Elisabeth funziona per te? Non ripristina lo zoom quando si passa alla modalità orizzontale per me.
esempio di me il

0

Ecco un altro modo per farlo, che sembra funzionare bene.

  1. Imposta il metatag per limitare la visualizzazione a scale = 1, che impedisce lo zoom:

    <meta name = "viewport" content = "width = device-width, initial-scale = 1, minimum-scale = 1, maximum-scale = 1">

  2. Con javascript, modifica il meta tag 1/2 secondo dopo per consentire lo zoom:

    setTimeout (function () {document.querySelector ("meta [name = viewport]"). setAttribute ('content', 'width = device-width, initial-scale = 1');}, 500);

  3. Sempre con javascript, al cambio di orientamento, ricarica la pagina:

    window.onorientationchange = function () {window.location.reload ();};

Ogni volta che riorienti il ​​dispositivo, la pagina si ricarica, inizialmente senza zoom. Ma 1/2 secondo dopo, viene ripristinata la capacità di zoomare.


6
Rispondere a una domanda 5 anni dopo che è stata posta è qualcosa .. Purtroppo non è così che funziona il web nel 2015. NON ricaricare la pagina quando l'utente ruota il suo dispositivo.
Pierre,

0

Ho trovato una soluzione molto semplice da implementare. Imposta lo stato attivo su un elemento di testo che ha una dimensione del carattere di 50 px al completamento del modulo. Non sembra funzionare se l'elemento di testo è nascosto, ma è facile nascondere questo elemento impostando le proprietà del colore degli elementi in modo che non abbiano opacità.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.