React Native Expo 横竖屏适配最全讲解

48 阅读4分钟

1. iPad可以旋转屏幕而iOS(iPhone)不能,是否是Expo的默认设置?

是的。在Expo中,默认通过app.json"orientation": "portrait"设置全局锁定为纵向,但iPad由于支持分屏(Split View)功能,系统会强制允许旋转,除非显式禁用分屏支持。而iPhone默认会遵守app.json的配置,锁定为纵向。

原因:苹果从iOS 9开始为iPad引入分屏模式,导致屏幕方向优先级由系统控制。若需完全锁定iPad方向,需额外禁用分屏功能(但Expo默认未禁用)。


2. 如何分别设置iPad和iOS支持/不支持旋转的四种情况?

以下是针对 iPad 和 iOS 设备(iPhone)分别设置支持/不支持屏幕旋转的四种情况的详细配置和代码实现:

1. iPad 支持旋转,iPhone 不支持旋转

配置步骤:
  1. app.json** 默认锁定纵向**(控制 iPhone 行为):

    1. {
        "expo": {
          "orientation": "portrait"
        }
      }
      
  2. 代码中动态允许 iPad 旋转

    1. import { Platform } from 'react-native';
      import * as ScreenOrientation from 'expo-screen-orientation';
      
      // 在根组件(如 App.js)中调用
      async function setOrientation() {
        if (Platform.OS === 'ios' && Platform.isPad) { // 检测 iPad
          await ScreenOrientation.unlockAsync(); // 允许旋转
        } else {
          await ScreenOrientation.lockAsync(ScreenOrientation.OrientationLock.PORTRAIT); // 锁定 iPhone
        }
      }
      

2. iPad 不支持旋转,iPhone 支持旋转

配置步骤:
  1. app.json** 设置默认方向为 ****default**(允许 iPhone 旋转):

    1. {
        "expo": {
          "orientation": "default"
        }
      }
      
  2. 代码中强制锁定 iPad 方向

    1. async function setOrientation() {
        if (Platform.OS === 'ios' && Platform.isPad) {
          await ScreenOrientation.lockAsync(ScreenOrientation.OrientationLock.PORTRAIT); // 锁定 iPad
        }
      }
      
  3. 禁用 iPad 分屏功能(关键步骤) :在 app.json 中添加 iOS 原生配置:

    1. {
        "expo": {
          "ios": {
            "requireFullScreen": true // 禁用分屏,强制锁定方向
          }
        }
      }
      

3. iPad 和 iPhone 都支持旋转

配置步骤:
  1. app.json** 中设置 ****default**

    1. {
        "expo": {
          "orientation": "default"
        }
      }
      
  2. 代码中显式解锁方向(可选)

    1. async function enableRotation() {
        await ScreenOrientation.unlockAsync();
      }
      

4. iPad 和 iPhone 都不支持旋转

配置步骤:
  1. app.json** 中全局锁定方向**:

    1. {
        "expo": {
          "orientation": "portrait"
        }
      }
      
  2. 代码中强制锁定方向(确保可靠性)

    1. async function lockAll() {
        await ScreenOrientation.lockAsync(ScreenOrientation.OrientationLock.PORTRAIT);
      }
      

完整代码示例

import React, { useEffect } from 'react';
import { Platform } from 'react-native';
import * as ScreenOrientation from 'expo-screen-orientation';

export default function App() {
  useEffect(() => {
    configureOrientation();
  }, []);

  async function configureOrientation() {
    if (Platform.OS === 'ios') {
      if (Platform.isPad) {
        // iPad 逻辑
        // 情况1:支持旋转(注释下一行)
        // await ScreenOrientation.unlockAsync();
        
        // 情况2:不支持旋转
        await ScreenOrientation.lockAsync(ScreenOrientation.OrientationLock.PORTRAIT);
      } else {
        // iPhone 逻辑
        // 情况3:支持旋转(注释下一行)
        // await ScreenOrientation.unlockAsync();

        // 情况4:不支持旋转
        await ScreenOrientation.lockAsync(ScreenOrientation.OrientationLock.PORTRAIT);
      }
    }
  }

  return (
    // 你的应用组件
  );
}

关键注意事项:

  1. iPad 分屏模式:iPad 方向锁定需同时禁用分屏(通过 "requireFullScreen": true),否则系统会强制允许旋转。

  2. 动态检测设备类型

    1. 使用 Platform.isPad 判断 iPad(需 React Native 0.63+)。

    2. 兼容旧版本可通过屏幕尺寸判断:

      • const { width, height } = Dimensions.get('window');
        const isPad = Platform.OS === 'ios' && (width >= 768 || height >= 768);
        
  3. Expo 配置限制:若需更精细控制(如不同界面不同方向),需使用裸工作流(ejected project)修改原生代码。


3. 导航栏未响应横竖屏样式的问题

原因:React Navigation的顶部栏默认未监听屏幕方向变化,需手动触发布局更新。

解决方案

  • 监听屏幕尺寸变化

    • import { Dimensions } from 'react-native';
      const [dimensions, setDimensions] = useState(Dimensions.get('window'));
      
      useEffect(() => {
        const subscription = Dimensions.addEventListener('change', ({ window }) => {
          setDimensions(window);
        });
        return () => subscription?.remove();
      }, []);
      
  • 动态调整导航栏样式

    • <NavigationContainer>
        <Stack.Navigator
          screenOptions={{
            headerStyle: {
              width: dimensions.width, // 根据屏幕宽度调整
            },
          }}
        >
        </Stack.Navigator>
      </NavigationContainer>
      
    •   结合媒体查询库(如@expo/match-media)或响应式单位(如vw)优化布局。


4. iOS/iPad的响应式设计实践

核心方法:

  • Flexbox布局:优先使用弹性布局,通过flex属性分配空间,适应不同屏幕尺寸。

  • Dimensions API:动态获取屏幕尺寸:

    • const { width, height } = Dimensions.get('window');
      
  • 媒体查询:使用@expo/match-media实现条件渲染:

    • import { useMediaQuery } from 'react-responsive';
      const isTablet = useMediaQuery({ query: '(min-width: 768px)' });
      
  • 分屏适配:通过onLayout事件监听容器尺寸变化,动态调整子元素布局。


5. iPad分屏显示设置与样式设计

分屏启用条件:

  • Expo项目:默认支持分屏,但需确保app.json中未设置"orientation": "portrait"(或动态解锁方向)。

  • 原生项目:在Xcode中勾选所有支持的方向(如Portrait、Landscape)。

分屏样式设计:

  • 自适应布局:使用百分比(width: '50%')或flex: 1让内容自动填充可用空间。

  • 多列布局:分屏时切换为水平排列:

    • <View style={{ flexDirection: dimensions.width > 768 ? 'row' : 'column' }}>
        <View style={{ flex: 1 }}>左面板</View>
        <View style={{ flex: 1 }}>右面板</View>
      </View>
      
  • 组件响应:通过useWindowDimensionsonLayout实时调整组件尺寸。


总结

  • 方向控制:通过expo-screen-orientation动态管理,区分iPad和iPhone的逻辑。
  • 响应式设计:结合Flexbox、Dimensions API和媒体查询,适配多尺寸场景。
  • 分屏优化:利用动态布局和事件监听实现分屏适配,避免固定尺寸导致内容截断。