케이스 ce-0285-ios-viewport-keyboard-safari-ko · 시나리오 scenario-ios-viewport-keyboard

iOS Safari에서 키보드 가시 시 viewport 메커니즘 이상

OS: iOS 16+ 기기: Mobile (iPhone/iPad) Any 브라우저: Safari 16+ 키보드: English (QWERTY) or iOS Virtual Keyboard 초안
iossafarikeyboardviewportposition-fixed

현상

iOS Safari에서 소프트웨어 키보드가 가시적일 때, viewport 메커니즘이 깨져서 position:fixed 요소와 viewport 계산이 올바르게 동작하지 않습니다.

재현 예시

  1. iPhone 또는 iPad의 Safari 브라우저를 엽니다.
  2. 페이지 로드합니다. position:fixed 요소가 올바르게 표시됩니다.
  3. contenteditable 요소에 탭하여 키보드를 나타냅니다.
  4. ❌ position:fixed 요소가 화면 밖으로 이동하거나 틀린 위치에 감습니다.
  5. window.innerHeight 값이 이상하게 변합니다.

관찰된 동작

  • position:fixed 깨짐: 키보드가 나타나면 fixed 요소의 위치가 계산이 틀려짐
  • viewport 높이 오차: window.innerHeight가 올바르지 않은 값을 반환 (키보드 영역을 제대로 반영 안함)
  • UI 사용 불가: 래딩 툴바나 메뉴가 제대로 작동하지 않아 사용자가 편집 불가
  • 키보드 숨김 후 복구 안됨: 키보드를 숨겨도 viewport 높이가 복구되지 않음
  • iOS Safari 특유: iPhone/iPad Safari에서만 심하게 발생

예상 동작

  • position:fixed 요소가 키보드 상태와 상관없이 항상 올바른 위치에 있어야 함
  • viewport 관련 CSS 속성이 올바르게 계산되어야 함
  • 사용자는 키보드가 있든 없든 UI를 정상적으로 사용할 수 있어야 함

참고사항 및 가능한 해결 방향

  • resize 이벤트 모니터링: 키보드 표시/숨김 감지
  • iOS Visual Viewport API: experimental이지만 더 정확한 viewport 정보 제공 가능
  • CSS viewport-fit 사용: <meta name="viewport" content="viewport-fit=cover"> 시도
  • position: absolute로 대안: position:fixed 대신 position: absolute 사용
  • 사용자 제어: “완료” 버튼 추가하여 사용자가 직접 키보드 조작
  • 안전 영역 확보: 래딩 UI에 충분한 여백(margin/padding) 추가
  • transform 사용: position 대신 transform: translate 사용 시도 (일부 경우 도움)

코드 예시

const editor = document.querySelector('[contenteditable]');
const fixedElement = document.querySelector('[style*="position: fixed"]');

let initialHeight = window.innerHeight;
let keyboardVisible = false;

// 키보드 감지 (iOS 특유)
window.addEventListener('resize', () => {
  const currentHeight = window.innerHeight;
  const heightDiff = initialHeight - currentHeight;
  
  if (heightDiff > 150) {
    // 키보드 나타남
    keyboardVisible = true;
    console.log('키보드 가시:', currentHeight, '원래:', initialHeight);
    
    // UI 위치 조정
    adjustUIForKeyboard(true);
  } else if (heightDiff < 100 && keyboardVisible) {
    // 키보드 숨김
    keyboardVisible = false;
    console.log('키보드 숨김:', currentHeight);
    
    // UI 위치 복구
    adjustUIForKeyboard(false);
  }
  
  initialHeight = currentHeight;
});

// UI 조정 함수
function adjustUIForKeyboard(isKeyboardVisible) {
  if (isKeyboardVisible) {
    // 키보드가 있을 때: UI를 위로 이동
    // position:fixed 요소를 안전한 위치로 조정
    if (fixedElement) {
      fixedElement.style.transform = 'translateY(0)';
      // 또는 position: absolute로 변경 시도
    }
  } else {
    // 키보드가 없을 때: UI 원위치 복구
    if (fixedElement) {
      fixedElement.style.transform = 'none';
    }
  }
}

// Visual Viewport API (iOS 지원 시)
if (window.visualViewport) {
  console.log('Visual Viewport:', window.visualViewport);
}

// 완료 버튼 추가 (사용자가 키보드 직접 조작 가능하게)
const doneButton = document.createElement('button');
doneButton.textContent = '완료';
doneButton.style.cssText = 'margin-top: 10px; padding: 10px 20px; background: #007AFF; color: white; border: none; border-radius: 4px;';

doneButton.addEventListener('click', () => {
  editor.blur(); // 키보드 숨김
  
  setTimeout(() => {
    // 키보드가 숨길 때까지 기다린 후 포커스 복구
    editor.focus();
  }, 300);
});

document.body.appendChild(doneButton);
/* CSS 해결책 시도 */
.fixed-element {
  /* 대안 1: position: absolute로 변경 */
  /* position: absolute;
     bottom: 10vh;
  */
  
  /* 대안 2: transform 사용 */
  transform: translate3d(0, 0, 0);
  
  /* 안전 영역 확보 */
  margin-bottom: 50px; /* 래딩 UI를 위한 여백 */
}

[contenteditable] {
  /* 충분한 최소 높이 확보 */
  min-height: 200px;
  padding-bottom: 300px; /* 키보드 공간 확보 */
}

/* viewport meta 태그 */
/* <meta name="viewport" content="viewport-fit=cover"> */

이 시나리오의 변형

케이스 OS 브라우저 상태
ce-0285-ios-viewport-keyboard-safari-ko iOS 16+ Safari 16+ 초안
ce-0286-ios-viewport-keyboard-chrome-ios-en-ko iOS 16+ Chrome (iOS) 120+ 초안

Playground for this case

Use the reported environment as a reference and record what happens in your environment while interacting with the editable area.

Reported environment
OS: iOS 16+
Device: Mobile (iPhone/iPad) Any
Browser: Safari 16+
Keyboard: English (QWERTY) or iOS Virtual Keyboard
Your environment
Sample HTML:
Event log
Use this log together with the case description when filing or updating an issue.
0 events
Interact with the editable area to see events here.