RxSwift: How to add gesture to UILabel?

29,662

Solution 1

A UILabel is not configured with a tap gesture recognizer out of the box, that's why RxCocoa does not provide the means to listen to a gesture directly on the label. You will have to add the gesture recognizer yourself. Then you can use Rx to observe events from the recognizer, like so:

let disposeBag = DisposeBag()
let label = UILabel()
label.text = "Hello World!"

let tapGesture = UITapGestureRecognizer()
label.addGestureRecognizer(tapGesture)

tapGesture.rx.event.bind(onNext: { recognizer in
    print("touches: \(recognizer.numberOfTouches)") //or whatever you like
}).disposed(by: disposeBag)

Solution 2

Swift 5 (using RxGesture library).

Best and simplest option imho.

     label
        .rx
        .tapGesture()
        .when(.recognized) // This is important!
        .subscribe(onNext: { [weak self] _ in
            guard let self = self else { return }
            self.doWhatYouNeedToDo()
        })
        .disposed(by: disposeBag)

Take care! If you don't use .when(.recognized) the tap gesture will fire as soon as your label is initialised!

Solution 3

Swift 4 with RxCocoa + RxSwift + RxGesture

let disposeBag = DisposeBag()
let myView = UIView()

myView.rx
  .longPressGesture(numberOfTouchesRequired: 1,
                    numberOfTapsRequired: 0,
                    minimumPressDuration: 0.01,
                    allowableMovement: 1.0)
            .when(.began, .changed, .ended)
            .subscribe(onNext: { pan in
                let view = pan.view
                let location = pan.location(in: view)
                switch pan.state {
                case .began:
                    print("began")
                case .changed:
                    print("changed \(location)")
                case .ended:
                    print("ended")
                default:
                    break
                }
            }).disposed(by bag)

or

myView.rx
.gesture(.tap(), .pan(), .swipe([.up, .down]))
.subscribe({ onNext: gesture in
    switch gesture {
    case .tap: // Do something
    case .pan: // Do something
    case .swipeUp: // Do something 
    default: break       
    }        
}).disposed(by: bag)

or event clever, to return an event. i.e string

var buttons: Observable<[UIButton]>!

let stringEvents = buttons
        .flatMapLatest({ Observable.merge($0.map({ button in
            return button.rx.tapGesture().when(.recognized)
                .map({ _ in return "tap" })
            }) )
        })

Solution 4

You can subscribe label to the tap gesture

    label
        .rx
        .tapGesture()
        .subscribe(onNext: { _ in
            print("tap")
        }).disposed(by: disposeBag)

Solution 5

As Write George Quentin. All work.

    view.rx
        .longPressGesture(configuration: { gestureRecognizer, delegate in
            gestureRecognizer.numberOfTouchesRequired = 1
            gestureRecognizer.numberOfTapsRequired = 0
            gestureRecognizer.minimumPressDuration = 0.01
            gestureRecognizer.allowableMovement = 1.0
        })
        .when(.began, .changed, .ended)
        .subscribe(onNext: { pan in
            let view = pan.view
            let location = pan.location(in: view)
            switch pan.state {
            case .began:
                print(":DEBUG:began")
            case .changed:
                print(":DEBUG:changed \(location)")
            case .ended:
                print(":DEBUG:end \(location)")
                nextStep()
            default:
                break
            }
        })
        .disposed(by: stepBag)
Share:
29,662
Rugmangathan
Author by

Rugmangathan

Updated on April 29, 2021

Comments

  • Rugmangathan
    Rugmangathan about 3 years

    I have a label with isUserInteractionEnabled set to true. Now, I need to add UITapGestureRecognizer for the label. Is there a way to add in Rx way.

    I have looked at the RxSwift library here. Which they didn't provide any extension for adding gesture. The UILabel+Rx file has only text and attributedText.

    Is there any workaround to add gesture to label?

  • Rugmangathan
    Rugmangathan almost 7 years
    For me, it throws 'UILabel' is not a subtype of 'UIGestureRecognizer' error. I'm using IBOutlet
  • RvdB
    RvdB almost 7 years
    I made a mistake in the answer. You need to use rx.event on the recognizer instead of the label. I just updated the answer.
  • FredFlinstone
    FredFlinstone about 4 years
    There's no need to create a seperate gesture recogniser. I you use RX in your file you can just do: label.rx.tapGesture()
  • Marcus
    Marcus over 3 years
    It took me some time to find that this functionality is provided by the RxGesture library.