Problém 🤔
Každý den se musí technik React Native vypořádat se změnami orientace obrazovky a jejich zvláštnostmi a bolestivými body, ať už jde o pouhou změnu uživatelského rozhraní na základě zářezů na šířku, spouštění nových síťových hovorů nebo případné zobrazení/upuštění klávesnice při každém otočení. přístroj. Potřebujeme tedy řešení, které nám řekne, kdykoli dojde ke změně orientace obrazovky, a odtud můžeme zjistit, jaký je nejlepší postup.
Naivní přístup 👀
OK. Nyní tedy víme, co se snažíme vyřešit. Rychlým a snadným přístupem může být rychlé porovnání šířky obrazovky s výškou obrazovky.
Něco takového:
import { Dimensions } from 'react-native'
const isLandscape = () => Dimensions.get('window').width > Dimensions.get('window').height
Nevýhody:
-
Neřekne nám ve skutečnosti, jaká je přesná orientace obrazovky, jen zda jsme na výšku nebo na šířku.
-
Toto řešení nám dynamicky neříká, že došlo ke změně orientace obrazovky. Jaká je orientace obrazovky, když jsme tuto funkci volali. Pokud například používám navigaci React a vložím do zásobníku novou obrazovku, mohu zjistit orientaci obrazovky v okamžiku, kdy jsem obrazovku zatlačil. Ale řekněme, že poté otočím zařízení, stále uvidím předchozí hodnotu isLandscape pokud to znovu ručně nezavolám.
Lepší řešení 💪
Chceme nastavit háček, který naslouchá změnám orientace obrazovky a způsobí vykreslení pokaždé, když se orientace obrazovky v našem stavu změní.
OK. Abychom si usnadnili život, použiji dvě knihovny, o kterých si myslím, že se zde opravdu hodí a umožní nám neponořit se do nativního kódu a sami jej překlenout:
- react-native-orientation-locker
- react-singleton-hook
Upozornění 🚨
React-native-orientation-locker se zdá mít na Androidu chybu, která brání posluchači v konzistentním vysílání událostí (testováno na React Native 0.65.1). Zatím tedy používám základní řešení, dokud to nebude opraveno. Bohužel ztrácíme možnost přesně vědět, v jaké orientaci obrazovky se nacházíme.
Krok 1
Nastavíme základní hák s reakčním jednoduchým háčkem, který můžeme v dalším kroku rozšířit. Důvod, proč chceme vždy jen jeden háček, je ten, že nemáme více posluchačů, kteří poslouchají změny. Pokud například znovu používáte React Navigation a vložíte do zásobníku tři obrazovky, každá z těchto obrazovek by mohla mít nastaveny posluchače a nastavovat stav, i když nejsou viditelné.
import { singletonHook } from 'react-singleton-hook'
export const useScreenOrientation = singletonHook(
{
isLandscape: false,
screenOrientation: undefined,
},
() => {
return {
isLandscape: false,
screenOrientation
}
},
)
Krok 2
Po nastavení základního háku nyní můžeme začít přidávat některé funkce. Nejprve nastavíme orientaci obrazovky jako počáteční orientaci obrazovky zachycenou funkcí Reagovat-nativní orientace-locker a můžeme také přidat useState abyste to měli přehled.
import Orientation, { LANDSCAPE } from 'react-native-orientation-locker'
import { singletonHook } from 'react-singleton-hook'
export const useScreenOrientation = singletonHook(
{
isLandscape: false,
screenOrientation: Orientation.getInitialOrientation(),
},
() => {
const [screenOrientation, setScreenOrientation] = useState(Orientation.getInitialOrientation())
return {
isLandscape: screenOrientation.includes(LANDSCAPE),
screenOrientation
}
},
)
Krok 3
OK. Nyní k hlavní části tohoto problému, musíme naslouchat změnám orientace obrazovky. Mám zde malou pomocnou funkci, kterou používám všude. Bude se to hodit kvůli dříve zmíněnému upozornění a jen mi to říká, zda jsme nebo nejsme na zařízení Android.
import { Platform } from 'react-native'
export const isAndroid = () => Platform.OS === 'android'
Níže nastavím useEffect který se spustí pouze jednou, protože nemá žádné závislosti, a poté nastavte dva posluchače, jeden pro iOS, který používá response-native-orientation-locker, a druhý pro Android, který používá posluchače událostí dimenzí ze samotného React Native (Nezapomeňte odebrat posluchači, když je háček zničen). V podstatě pak při změně orientace obrazovky nastavíme stav na správný OrientationType (NA VÝŠKU, NA VÝŠKU-Vzhůru DOLŮ, NA KRAJINU-VLEVO, NA KRAJINKU-VPRAVO). U Androidu jen zkontrolujeme výšku oproti šířce, abychom poznali, zda je to na výšku nebo na šířku. Samozřejmě, že když si to přečtete, tento reaktivní-nativní-orientační-locker funguje konzistentně pro Android, nebudete potřebovat žádný z tohoto specifického kódu Android.
import { useEffect, useState } from 'react'
import { Dimensions, ScaledSize } from 'react-native'
import Orientation, { LANDSCAPE, OrientationType } from 'react-native-orientation-locker'
import { singletonHook } from 'react-singleton-hook'
export const useScreenOrientation = singletonHook(
{
isLandscape: false,
screenOrientation: Orientation.getInitialOrientation(),
},
() => {
const [screenOrientation, setScreenOrientation] = useState(Orientation.getInitialOrientation())
useEffect(() => {
const onChange = (result: OrientationType) => {
setScreenOrientation(result)
}
const onChangeAndroid = (result: { screen: ScaledSize }) => {
return onChange(
result.screen.height > result.screen.width
? OrientationType.PORTRAIT
: OrientationType['LANDSCAPE-LEFT'],
)
}
if (isAndroid()) {
Dimensions.addEventListener('change', onChangeAndroid)
} else {
Orientation.addOrientationListener(onChange)
}
return () => {
if (isAndroid()) {
Dimensions.removeEventListener('change', onChangeAndroid)
} else {
Orientation.removeOrientationListener(onChange)
}
}
}, [])
return {
isLandscape: screenOrientation.includes(LANDSCAPE),
screenOrientation
}
},
)
Dotáhli jste to až do konce! 🎉
Děkuji za přečtení. Bylo to poprvé, co jsem se pokusil něco takového napsat. Řekněte mi, co by se podle vás mohlo zlepšit, a já se pokusím tato vylepšení začlenit do budoucích.