|
1 | 1 | /** |
2 | 2 | * 液态玻璃容器。 |
3 | | - * 用 expo-blur 实现真实毛玻璃(模糊层 + 薄边 + 顶部高光)。 |
| 3 | + * iOS 26+ 用 expo-glass-effect(系统 UIGlassEffect);旧 iOS 回退 expo-blur 毛玻璃。 |
| 4 | + * 换掉 UIVisualEffectView 方案的原因:它的高斯模糊会越过 bounds 向外渗出约一个 |
| 5 | + * blur radius,iOS 26 上 RN 层的裁剪约束不住(Apple 标注裁剪其宿主为未定义行为), |
| 6 | + * composer 顶边上方会浮出一条把消息糊住的毛玻璃光晕。UIGlassEffect 严格按 bounds 渲染。 |
4 | 7 | */ |
5 | 8 | import { BlurView } from 'expo-blur'; |
6 | 9 | import React from 'react'; |
7 | 10 | import { Platform, StyleSheet, View, type StyleProp, type ViewStyle } from 'react-native'; |
8 | 11 | import { useTheme } from '@/theme'; |
9 | 12 |
|
| 13 | +// 懒加载 + 容错:OTA 包可能跑在还没编入 ExpoGlassEffect 原生模块的旧安装包上, |
| 14 | +// 顶层 import 触发 requireNativeModule 抛错会直接崩 App;失败则永远走 BlurView 回退。 |
| 15 | +let glassEffectMod: typeof import('expo-glass-effect') | null | undefined; |
| 16 | +function liquidGlassModule(): typeof import('expo-glass-effect') | null { |
| 17 | + if (glassEffectMod === undefined) { |
| 18 | + try { |
| 19 | + const mod = require('expo-glass-effect') as typeof import('expo-glass-effect'); |
| 20 | + glassEffectMod = mod.isLiquidGlassAvailable() ? mod : null; |
| 21 | + } catch { |
| 22 | + glassEffectMod = null; |
| 23 | + } |
| 24 | + } |
| 25 | + return glassEffectMod; |
| 26 | +} |
| 27 | + |
10 | 28 | interface GlassProps { |
11 | 29 | children?: React.ReactNode; |
12 | 30 | style?: StyleProp<ViewStyle>; |
@@ -42,6 +60,20 @@ export function Glass({ children, style, radius = 0, intensity = 36, border = tr |
42 | 60 | ); |
43 | 61 | } |
44 | 62 |
|
| 63 | + // iOS 26+:系统液态玻璃。intensity 在此路径无效(材质强度由系统定),观感靠 veil 统一; |
| 64 | + // colorScheme 跟随 App 内主题开关而非系统外观,避免强制深/浅色时玻璃底色错位。 |
| 65 | + const glassEffect = liquidGlassModule(); |
| 66 | + if (glassEffect) { |
| 67 | + const { GlassView } = glassEffect; |
| 68 | + return ( |
| 69 | + <View style={[radiusStyle, shadow && t.shLift, style]}> |
| 70 | + <GlassView glassEffectStyle="regular" colorScheme={t.glassTint} style={[StyleSheet.absoluteFill, radiusStyle]} /> |
| 71 | + <View style={[StyleSheet.absoluteFill, radiusStyle, { backgroundColor: t.glassVeil }, borderStyle]} /> |
| 72 | + {children} |
| 73 | + </View> |
| 74 | + ); |
| 75 | + } |
| 76 | + |
45 | 77 | return ( |
46 | 78 | <View style={[radiusStyle, shadow && t.shLift, style]}> |
47 | 79 | <BlurView |
|
0 commit comments