Ipad适配问题

This commit is contained in:
吕新雨
2026-03-03 19:55:53 +08:00
parent 0ad21da246
commit d37262876b
20 changed files with 692 additions and 147 deletions

View File

@@ -2,13 +2,13 @@ import { useEffect, useLayoutEffect, useMemo, useState, useCallback, useRef } fr
import {
StyleSheet,
View,
Dimensions,
Text,
Pressable,
PanResponder,
Animated as RNAnimated,
ImageBackground,
Platform,
useWindowDimensions,
} from 'react-native';
import { useTranslation } from 'react-i18next';
import { useFocusEffect } from 'expo-router';
@@ -57,8 +57,6 @@ import { wrapText } from '@/src/features/textWrap';
import { defaultMeasureWidthImpl } from '@/src/features/textWrap/measure';
import { ensureDailyWidgetRecoUpToDate } from '@/src/modules/dailyWidgetReco';
const { height: SCREEN_HEIGHT } = Dimensions.get('window');
// 预定义风景图列表
const NATURE_IMAGES = [
require('@/assets/theme/nature/1.png'),
@@ -97,7 +95,9 @@ type FeedItem = { content_id: string; text: string };
export default function HomeScreen() {
const { t, i18n } = useTranslation();
const { width: windowWidth, height: windowHeight } = useWindowDimensions();
const isEnglish = i18n.language?.startsWith('en');
const isTablet = Platform.OS === 'ios' && Math.min(windowWidth, windowHeight) >= 768;
const recoLang: 'en' | 'tc' = toBackendLocaleFromLanguageTag(i18n.language);
const insets = useSafeAreaInsets();
const [index, setIndex] = useState(0);
@@ -248,8 +248,8 @@ export default function HomeScreen() {
});
const fontSpec = {
fontSize: 22,
fontWeight: lang === 'EN' ? '600' : '700',
fontSize: 24,
fontWeight: lang === 'EN' ? '700' : '800',
fontFamily: String(fontFamily ?? 'System'),
};
@@ -659,6 +659,10 @@ export default function HomeScreen() {
}
}
const actionsBottom = isTablet
? Math.max(insets.bottom + 36, Math.min(windowHeight * 0.12, 140))
: windowHeight * 0.16;
return (
<View style={[styles.container, { backgroundColor }]} {...panResponder.panHandlers}>
{themeMode === 'scenery' && (
@@ -670,7 +674,15 @@ export default function HomeScreen() {
)}
{/* 自绘顶部按钮:不使用系统 Header彻底避免 iOS 导航栏的毛玻璃/液玻璃材质 */}
<View style={[styles.topRight, { top: insets.top + 8 }]}>
<View
style={[
styles.topRight,
{
top: insets.top + (isTablet ? 16 : 8),
right: isTablet ? 26 : 20,
},
]}
>
<CircleIconButton
onPress={() => setThemeOpen(true)}
accessibilityLabel={t('home.theme')}
@@ -685,19 +697,21 @@ export default function HomeScreen() {
</CircleIconButton>
</View>
<Animated.View
style={[styles.card, textAnimatedStyle, themeMode === 'scenery' && styles.sceneryCard]}
onLayout={(e) => {
const w = e.nativeEvent.layout.width;
if (Number.isFinite(w) && w > 0) setCardWidth(w);
}}
>
<Text style={[styles.text, isEnglish && styles.textEnglish, themeMode === 'scenery' && styles.sceneryText]}>
{wrappedText || item.text}
</Text>
<Animated.View style={[styles.card, textAnimatedStyle, themeMode === 'scenery' && styles.sceneryCard]}>
<View
style={[styles.textMeasureBox, isTablet && styles.textMeasureBoxTablet]}
onLayout={(e) => {
const w = e.nativeEvent.layout.width;
if (Number.isFinite(w) && w > 0) setCardWidth(w);
}}
>
<Text style={[styles.text, isEnglish && styles.textEnglish, themeMode === 'scenery' && styles.sceneryText]}>
{wrappedText || item.text}
</Text>
</View>
</Animated.View>
<View style={styles.actions}>
<View style={[styles.actions, { bottom: actionsBottom }]}>
<Animated.View style={[styles.reactionButton, likeAnimatedStyle]}>
<Pressable
onPress={onPressLike}
@@ -762,7 +776,6 @@ const styles = StyleSheet.create({
},
topRight: {
position: 'absolute',
right: 20,
flexDirection: 'row',
gap: 10,
zIndex: 30,
@@ -787,16 +800,23 @@ const styles = StyleSheet.create({
zIndex: 5, // 降低层级,防止遮挡底部按钮
},
text: {
fontSize: 22,
lineHeight: 32,
fontSize: 24,
lineHeight: 34,
color: '#5E2A28',
fontWeight: '700',
fontWeight: '800',
textAlign: 'center',
},
textMeasureBox: {
width: '100%',
alignItems: 'center',
},
textMeasureBoxTablet: {
maxWidth: 760,
},
textEnglish: {
fontFamily: 'STIXTwoText',
// 英文字体观感更细一点,避免过粗
fontWeight: '600',
// 英文字体保持较粗但避免过度发黑
fontWeight: '700',
},
sceneryCard: {
// 风景模式下稍微收窄文案宽度,增加呼吸感
@@ -810,7 +830,6 @@ const styles = StyleSheet.create({
},
actions: {
position: 'absolute',
bottom: SCREEN_HEIGHT * 0.16,
left: 0,
right: 0,
flexDirection: 'row',

View File

@@ -1,9 +1,13 @@
import Constants from 'expo-constants';
import { StyleSheet, Text, View } from 'react-native';
import { Platform, StyleSheet, Text, View, useWindowDimensions } from 'react-native';
import { useTranslation } from 'react-i18next';
import { clampContentWidth, isIPadLike } from '@/src/utils/device';
export default function SettingsScreen() {
const { t } = useTranslation();
const { width, height } = useWindowDimensions();
const isTablet = isIPadLike(width, height);
const contentWidth = isTablet ? clampContentWidth(width, 720, 24) : undefined;
const version =
Constants.expoConfig?.version ??
@@ -12,6 +16,7 @@ export default function SettingsScreen() {
return (
<View style={styles.container}>
<View style={[styles.contentWrap, contentWidth ? { width: contentWidth } : null]}>
<View style={styles.section}>
<Text style={styles.label}>{t('settings.version')}</Text>
<Text style={styles.value}>{version}</Text>
@@ -21,12 +26,18 @@ export default function SettingsScreen() {
<Text style={styles.cardTitle}>{t('settings.widgetTitle')}</Text>
<Text style={styles.cardText}>{t('settings.widgetDesc')}</Text>
</View>
</View>
</View>
);
}
const styles = StyleSheet.create({
container: { flex: 1, padding: 16, gap: 16 },
container: { flex: 1, width: '100%', alignSelf: 'stretch', padding: 16 },
contentWrap: {
width: '100%',
alignSelf: 'center',
gap: 16,
},
section: {
borderRadius: 14,
padding: 16,
@@ -38,7 +49,12 @@ const styles = StyleSheet.create({
justifyContent: 'space-between',
},
label: { color: '#374151', fontSize: 16 },
value: { color: '#111827', fontSize: 16, fontWeight: '600' },
value: {
color: '#111827',
fontSize: 16,
fontWeight: '600',
fontFamily: Platform.OS === 'ios' ? 'STIX Two Text' : undefined,
},
card: {
borderRadius: 16,
padding: 16,