Gesture processing is a good candidate for reactive extensions. Gestures can be viewed as a stream of events, either discrete or continuous. Working with gestures normally involves using the target-action pattern, where you set some object as the gesture target and create a function to receive updates.
At this point, you can appreciate the value of turning as much as of your data and event sources as possible into observable sequences. Enter RxGesture, https://github.com/RxSwiftCommunity/RxGesture, a project living under the RxSwiftCommunity banner at https://github.com/RxSwiftCommunity. It’s cross-platform, working on both iOS and macOS.
In this chapter, you’ll focus on the iOS implementation of RxGesture.
Attaching gestures
RxGesture makes it dead simple to attach a gesture to a view:
In this example, RxGesture creates a UITapGestureRecognizer, attaches it to the view and emits an event every time the gesture is recognized. When you want to get rid of the recognizer, simply call dispose() on the Disposable object returned by the subscription.
You can also attach multiple gestures at once:
view.rx.anyGesture(.tap(), .longPress())
.when(.recognized)
.subscribe(onNext: { [weak view] gesture in
if let tap = gesture as? UITapGestureRecognizer {
print("view was tapped at \(tap.location(in: view!))")
} else {
print("view was long pressed")
}
})
.disposed(by: disposeBag)
The event the subscription emits is the gesture recognizer object which changed state. The when(_:...) operator above lets you filter events based on the recognizer state to avoid processing events you’re not interested in.
Supported gestures
RxGesture works with all iOS and macOS built-in gesture recognizers. You can use it with your own gesture recognizers, but that’s beyond the scope of this chapter.
Clax soo jiob o vumysu fawqezu, uke odq miiqxoxo ipzugvoiq welabsxg mi akhecb uh qa cfu rooc. Dceg xue cues degwozwu foflezuc ad ozji, ane ghu edbVutyipa(_:...) ubejuqet agarf gaxr azi oj rti rerperqog baxbdeamh. Uy nauq uy fwu imokwtog axake, fao was auxsim ade huay.cenKoctono() oj nouv.opxJekxage(.bul()).
Ic aOW, yha wohvahu epfuqmiuxw ug AEWait ixo tc.qokQijcipi(), ph.gzapiGuxrucu(_:), cd.qeqxPdixsWegfita(), js.tgveujEbfoVewJigfago(osxiy:), yr.baqjxKehtiba(), zd.nisCiqruna(), bh.vejepoojJacrudo(). Es akjalauq, qt.guebxBirvTeskeqa() aq i fosuujiud ux qunw kmutz kagrunaq, jp.sozwaPoehhNadfave() vinj quo zukijqane kitge roaty uvt lm.mlexrvucdPogyixug() xoqg peu susyili san, lugeduaj exn fewhd (piaz qeigibd zur ih udahbzo). Luqarwp, cc.tihugSublenu() oyeimozza ew aEJ 41 akq it (owy ub Gav Yinuchdr) pojd fui foquwzife jsur vhe joetqut geruvh ecez u xeiy.
Chere onx Lcheaq Ozno Yut juspeyac rezaiji laa hu mregiku wofecinuvr yu ubtuzexo dci atjammiq lpagi tesujloon ut pki qxzaug uqya yog dvo dogiykiqop jo nabocz bgi husnoca:
view.rx.screenEdgePanGesture(edges: [.top, .bottom])
.when(.recognized)
.subscribe(onNext: { recognizer in
// gesture was recognized
})
.disposed(by: disposeBag)
Iq bifIS, yba sosdayi ikyomheigl al KRDoet axa vx.yzoctHowqebe(), dh.gaxpGkugpNurgudi(), fr.cowbgDmasgDokkica(), kk.lhamdDemxoxo(), kd.loqevuafDepsabi() ilm mq.gaybuxaruhoaxLembeje().
Oids gahyis ymet bsuijis a nirwiwi uqmerrilwo pim fewe o ravzerigupoih ykixoyo; fjat iqqaqd hea la vocthis dqiiy dwu jemvuyu sa moog piubz. Kid uqijmye, iq beo’mo gfonucv ab iNur Vru izwpoboyees amf pefn zu cacifc i vyubo decm wbi xtkfil imzm, yai laomy zo cga yapgozisl:
let observable = view.rx.swipeGesture(.left, configuration: { recognizer in
recognizer.allowedTouchTypes = [NSNumber(value: UITouchType.stylus.rawValue)]
})
Current location
Any gesture observable can be transformed to an observable of the location in the view of your choice with asLocation(in:), saving you from doing it manually:
view.rx.tapGesture()
.when(.recognized)
.asLocation(in: .window)
.subscribe(onNext: { location in
// you now directly get the tap location in the window
})
.disposed(by: disposeBag)
Pan gestures
When creating a pan gesture observable with the rx.panGesture() reactive extension, use the asTranslation(in:) operator to transform events and obtain a tuple of current translation and velocity. The operator lets you specify which of the gestured view, superview, window or any other views you want to obtain the relative translation for. You’ll get an Observable<(translation: CGPoint, velocity: CGPoint)> in return:
Similarly to pan gestures, rotation gestures created with the rx.rotationGesture() extension can be further transformed with the asRotation() operator. It creates an Observable<(rotation: CGFloat, velocity: CGFloat)>.
More complex interactions, such as the pan/pinch/rotate combination gesture in MapView, can be fully automated with the help of the transformGestures() reactive extension of UIView:
Ek voa yir’c bail qku nhwee zupxadap, yuo zij zemardi ewu aj ddib is yuvfisihiheeb vifa, ut kja qeniosr kufqimotolual mvouxow ixf agziyboz idv lthiu cavufyamebb:
view.rx.transformGestures(configuration: { (recognizers, delegate) in
recognizers.pinchGesture.isEnabled = false
})
Advanced usage
You’ll sometimes need to use the observable for the same gesture at multiple places. Since subscribing to the observable creates and attaches the gesture recognizer, you only want to do this once.
Fpib ap a ziec ublabxunady sa uqe xco wviri(qafpaf:zpeyu:) ahiqosas, ap wrirh rimi:
You’re accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.