You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在React Native中创建iOS风格底部弹出式模态窗口?

在React Native中实现iOS底部弹出式模态窗口(即iOS原生Page Sheet模态)

你要找的这种iOS原生底部弹出窗口官方名称是Page Sheet,React Native自带的Modal组件就可以实现,以下是具体方案:

一、iOS原生样式实现(官方方案)

通过Modal的presentationStyle属性设置为pageSheet,即可实现和日历App一致的底部弹出效果,同时自定义顶部的取消/保存按钮与标题:

完整代码示例

import React, { useState } from 'react';
import { View, Text, Modal, TouchableOpacity, StyleSheet } from 'react-native';

const PageSheetModal = () => {
  const [isModalVisible, setIsModalVisible] = useState(false);

  return (
    <View style={styles.screenContainer}>
      {/* 唤起按钮(模拟右上角加号) */}
      <TouchableOpacity 
        onPress={() => setIsModalVisible(true)} 
        style={styles.addBtn}
      >
        <Text style={styles.addBtnText}>+</Text>
      </TouchableOpacity>

      <Modal
        animationType="slide"
        transparent={false}
        visible={isModalVisible}
        // iOS核心属性:启用Page Sheet样式
        presentationStyle="pageSheet"
        onRequestClose={() => setIsModalVisible(false)}
      >
        {/* 顶部导航栏 */}
        <View style={styles.modalHeader}>
          <TouchableOpacity 
            onPress={() => setIsModalVisible(false)} 
            style={styles.headerBtn}
          >
            <Text style={styles.cancelText}>取消</Text>
          </TouchableOpacity>
          <Text style={styles.headerTitle}>新建事项</Text>
          <TouchableOpacity 
            onPress={() => {
              // 这里写保存逻辑
              setIsModalVisible(false);
            }} 
            style={styles.headerBtn}
          >
            <Text style={styles.saveText}>保存</Text>
          </TouchableOpacity>
        </View>

        {/* 自定义内容区域 */}
        <View style={styles.modalContent}>
          <Text>在此添加表单或其他内容</Text>
        </View>
      </Modal>
    </View>
  );
};

const styles = StyleSheet.create({
  screenContainer: {
    flex: 1,
    justifyContent: 'flex-end',
    alignItems: 'flex-end',
    padding: 20,
    backgroundColor: '#f5f5f5',
  },
  addBtn: {
    width: 44,
    height: 44,
    borderRadius: 22,
    backgroundColor: '#007AFF',
    justifyContent: 'center',
    alignItems: 'center',
  },
  addBtnText: {
    color: '#fff',
    fontSize: 24,
    fontWeight: 'bold',
  },
  modalHeader: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    alignItems: 'center',
    paddingHorizontal: 16,
    paddingVertical: 12,
    borderBottomWidth: 1,
    borderBottomColor: '#e5e5e5',
  },
  headerBtn: {
    paddingHorizontal: 8,
  },
  headerTitle: {
    fontSize: 17,
    fontWeight: '600',
  },
  cancelText: {
    fontSize: 17,
    color: '#007AFF',
  },
  saveText: {
    fontSize: 17,
    color: '#007AFF',
    fontWeight: '600',
  },
  modalContent: {
    flex: 1,
    padding: 16,
    justifyContent: 'center',
    alignItems: 'center',
  },
});

export default PageSheetModal;

关键说明

  • presentationStyle="pageSheet"仅支持iOS 13+,会自动适配系统的底部弹出交互(可拖拽调整高度、点击背景关闭等)。
  • Android端使用该组件时,会默认采用全屏模态,符合Android平台的设计规范。

二、Android端自定义底部弹出样式(可选)

如果需要Android端也实现底部弹出效果(而非全屏),可设置Modal为透明背景,自定义底部容器:

适配代码片段

// 添加到StyleSheet中
modalOverlay: {
  flex: 1,
  justifyContent: 'flex-end',
  backgroundColor: 'rgba(0,0,0,0.5)',
},
modalBottomContainer: {
  backgroundColor: '#fff',
  borderTopLeftRadius: 16,
  borderTopRightRadius: 16,
  minHeight: '50%',
},

修改Modal部分:

<Modal
  animationType="slide"
  transparent={true}
  visible={isModalVisible}
  onRequestClose={() => setIsModalVisible(false)}
>
  <View style={styles.modalOverlay}>
    <View style={styles.modalBottomContainer}>
      {/* 顶部导航栏 + 内容区域,复用之前的样式 */}
      <View style={styles.modalHeader}>...</View>
      <View style={styles.modalContent}>...</View>
    </View>
  </View>
</Modal>

三、第三方库方案(复杂场景可选)

若需要更多高级功能(如拖拽调整高度、手势关闭),可使用第三方库:

  • react-native-bottom-sheet:专门针对底部弹出面板的组件,支持跨平台自定义。
  • react-native-modal:提供更灵活的模态配置选项。

但官方Modal组件已能满足基础需求,优先推荐官方方案。

内容的提问来源于stack exchange,提问作者deekep

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 22:35:41