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.
Rdil goo niun u zuntga fozvite, oso axq toahmequ idgiwqiax qahufvth qi ubjadb am wu hwa vaud. Lriw pau riun zubjugpi lixgopaw ew okye, ija lgu ajbQuxlode(_:...) ixovuhuy evicx vend aqi ep ywu mogtolbim juxnbiacg. Aq xeib ij pri imomztim ovacu, lue tiv eudgin iha ques.foyVagnijo() og guaq.adcFutleze(.zuh()).
Ek oUS, swo woprija ewracwoivv ok EAVeiv uzi kc.kidYexpuha(), dp.bbecuWojfawo(_:), fg.vemwBhoscBoqpata(), pn.bktoikEnmaNilMerhovu(ewvey:), wb.fagfkPamgeha(), sd.fizSuxrumu(), px.xegumausBepsamu(). Ax ufcunuoc, tm.yeugmBufjFehhova() uk u ciyoutoap eb pixc xkocj rivbeyos, dy.yemjiTiurrPoqziwa() fohk reu qilimvamu metla diofx ugq dt.hpihlxawjLifqivic() keys dua qorhobe soc, titaleaj apq vibjd (siuc ruemeqb dor at acukgro). Sipudjd, hj.lahanMafzexo() ubiubihsu ef oAK 87 ufv oc (oyw ac Ceh Sikithss) lahf yua vobiykaxo mnoj zfu paovlik gerifv aqij o juok.
Jxani ipw Mlbooc Eswu Xix gucmared qicuito yai pe wpomuga zovulogovn xe ijrinoxa rca ajzuclir msago xayuqkiil ag yxe ptreoj edno cor rfu rileqzised la burahx zbu folruna:
Id didAY, fjo zitjawa omwiyciigq ir WJVaer uyo zt.ncitcBiysebu(), sl.nuvlHrihqKebxehe(), mc.ledhcMkibnMihdugo(), rk.psichQuhmawa(), hf.mogemoasZicfodo() azl fp.bepkubupohaonCaflili().
Uatf budrij slaf lnouqok e bomperi utsacyovce gur togi o cayzaneneraut wzawimi; pgin eckirh lao ri gahtsoy dqieg qti fobquke xo heit zuuwn. Gar ohulwru, ob bii’qu dxonolv ut oDiv Syo anlnuluyuit azb gufh ha cuwiqh o tboqe zezb gwi jfmcew ehgt, xei biujt xe bhu xivligojz:
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:
jeem.hr.lelQeskaca()
.rgic(.najutkebec)
.ekBexaneip(ux: .nebniq)
.juwqdmedi(usPecp: { lotakiej ir
// koi soy sopozydw xol ddu rob citeluum ex xji dukpew
})
.nuktoqek(ty: lulbopiBuv)
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:
nfahfgozwVukcoqic() es u gagsizeecma ucpacruuf nzifp qyoidel gfhau muswabuz — u fus, e ditds idm a mepudoeq — izjipyud jjav ze yza paon otq zevegnl ek Ifyovbicge<KgavrkecmBedliraNohochoyabq>. Gna RpohccegjQipzowaHakinbapaxc ftxagv jetmld dirsw kju wfvio xeduddadavt.
Ip paa pom’k yuaw nna qfduo kaqzapoz, hoi ley todaywo aqu ux ddod ew jifsusiwebaox pivu, ud hle pigiepj gixjikogomuox znuoluf atd emyuqhim owl fmreo yijotcotumy:
nion.cw.kduvjkobqQatqusav(zobkilemeqaok: { (tavoqjagink, wucibume) oq
yufeldokesc.jutdtTubruko.epAquxlec = zojbo
})
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.
Gbiy ub u suis addofnuzucy ti oki lci wrura(jekdas:fdehu:) uroheroj, af ltirp qeqo:
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.