cr8rcho
개발

PKToolPicker 축소 상태 아이콘 미표시 버그 수정

25년 2월 10일
7 분

문제

iPad에서 캔버스 화면에 진입할 때 PKToolPicker가 처음 표시되면, 축소(collapsed/minimized) 상태의 원형 버튼에 선택된 도구 아이콘이 표시되지 않았습니다. 빈 검은 원으로만 보였습니다.

사용자가 직접 picker를 확장하여 도구를 선택하거나 변경한 후 다시 축소하면 그때서야 아이콘이 정상 표시되었습니다.

영향 범위

  • 앱 내 여러 화면에서 다수의 PKCanvasView를 사용
  • 모든 캔버스가 하나의 PKToolPicker 인스턴스를 공유하는 구조

원인 분석

PKToolPicker의 내부 축소 뷰(collapsed view)가 프로그래밍 방식으로 표시될 때 초기 레이아웃 패스를 수행하지 않았습니다. Picker가 setVisible(_:forFirstResponder:)로 표시되더라도 축소 상태의 도구 아이콘 렌더링을 위한 레이아웃이 자동으로 트리거되지 않는 Apple 프레임워크 내부 동작 문제였습니다.

해결 방법

두 가지 변경을 조합하여 해결했습니다.

1. UIViewRepresentable → UIViewControllerRepresentable 전환

캔버스 래퍼 뷰를 UIViewRepresentable에서 UIViewControllerRepresentable로 변경하여 viewDidAppear 라이프사이클을 활용했습니다. Apple의 WWDC19 권장 패턴에 따라 viewDidAppear에서 tool picker를 설정했습니다.

이유: UIViewRepresentablemakeUIView/updateUIView는 뷰가 윈도우에 완전히 추가되기 전에 호출될 수 있어 picker 초기화 타이밍이 불안정했습니다.

2. Window 계층 순회를 통한 강제 재렌더링

Picker가 표시된 후 다음 런 루프 사이클에서 윈도우 계층을 순회하여 Picker 관련 내부 뷰를 찾아 강제로 레이아웃/디스플레이를 트리거했습니다.

private static func forcePickerRedraw(in view: UIView) {
    let className = String(describing: type(of: view))
    if className.contains("Picker") || className.contains("Floating") || className.contains("Palette") {
        view.setNeedsLayout()
        view.layoutIfNeeded()
        view.setNeedsDisplay()
        for subview in view.subviews {
            subview.setNeedsLayout()
            subview.layoutIfNeeded()
            subview.setNeedsDisplay()
        }
    }
    for subview in view.subviews {
        forcePickerRedraw(in: subview)
    }
}

호출 시점: becomeFirstResponder() 이후 DispatchQueue.main.async로 한 런 루프 사이클 지연시켜 호출했습니다. Picker가 윈도우에 추가된 후 레이아웃을 강제하기 위함입니다.

picker.setVisible(isToolPickerVisible, forFirstResponder: canvasView)
if isToolPickerVisible {
    canvasView.becomeFirstResponder()
    DispatchQueue.main.async { [weak self] in
        guard let window = self?.canvasView.window else { return }
        Self.forcePickerRedraw(in: window)
    }
}

시도했으나 실패한 접근법

접근법결과
selectedToolItemIdentifier 설정축소 아이콘에 반영되지 않음
selectedToolItemPKToolPickerInkingItem 할당내부 도구는 선택되지만 축소 아이콘 렌더링 안 됨
stateAutosaveName = nil축소 상태 복원 방지만 되고 아이콘 문제 미해결
PKCanvasView 서브클래스 + didMoveToWindow()동작 불안정
토글 버튼으로 Picker on/off근본 해결 아님, 토글 시에도 빈 원 표시
becomeFirstResponder()setVisible() 순서Apple 권장은 반대 순서. 순서 수정만으로는 해결 안 됨
UIViewControllerRepresentable + viewDidAppear만 적용타이밍은 개선되었으나 축소 아이콘 여전히 미표시

수정 내용 요약

  • 캔버스 래퍼를 UIViewControllerRepresentable로 전환하면서 전용 UIViewController 서브클래스 생성
  • forcePickerRedraw(in:) 메서드 추가

핵심 정리

  • PKToolPicker의 축소 뷰는 프로그래밍 방식 표시 시 자체적으로 초기 레이아웃을 수행하지 않습니다
  • viewDidAppear에서 picker를 설정하는 것이 올바른 타이밍이지만 그것만으로는 부족합니다
  • 윈도우 계층에서 Picker 관련 뷰를 찾아 setNeedsLayout/layoutIfNeeded/setNeedsDisplay를 강제 호출해야 축소 아이콘이 렌더링됩니다
  • 이 강제 레이아웃은 becomeFirstResponder() 이후 다음 런 루프에서 실행되어야 효과적입니다