-
[iOS] Pinch Gesture와 Double Tap으로 구현하는 이미지 확대/축소 기능/iOS 📱 2025. 4. 24. 00:05반응형
안녕하세요 아렉스입니다 :> 관성적으로 사용하던 부분을 다르게 설계해본 것을 공유드려볼려고해요.
기존 target-action 패턴에 대해서는 설명하지않으니, 패턴에서 이해가 안되는 부분은 따로 찾아보시면 좋을거같아요
1. 기존 target-action 패턴과 관성적인 문제점
iOS에서 제스처를 처리할 때, target-action 패턴을 사용합니다.
즉, UITapGestureRecognizer나 UIPinchGestureRecognizer 같은 제스처를 설정할 때, 제스처 인식기의 target을 self로 설정하여, self 클래스 내에 구현된 메서드를 액션으로 호출하게 됩니다.
예를 들어, 이미지 뷰에서 두 손가락으로 확대/축소 제스처를 처리할 때 아래와 같은 코드가 흔히 사용됩니다.
let pinchGesture = UIPinchGestureRecognizer(target: self, action: #selector(handlePinch(_:))) imageView.addGestureRecognizer(pinchGesture)이 방식은 직관적이고 간단한 구현이 가능하지만, 여러 가지 문제점도 동반합니다.
가장 큰 문제는 target: self 로 인해 가 view나 view controller의 메서드에 종속된다는 점입니다.
2. target: self 로 인한 의존성 문제
target: self를 사용하면 제스처가 발생했을 때 self(보통은 UIViewController나 UIView) 의 메서드가 호출됩니다.
이 방식은 단기적으로 보면 구현이 간단하고 직관적이지만, 장기적으로는 유지보수성과 재사용성에 큰 단점이 있습니다.
종속 되어있기 때문에, 인터렉션을 처리할 때 보일러 플레이트 코드가 발견되기도 합니다.
문제 1: 로직의 분산과 의존성 증가
self에 의존하는 방식은 뷰나 뷰컨트롤러에 제스처 관련 로직을 직접 구현하게 만듭니다.
이로 인해 뷰(View)나 뷰컨트롤러(ViewController)의 책임이 커지고, 다른 곳에서 재사용하기 어려운 구조가 됩니다.
예를 들어, 다른 뷰에서 같은 제스처를 사용하고 싶다면 기존의 view나 view controller에 있는 로직을 복사하거나, 새로운 메서드를 다시 작성해야 하는 번거로움이 있습니다.
문제 2: 테스트와 확장에 어려움
제스처 로직이 UIViewController나 UIView에 직접적으로 결합되면, 이를 독립적으로 테스트하거나 확장하기 어려워집니다.
예를 들어, ZoomableImageView를 여러 곳에서 재사용하려면, 해당 뷰와 뷰컨트롤러의 종속성을 끊는 구조로 바꿀 필요가 있습니다.
3. 개선된 구조: target: self 대신 ImageZoomHandler 사용
target: self 대신, 제스처 관련 로직을 ImageZoomHandler 클래스에 분리하는 접근을 했습니다.
@MainActor class ImageZoomHandler { nonisolated init() {} func regist(with view: UIView) { ... } @objc func pinched(_ recognizer: UIPinchGestureRecognizer) { ... } @objc func doubleTapped(_ recognizer: UITapGestureRecognizer) { ... } }UIImageView를 상속한 ZoomableImageView에 적용하는 예제입니다.
target: self 대신, 제스처 관련 로직을 ImageZoomHandler 클래스에 분리하여, ZoomableImageView와 같은 뷰는 로직을 독립적으로 관리할 수 있게 되었습니다.
복잡한 인터렉션이 발생시에는 ImageZoomHandler 클래스를 상속하여 확장성을 가질 수 있습니다.
@MainActor class ZoomableImageView: UIImageView { private let zoomHandler: ImageZoomHandler init(zoomHandler: ImageZoomHandler = .init(), frame: CGRect) { self.zoomHandler = zoomHandler super.init(frame: frame) registToZoomHandler() } ... 생략 // ✅ target에 self 대신 zoomHanlder를 지정 private func registToZoomHandler() { let pinchGesture = UIPinchGestureRecognizer(target: zoomHandler, action: #selector(zoomHandler.pinched(_:))) let tapGesture = UITapGestureRecognizer(target: zoomHandler, action: #selector(zoomHandler.doubleTapped(_:))) tapGesture.numberOfTapsRequired = 2 addGestureRecognizer(tapGesture) addGestureRecognizer(pinchGesture) isUserInteractionEnabled = true // ✅ 기본 구현으로 충분하다면 sugar API regist(with:) 를 사용해도 충분 // zoomHandler.regist(with: self) } }이제 ZoomableImageView와 ImageZoomHandler를 사용하여 target: self를 관성적으로 사용하던 문제를 해결했습니다.
target: self 대신, 제스처 관련 로직을 ImageZoomHandler 클래스에 분리하여, ZoomableImageView와 같은 뷰는 로직을 독립적으로 관리할 수 있게 되었습니다.
요약 및 정리
- 재사용성: ImageZoomHandler를 다른 뷰에서 재사용할 수 있게 되어, 중복 코드가 줄어듭니다. 예를 들어, 이미지 뷰뿐만 아니라 다른 인터랙티브한 뷰에서 동일한 확대/축소 기능을 손쉽게 사용할 수 있습니다.
- 확장성: 제스처 로직을 ImageZoomHandler로 분리했기 때문에, 나중에 다른 제스처나 기능을 추가할 때도, 뷰나 뷰컨트롤러에 영향을 주지 않고 독립적으로 확장할 수 있습니다.
- 유지보수성: 제스처 처리 로직을 한 곳에 집중시키면, 나중에 코드 수정이나 버그 수정을 쉽게 할 수 있습니다. 특정 뷰나 뷰컨트롤러가 아닌, 독립된 ImageZoomHandler에서만 수정하면 되기 때문입니다.
잘못된 내용 혹은 이해가 어려운 내용들은 언제든 댓글에서 의견 교환하고 싶습니다!
공감과 구독은 더 나은 글을 작성되는데 큰 도움이 됩니다.읽어주셔서 감사합니다.
https://github.com/Swift-Education/ExImage/tree/main
GitHub - Swift-Education/ExImage
Contribute to Swift-Education/ExImage development by creating an account on GitHub.
github.com
반응형' > iOS 📱' 카테고리의 다른 글
[iOS] 커스텀 폰트 (Custom Font) 추가하기 (0) 2026.01.28 [iOS] MultipeerConnectivity(P2P 프로토콜) 소개 및 예제 프로젝트 (1) 2025.05.12 [UIKit] iOS 앱 화면 밝기 조정하기 (0) 2025.02.13 [UIKit] UIKit의 스토리보드 Preview 적용하기(Storyboard, Preview, 미리보기) (0) 2024.12.08 [iOS] Meet AccessorySetupKit 정리 (0) 2024.06.14