Fix: SafeArea for iOS dynamic island
Fixes iOS safe area issues caused by the dynamic island.
This commit is contained in:
@@ -21,7 +21,17 @@ const Header: React.FC = () => {
|
|||||||
|
|
||||||
// 플랫폼 감지
|
// 플랫폼 감지
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setIsIOS(isIOSPlatform());
|
const checkPlatform = async () => {
|
||||||
|
try {
|
||||||
|
const isiOS = isIOSPlatform();
|
||||||
|
console.log('Header: iOS 플랫폼 감지 결과:', isiOS);
|
||||||
|
setIsIOS(isiOS);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('플랫폼 감지 중 오류:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
checkPlatform();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// 이미지 프리로딩 처리
|
// 이미지 프리로딩 처리
|
||||||
@@ -37,7 +47,8 @@ const Header: React.FC = () => {
|
|||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const headerClass = isIOS ? 'has-safe-area-top' : 'py-4';
|
// iOS 전용 헤더 클래스 - 안전 영역 적용
|
||||||
|
const headerClass = isIOS ? 'ios-notch-padding' : 'py-4';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<header className={headerClass}>
|
<header className={headerClass}>
|
||||||
|
|||||||
@@ -25,15 +25,22 @@ const SafeAreaContainer: React.FC<SafeAreaContainerProps> = ({
|
|||||||
|
|
||||||
// 마운트 시 플랫폼 확인
|
// 마운트 시 플랫폼 확인
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setIsIOS(isIOSPlatform());
|
const checkPlatform = async () => {
|
||||||
|
const isiOS = isIOSPlatform();
|
||||||
|
console.log('SafeAreaContainer: 플랫폼 확인 - iOS:', isiOS);
|
||||||
|
setIsIOS(isiOS);
|
||||||
|
};
|
||||||
|
|
||||||
|
checkPlatform();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// 플랫폼에 따른 클래스 결정
|
// 플랫폼에 따른 클래스 결정
|
||||||
let safeAreaClass = '';
|
let safeAreaClass = 'safe-area-container';
|
||||||
|
|
||||||
if (isIOS) {
|
if (isIOS) {
|
||||||
if (!bottomOnly) safeAreaClass += ' has-safe-area-top'; // iOS 상단 안전 영역
|
if (!bottomOnly) safeAreaClass += ' has-safe-area-top'; // iOS 상단 안전 영역
|
||||||
if (!topOnly) safeAreaClass += ' has-safe-area-bottom'; // iOS 하단 안전 영역
|
if (!topOnly) safeAreaClass += ' has-safe-area-bottom'; // iOS 하단 안전 영역
|
||||||
|
safeAreaClass += ' ios-safe-area'; // iOS 전용 클래스 추가
|
||||||
} else {
|
} else {
|
||||||
if (!bottomOnly) safeAreaClass += ' pt-4'; // 안드로이드 상단 여백
|
if (!bottomOnly) safeAreaClass += ' pt-4'; // 안드로이드 상단 여백
|
||||||
if (!topOnly) safeAreaClass += ' pb-4'; // 안드로이드 하단 여백
|
if (!topOnly) safeAreaClass += ' pb-4'; // 안드로이드 하단 여백
|
||||||
@@ -42,6 +49,30 @@ const SafeAreaContainer: React.FC<SafeAreaContainerProps> = ({
|
|||||||
// 추가 하단 여백 적용
|
// 추가 하단 여백 적용
|
||||||
const extraBottomClass = extraBottomPadding ? 'pb-[80px]' : '';
|
const extraBottomClass = extraBottomPadding ? 'pb-[80px]' : '';
|
||||||
|
|
||||||
|
// 디버그용 로그 추가
|
||||||
|
useEffect(() => {
|
||||||
|
if (isIOS) {
|
||||||
|
console.log('SafeAreaContainer: iOS 안전 영역 적용됨', {
|
||||||
|
topOnly,
|
||||||
|
bottomOnly,
|
||||||
|
extraBottomPadding
|
||||||
|
});
|
||||||
|
|
||||||
|
// 안전 영역 값 확인 (CSS 변수)
|
||||||
|
try {
|
||||||
|
const computedStyle = getComputedStyle(document.documentElement);
|
||||||
|
console.log('Safe area 변수 값:', {
|
||||||
|
top: computedStyle.getPropertyValue('--safe-area-top'),
|
||||||
|
bottom: computedStyle.getPropertyValue('--safe-area-bottom'),
|
||||||
|
left: computedStyle.getPropertyValue('--safe-area-left'),
|
||||||
|
right: computedStyle.getPropertyValue('--safe-area-right')
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('CSS 변수 확인 중 오류:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [isIOS, topOnly, bottomOnly]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`${safeAreaClass} ${extraBottomClass} ${className}`}>
|
<div className={`${safeAreaClass} ${extraBottomClass} ${className}`}>
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
@@ -52,11 +52,11 @@
|
|||||||
|
|
||||||
--sidebar-ring: 217.2 91.2% 59.8%;
|
--sidebar-ring: 217.2 91.2% 59.8%;
|
||||||
|
|
||||||
/* Safe area 값 */
|
/* Safe area 값 - !important 추가하여 우선 적용 */
|
||||||
--safe-area-top: env(safe-area-inset-top, 0px);
|
--safe-area-top: env(safe-area-inset-top, 0px) !important;
|
||||||
--safe-area-bottom: env(safe-area-inset-bottom, 0px);
|
--safe-area-bottom: env(safe-area-inset-bottom, 0px) !important;
|
||||||
--safe-area-left: env(safe-area-inset-left, 0px);
|
--safe-area-left: env(safe-area-inset-left, 0px) !important;
|
||||||
--safe-area-right: env(safe-area-inset-right, 0px);
|
--safe-area-right: env(safe-area-inset-right, 0px) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark {
|
.dark {
|
||||||
@@ -150,17 +150,29 @@
|
|||||||
@apply neuro-pressed px-4 py-3 w-full focus:outline-none focus:ring-2 focus:ring-neuro-accent/30;
|
@apply neuro-pressed px-4 py-3 w-full focus:outline-none focus:ring-2 focus:ring-neuro-accent/30;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 안전 영역 관련 클래스 */
|
/* 안전 영역 관련 클래스 - 강화된 버전 */
|
||||||
|
.safe-area-container {
|
||||||
|
width: 100%;
|
||||||
|
position: relative;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ios-safe-area {
|
||||||
|
/* iOS 전용 안전 영역 처리 */
|
||||||
|
position: relative;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
.has-safe-area-top {
|
.has-safe-area-top {
|
||||||
padding-top: max(1rem, var(--safe-area-top));
|
padding-top: max(1rem, var(--safe-area-top)) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.has-safe-area-bottom {
|
.has-safe-area-bottom {
|
||||||
padding-bottom: max(1rem, var(--safe-area-bottom));
|
padding-bottom: max(1rem, var(--safe-area-bottom)) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ios-header {
|
.ios-header {
|
||||||
padding-top: max(1rem, var(--safe-area-top));
|
padding-top: max(1rem, var(--safe-area-top)) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 모바일 화면에서의 추가 스타일 */
|
/* 모바일 화면에서의 추가 스타일 */
|
||||||
@@ -186,6 +198,11 @@
|
|||||||
.SheetContent {
|
.SheetContent {
|
||||||
@apply rounded-xl overflow-hidden;
|
@apply rounded-xl overflow-hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* iOS 고유 노치/다이나믹 아일랜드 영역 고려한 추가 스타일 */
|
||||||
|
.ios-safe-area-screen {
|
||||||
|
padding: var(--safe-area-top) var(--safe-area-right) var(--safe-area-bottom) var(--safe-area-left) !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 데스크탑 화면에서의 추가 스타일 */
|
/* 데스크탑 화면에서의 추가 스타일 */
|
||||||
@@ -260,11 +277,21 @@
|
|||||||
/* iOS 전용 스타일 */
|
/* iOS 전용 스타일 */
|
||||||
@supports (-webkit-touch-callout: none) {
|
@supports (-webkit-touch-callout: none) {
|
||||||
.ios-safe-area-top {
|
.ios-safe-area-top {
|
||||||
padding-top: var(--safe-area-top);
|
padding-top: var(--safe-area-top) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ios-safe-area-bottom {
|
.ios-safe-area-bottom {
|
||||||
padding-bottom: var(--safe-area-bottom);
|
padding-bottom: var(--safe-area-bottom) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* iOS에서 노치/다이나믹 아일랜드 영역 처리를 위한 추가 클래스 */
|
||||||
|
.ios-notch-padding {
|
||||||
|
padding-top: max(1.5rem, var(--safe-area-top)) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* iOS에서 하단 홈 인디케이터 영역을 위한 추가 클래스 */
|
||||||
|
.ios-bottom-padding {
|
||||||
|
padding-bottom: max(1.5rem, var(--safe-area-bottom)) !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,26 @@ import { useDataInitialization } from '@/hooks/useDataInitialization';
|
|||||||
import { useIsMobile } from '@/hooks/use-mobile';
|
import { useIsMobile } from '@/hooks/use-mobile';
|
||||||
import useNotifications from '@/hooks/useNotifications';
|
import useNotifications from '@/hooks/useNotifications';
|
||||||
import SafeAreaContainer from '@/components/SafeAreaContainer';
|
import SafeAreaContainer from '@/components/SafeAreaContainer';
|
||||||
|
import { BudgetData } from '@/contexts/budget/types';
|
||||||
|
|
||||||
|
// 기본 예산 데이터 (빈 객체 대신 사용할 더미 데이터)
|
||||||
|
const defaultBudgetData: BudgetData = {
|
||||||
|
daily: {
|
||||||
|
targetAmount: 0,
|
||||||
|
spentAmount: 0,
|
||||||
|
remainingAmount: 0
|
||||||
|
},
|
||||||
|
weekly: {
|
||||||
|
targetAmount: 0,
|
||||||
|
spentAmount: 0,
|
||||||
|
remainingAmount: 0
|
||||||
|
},
|
||||||
|
monthly: {
|
||||||
|
targetAmount: 0,
|
||||||
|
spentAmount: 0,
|
||||||
|
remainingAmount: 0
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// 메인 컴포넌트
|
// 메인 컴포넌트
|
||||||
const Index = () => {
|
const Index = () => {
|
||||||
@@ -69,7 +89,7 @@ const Index = () => {
|
|||||||
try {
|
try {
|
||||||
console.log('Index 페이지 마운트, 현재 데이터 상태:');
|
console.log('Index 페이지 마운트, 현재 데이터 상태:');
|
||||||
console.log('트랜잭션:', transactions?.length || 0);
|
console.log('트랜잭션:', transactions?.length || 0);
|
||||||
console.log('예산 데이터:', budgetData);
|
console.log('예산 데이터:', budgetData || defaultBudgetData);
|
||||||
|
|
||||||
// 페이지 첫 마운트 시에만 실행되는 로직으로 수정
|
// 페이지 첫 마운트 시에만 실행되는 로직으로 수정
|
||||||
const isFirstMount = sessionStorage.getItem('initialDataLoaded') !== 'true';
|
const isFirstMount = sessionStorage.getItem('initialDataLoaded') !== 'true';
|
||||||
@@ -194,7 +214,7 @@ const Index = () => {
|
|||||||
|
|
||||||
<HomeContent
|
<HomeContent
|
||||||
transactions={transactions || []}
|
transactions={transactions || []}
|
||||||
budgetData={budgetData || {}}
|
budgetData={budgetData || defaultBudgetData}
|
||||||
selectedTab={selectedTab}
|
selectedTab={selectedTab}
|
||||||
setSelectedTab={setSelectedTab}
|
setSelectedTab={setSelectedTab}
|
||||||
handleBudgetGoalUpdate={handleBudgetGoalUpdate}
|
handleBudgetGoalUpdate={handleBudgetGoalUpdate}
|
||||||
|
|||||||
Reference in New Issue
Block a user