본문 바로가기
iOS

[iOS/Swift] CollectionView, TableView 내의 UIView에 TapGesture 추가하기

by 코코종 2022. 6. 22.

안녕하세요 코코종입니다!
블로그 글을 써야지 써야지.. 하면서 공부할 것도 많고 한데 밍기적 밍기적😇😇 대다가 이제야 글을 쓰네요.(사실 이전글 보다 먼저 쓰기 시작했는데 이것마저 미뤄짐)

최근에는 제가 이전에 썼던 SeSAC iOS개발자 데뷔과정 후기로 많은 분들이 방문을 해주셨네요 ㅎㅎ (고마워요 새싹~~)

오늘은 제가 일하면서 겪었던 트러블 중 하나인 CollectionView, TableView 내의 TapGesture 추가하는 법을 회고 해보려고 합니다. 가보시죠!


먼저 겪었던 문제는 다음과 같습니다. table/collectionView의 cell 안에 Button이 아닌 View로 그린 버튼아닌 버튼(fakeButton)이 있습니다. 이때 컬렉션뷰나 테이블 뷰의 다음 요소를 보려고 스크롤을 하잖아요? 그때 Button은 그 버튼을 클릭하면 이벤트가 발생하고(이건 당연) 스크롤을 할때는 이벤트를 발생시키기 않습니다.(그래서 스크롤이 되지 않고) 그런데 View는 스크롤을 하려고 누르자마자 그 버튼아닌 버튼(fakeButton)이 클릭되는 현상이 있었습니다. 이번의 경우에는 저 fakeButton을 클릭하면 WebView를 Push해줘야해서 스크롤을 하려다가 웹뷰가 떠버리는 웃지못할 사태가 벌어졌습니다. 이를 해결한 방법을 보여드리도록 하겠습니다. 

 

색 선택 미쳤다... 이게 개..발자?

 override func awakeFromNib() {
        super.awakeFromNib()
        
        fakeButton.addGestureRecognizer(tapGesture)
        fakeButton.isUserInteractionEnabled = true
        tapGesture.delegate = self
}
    
extension MainCollectionViewCell: UIGestureRecognizerDelegate {
    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
        print("touched")
        return true
    }
}

위와 같은 구성으로 되어 있었습니다. 다시 설명을 하자면 버튼 부분을 swipe하게 되면  button의 tap이벤트가 스크롤보다 우선순위를 가져서 스크롤이 되지 않고, view는 gestureRecognizer가 우선순위를 가져 바로 "touched"가 출력되었습니다. 여담으로 공식문서를 찾아보니 touch 이외에도 press, event(touch+press)가 있는데 UIView이기 때문에 touch만 동작했습니다. (버튼에는 press도 있겠쬬?)


import UIKit
import RxSwift
import RxCocoa
import RxGesture

class MainCollectionViewCell: UICollectionViewCell {
    
    @IBOutlet weak var button: UIButton!
    @IBOutlet weak var fakeButton: UIView!
    
    static let identifier = "MainCollectionViewCell"
    
    let tapGesture = UITapGestureRecognizer()
    
    var bag = DisposeBag()

    override func awakeFromNib() {
        super.awakeFromNib()
        
        fakeButton.addGestureRecognizer(tapGesture)
        fakeButton.isUserInteractionEnabled = true
        tapGesture.delegate = self // 1. gestureRecognizer
        tapGesture.addTarget(self, action: #selector(targetEvent)) // 2. addTarget
      
        fakeButton.rx.tapGesture() // 3. RxGesture
            .subscribe(onNext: { _ in
                print("RxGesture")
            }).disposed(by: bag)
        
        button.rx.tap.subscribe(onNext: { _ in
            print("button tap")
        }).disposed(by: bag)
        
    }
    
    @objc func targetEvent() {
        print("targetEvent")
    }
    
    override func prepareForReuse() {
        super.prepareForReuse()
    }
    
}

extension MainCollectionViewCell: UIGestureRecognizerDelegate {
    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
        print("touched")
        return true
    }
}

해결방법에는 여러가지가 있었는데요 그중에서 실제로는 RxGesture를 이용해서 작업했습니다. 최대한 RxSwift를 사용하려고 해서 이렇게 해결했습니다. 이외에도 고전적인 방식인 addTarget으로도 잘 동작하는것을 확인해 봤습니다. 이 3가지 방식을 모두 기입해봤는데요 궁금한점이 '이 세가지 방식에도 우선순위?가 있을까 궁금해서 체크를 해봤습니다.

 

먼저, fakeButton으로 스크롤을 하게 되면 touched만 출력이 되었습니다.(이걸 막으려고 노력한거니까..?)

그럼 그냥 클릭은 어떨까요? 위 코드에 적어둔 번호대로 호출이 되는 것도 확인을 했습니다.

 

제 생각에는 gestureRecognizer는 touch를 받기 때문에 press가 되기 전에 호출이 되고, 직접적으로 addTarget을 해준 @objc가 호출되고, 마지막으로 subscribe를 하기 때문에 tapGesture가 마지막으로 호출된다고 이해했습니다.(제.. 생각이지만요...)


뭔가 결론이 쬐금 이상하게 빠진것 같지만! 저처럼UITapGestureRecognizer에 delegate를 무지성으로 해버리지 마시길 바랍니다 하하... 'delegate = self를 넣었더니 이게 뜨네? 개이득? 바로 써야지' 하다가는.. 큰코 다칩니다 ㅜㅜ addTarget이나 RxGesture를 한 번 써보시지요!