From 5916e17b258325633ce7502d695175e97bae5eba Mon Sep 17 00:00:00 2001 From: songyc macbook Date: Mon, 13 Jul 2026 15:08:08 +0900 Subject: [PATCH] fix(diary): keep pencil strokes sharp at any zoom level MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 페이지가 논리 768×1024pt 고정 + 스케일 변환이라 PKCanvasView 래스터가 비정수 축소에선 뭉개지고 확대에선 픽셀이 보였음 → 캔버스(내부 렌더 뷰 포함)의 contentScaleFactor를 기본 1.5배 슈퍼샘플링 + 줌 배율 추종(기기 스케일 ×2.5 상한)으로 조정. 레이아웃 변경과 핀치 종료 시점에 갱신. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7 --- myApp/HaruDanim/IOS/Views/DiaryNotePage.swift | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/myApp/HaruDanim/IOS/Views/DiaryNotePage.swift b/myApp/HaruDanim/IOS/Views/DiaryNotePage.swift index 9f815b4..eb2bbb7 100644 --- a/myApp/HaruDanim/IOS/Views/DiaryNotePage.swift +++ b/myApp/HaruDanim/IOS/Views/DiaryNotePage.swift @@ -288,6 +288,11 @@ private struct DiaryZoomContainer: UIViewRepresentable { func scrollViewDidZoom(_ scrollView: UIScrollView) { (scrollView as? DiaryZoomScrollView)?.centerZoomedView() } + + func scrollViewDidEndZooming(_ scrollView: UIScrollView, with view: UIView?, atScale scale: CGFloat) { + // 핀치가 끝난 배율에 맞춰 캔버스 래스터 해상도를 다시 맞춘다 (확대 시 픽셀 번짐 방지) + (scrollView as? DiaryZoomScrollView)?.updateCanvasSharpness() + } } } @@ -296,6 +301,7 @@ final class DiaryZoomScrollView: UIScrollView { var pageSize: CGSize = DiaryPageMetrics.size weak var zoomedView: UIView? private var didSetInitialZoom = false + private var lastCanvasScale: CGFloat = 0 override func layoutSubviews() { super.layoutSubviews() @@ -315,6 +321,7 @@ final class DiaryZoomScrollView: UIScrollView { didSetInitialZoom = true } centerZoomedView() + updateCanvasSharpness() } /// 콘텐츠가 화면보다 작을 때 가운데 정렬 @@ -325,6 +332,40 @@ final class DiaryZoomScrollView: UIScrollView { zoomedView.center = CGPoint(x: contentSize.width / 2 + dx, y: contentSize.height / 2 + dy) } + + /// 펜슬 캔버스의 래스터 해상도를 현재 줌 배율에 맞춘다. + /// 페이지는 논리 768×1024pt 고정이라 화면 fit/확대가 전부 스케일 변환인데, + /// PKCanvasView는 자기 contentScaleFactor로만 래스터하므로 그대로 두면 + /// 축소 표시(비정수 배율 미니피케이션)에선 획이 뭉개지고 확대에선 픽셀이 보인다. + /// → 기본 1.5배 슈퍼샘플링 + 확대 배율 추종(기기 스케일 × 최대 2.5)으로 항상 선명하게. + func updateCanvasSharpness() { + guard let zoomedView else { return } + let display = max(traitCollection.displayScale, 1) + let target = display * min(max(zoomScale, 1) * 1.5, 2.5) + guard abs(target - lastCanvasScale) > 0.01 else { return } + lastCanvasScale = target + Self.applyCanvasScale(target, in: zoomedView) + } + + /// 서브트리에서 PKCanvasView를 찾아 자신+내부 렌더 뷰들의 contentsScale을 올린다 + private static func applyCanvasScale(_ scale: CGFloat, in view: UIView) { + if view is PKCanvasView { + setScaleRecursively(scale, view: view) + return + } + for subview in view.subviews { + applyCanvasScale(scale, in: subview) + } + } + + private static func setScaleRecursively(_ scale: CGFloat, view: UIView) { + view.contentScaleFactor = scale + view.layer.contentsScale = scale + view.setNeedsDisplay() + for subview in view.subviews { + setScaleRecursively(scale, view: subview) + } + } } // MARK: - 배치 요소 (사진/도형)