관리 메뉴

김종권의 iOS 앱 개발 알아가기

[iOS - swift] UIControl의 UIButton 액션 클로저 만드는 방법 (touchUpInside, UIControl.Event, objc_setAssociatedObject) 본문

iOS 응용 (swift)

[iOS - swift] UIControl의 UIButton 액션 클로저 만드는 방법 (touchUpInside, UIControl.Event, objc_setAssociatedObject)

jake-kim 2023. 1. 6. 23:36

일반적인 UIButton의 action 처리 방법

  • UIButton의 인스턴스 메소드인 addTarge(_:action:for:)를 사용하여 처리
    • 단점은 button의 addTarget하는 부분에서 특정 지역변수에 대한 기능을 touchUpInside 액션 시 동작하게 하기 어려운 점이 존재
    • 아래 abc 지역 프로퍼티를 button의 touchUpInside될 때 출력하고 싶어도 어려운 상태
    • button의 클로저로 만들면? (아래에서 계속)
class ViewController: UIViewController {
    private let button = UIButton()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let abc = 123
        
        button.translatesAutoresizingMaskIntoConstraints = false
        
        button.setTitle("타이틀", for: .normal)
        button.setTitleColor(.systemBlue, for: .normal)
        button.setTitleColor(.blue, for: .highlighted)
        
        view.addSubview(button)
        NSLayoutConstraint.activate([
            button.centerYAnchor.constraint(equalTo: view.centerYAnchor),
            button.centerXAnchor.constraint(equalTo: view.centerXAnchor),
        ])
        
        button.addTarget(self, action: #selector(tab), for: .touchUpInside)
    }
    
    @objc private func tab() {
        print("Tab Button!")
    }
}

UIControl.Event에 대한 클로저 만드는 방법

* (방법은 해당 글 참고하여 작성)

  • 사용하는 쪽 - 클로저로 접근 가능
        // 적용 전
        button.addTarget(self, action: #selector(tab), for: .touchUpInside)
        
        // 적용 후
        button.addAction {
            print("Tab Button!", abc)
        }
  • 구현 방법
    • UIControl에 extension으로 정의
    • 파리미터로 controlEvents와 closure를 받는 메소드
public extension UIControl {
    func addAction(for controlEvents: UIControl.Event = .touchUpInside, _ closure: @escaping () -> ()) {
    }
}
  • 클로저를 처리하는 @objc class를 따로 정의하고, 이 클로저를 넘겨주도록 구현
    • 끝에 objc_setAssociatedObject를 사용하여 해당 액션 클로저가 해제되지 않도록 retain 걸어놓기
    • objc_setAssociatedObject 관련 개념은 이전 포스팅 글 참고
    func addAction(for controlEvents: UIControl.Event = .touchUpInside, _ closure: @escaping () -> ()) {
        @objc class ClosureSleeve: NSObject {
            let closure: () -> ()
            
            init(_ closure: @escaping () -> ()) {
                self.closure = closure
            }
            
            @objc func invoke() {
                closure()
            }
        }
        
        let sleeve = ClosureSleeve(closure)
        addTarget(sleeve, action: #selector(ClosureSleeve.invoke), for: controlEvents)
        objc_setAssociatedObject(self, "\(UUID())", sleeve, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN)
    }

* 전체 코드: https://github.com/JK0369/ExAction

* 참고

https://stackoverflow.com/questions/25919472/adding-a-closure-as-target-to-a-uibutton

Comments