본문 바로가기
Swift

[Swift] 뷰 재사용하기 - Delegate 패턴 활용

by Jimmy_iOS 2023. 11. 12.

얼마전 개발한 운동 기록앱인 JimFit에서 손잡이를 통해 높이 조절이 가능한 바텀뷰를 구현했습니다.

바텀뷰에는 UISwipeGestureRecognizer, TitleLable, RighBarButton이 있습니다.

여러 뷰컨트롤러에서 이를 재사용하고 있기 때문에 코드를 수정할 때 뷰컨트롤러과 바텀뷰 간에 의존성이 발생해 코드 수정에 어려움을 겪었습니다.

이 문제는 Protocol과 Delegate 패턴을 활용하여 의존성을 분리하고, 직관적인 코드를 구현할 수 있었습니다.

1. GrabberViewDelegate Protocol 정의하기

먼저, GrabberViewDelegate Protocol을 정의합니다.

이 Protocol은 GrabberView에서 발생하는 이벤트를 처리하기 위해 구현해야 할 메서드를 선언합니다.

protocol GrabberViewDelegate: AnyObject {
    func grabber(configureTitle label: UILabel)
    func grabber(swipeGestureFor direction: UISwipeGestureRecognizer.Direction)
    func grabberButtonTapped()
}

extension GrabberViewDelegate {
    func grabber(configureTitle label: UILabel) { }
}

GrabberViewDelegate는 grabber(configureTitle:), grabber(swipeGestureFor:), grabberButtonTapped() 메서드를 가지고 있습니다.

이 메서드들을 구현하여 GrabberView의 동작을 외부에서 제어할 수 있습니다.

또한, GrabberViewDelegate의 extension을 통해 구현을 선택적으로 제공할 수도 있습니다.

2. GrabberView에 Delegate 추가하기

GrabberView 클래스에 Delegate 속성을 추가하고, 필요한 곳에서 Delegate 메서드를 호출하도록 구현합니다.

또한 weak 키워드를 사용하여 뷰와 뷰컨트롤러 간의 강한 참조 사이클을 방지할 수 있습니다.

final class GrabberView: UIView {
    //...
    weak var delegate: GrabberViewDelegate?
    //...
    private func performDelegate() {
        delegate?.grabber(configureTitle: titleLabel)
        rightBarButton.addAction { [unowned self] in
            self.delegate?.grabberButtonTapped()
        }
        let swipeUpForGrabber = UISwipeGestureRecognizer(target: self, action: #selector(swipe(_:)))
        swipeUpForGrabber.direction = UISwipeGestureRecognizer.Direction.up
        let swipeDownForGrabber = UISwipeGestureRecognizer(target: self, action: #selector(swipe(_:)))
        swipeDownForGrabber.direction = UISwipeGestureRecognizer.Direction.down
        backView.addGestureRecognizer(swipeUpForGrabber)
        backView.addGestureRecognizer(swipeDownForGrabber)
    }
    //...
}

GrabberView의 performDelegate() 메서드에서는 Delegate 메서드를 호출하는 부분을 구현합니다.

grabber(configureTitle:) 메서드를 호출하여 titleLabel을 설정하고,

grabberButtonTapped() 메서드를 호출하여 버튼 탭 이벤트를 처리합니다.

또한, 스와이프 동작을 처리하기 위해 해당 동작을 감지하는 Gesture Recognizer를 추가하고,

grabber(swipeGestureFor:) 메서드를 호출합니다.

3. GrabberViewDelegate 구현하기

GrabberViewDelegate를 구현하여 GrabberView의 동작을 외부에서 제어할 수 있습니다. 필요한 클래스에서 GrabberViewDelegate를 채택하고, 메서드를 구현합니다.

class ViewController: UIViewController, GrabberViewDelegate {
    //...
    func grabber(configureTitle label: UILabel) {
        // titleLabel 설정
    }
    
    func grabber(swipeGestureFor direction: UISwipeGestureRecognizer.Direction) {
        // 스와이프 동작 처리
        switch direction {
        case .up :
        case .down:
        default: break
        }
    }
    
    func grabberButtonTapped() {
        // 버튼 탭 이벤트 처리
    }
    //...
}

ViewController 클래스에서는 GrabberViewDelegate를 채택하여 GrabberView의 동작을 구현합니다. grabber(configureTitle:) 메서드를 구현하여 titleLabel을 설정하고,

grabberButtonTapped() 메서드를 구현하여 버튼 탭 이벤트를 처리합니다.

또한, grabber(swipeGestureFor:) 메서드를 구현하여 스와이프 동작을 처리할 수 있습니다.

4. GrabberView 재사용하기

이제 GrabberView를 재사용할 때는 Delegate를 활용하여 필요한 동작을 외부에서 구현할 수 있습니다.

GrabberView를 생성하고, Delegate를 설정한 뒤에 사용하면 됩니다.

let grabberView = GrabberView()
grabberView.delegate = self

GrabberView를 생성한 후에 delegate 속성에 Delegate 객체를 설정해주면 됩니다. 이를 통해 GrabberView의 동작을 외부에서 구현할 수 있습니다.

마무리

Delegate 패턴을 활용하여 GrabberView를 재사용할 수 있도록 설계했습니다.

Delegate를 통해 GrabberView의 동작을 외부에서 제어할 수 있으며,

이를 통해 코드의 재사용성을 높일 수 있습니다.

GrabberView를 사용하는 다양한 클래스에서 Delegate를 구현하여 필요한 동작을 처리할 수 있습니다.

이번 포스팅에서는 GrabberView를 재사용하기 위한 Delegate 패턴의 활용 방법에 대해 알아보았습니다.

Delegate 패턴은 iOS 개발에서 유용하게 활용될 수 있는 디자인 패턴 중 하나이며,

다른 UI 요소의 재사용에도 적용할 수 있습니다.


Uploaded by N2T