@@ -13,170 +13,155 @@ import RxCocoa
1313import RxSwift
1414
1515public protocol RxKeyboardType {
16- var frame : Driver < CGRect > { get }
17- var visibleHeight : Driver < CGFloat > { get }
18- var willShowVisibleHeight : Driver < CGFloat > { get }
19- var isHidden : Driver < Bool > { get }
16+ var frame : Driver < CGRect > { get }
17+ var visibleHeight : Driver < CGFloat > { get }
18+ var willShowVisibleHeight : Driver < CGFloat > { get }
19+ var isHidden : Driver < Bool > { get }
2020}
2121
2222/// RxKeyboard provides a reactive way of observing keyboard frame changes.
2323public class RxKeyboard : NSObject , RxKeyboardType {
2424
25- // MARK: Public
26-
27- /// Get a singleton instance.
28- public static let instance = RxKeyboard ( )
29-
30- /// An observable keyboard frame.
31- public let frame : Driver < CGRect >
32-
33- /// An observable visible height of keyboard. Emits keyboard height if the keyboard is visible
34- /// or `0` if the keyboard is not visible.
35- public let visibleHeight : Driver < CGFloat >
36-
37- /// Same with `visibleHeight` but only emits values when keyboard is about to show. This is
38- /// useful when adjusting scroll view content offset.
39- public let willShowVisibleHeight : Driver < CGFloat >
40-
41- /// An observable visibility of keyboard. Emits keyboard visibility
42- /// when changed keyboard show and hide.
43- public let isHidden : Driver < Bool >
44-
45- // MARK: Private
46-
47- private let disposeBag = DisposeBag ( )
48- private let panRecognizer = UIPanGestureRecognizer ( )
49-
50- // MARK: Initializing
51-
52- override init ( ) {
53-
54- let defaultFrame = CGRect (
55- x: . zero,
56- y: UIScreen . main. bounds. height,
57- width: UIScreen . main. bounds. width,
58- height: . zero
59- )
60-
61- let frameVariable = BehaviorRelay < CGRect > ( value: defaultFrame)
62-
63- frame = frameVariable. asDriver ( ) . distinctUntilChanged ( )
64- visibleHeight = frame. map { UIScreen . main. bounds. height - $0. origin. y }
65-
66- willShowVisibleHeight = visibleHeight
67- . scan ( ( visibleHeight: . zero, isShowing: false ) ) { lastState, newVisibleHeight in
68- ( visibleHeight: newVisibleHeight, isShowing: lastState. visibleHeight <= . zero && newVisibleHeight > . zero)
69- }
70- . filter { $0. isShowing }
71- . map { $0. visibleHeight }
72-
73- isHidden = visibleHeight
74- . map { $0 <= . ulpOfOne }
75- . distinctUntilChanged ( )
76-
77- super. init ( )
78-
79- // keyboard will change frame
80- let willChangeFrame = NotificationCenter . default. rx. notification ( . keyboardWillChangeFrame)
81- . map { notification -> CGRect in
82- let rectValue = notification. userInfo ? [ String . keyboardFrameEndKey] as? NSValue
83- return rectValue? . cgRectValue ?? defaultFrame
84- }
85- . map { frame -> CGRect in
86- if frame. origin. y < . zero { // if went to wrong frame
87- var newFrame = frame
88- newFrame. origin. y = UIScreen . main. bounds. height - newFrame. height
89- return newFrame
90- }
91- return frame
92- }
93-
94- // keyboard will hide
95- let willHide = NotificationCenter . default. rx. notification ( . keyboardWillHide)
96- . map { notification -> CGRect in
97- let rectValue = notification. userInfo ? [ String . keyboardFrameEndKey] as? NSValue
98- return rectValue? . cgRectValue ?? defaultFrame
99- }
100- . map { frame -> CGRect in
101- if frame. origin. y < . zero { // if went to wrong frame
102- var newFrame = frame
103- newFrame. origin. y = UIScreen . main. bounds. height
104- return newFrame
105- }
106- return frame
107- }
108-
109- // pan gesture
110- let didPan = panRecognizer. rx. event
111- . withLatestFrom ( frameVariable. asObservable ( ) ) { ( $0, $1) }
112- . flatMap { ( gestureRecognizer, frame) -> Observable < CGRect > in
113- guard case . changed = gestureRecognizer. state,
114- let window = UIApplication . shared. windows. first,
115- frame. origin. y < UIScreen . main. bounds. height else {
116- return . empty( )
117- }
118-
119- let origin = gestureRecognizer. location ( in: window)
120- var newFrame = frame
121- newFrame. origin. y = max ( origin. y, UIScreen . main. bounds. height - frame. height)
122- return . just( newFrame)
123- }
124-
125- // merge into single sequence
126- Observable . merge ( didPan, willChangeFrame, willHide)
127- . bind ( to: frameVariable)
128- . disposed ( by: disposeBag)
129-
130- // gesture recognizer
131- panRecognizer. delegate = self
132-
133- UIApplication . rx. didFinishLaunching // when RxKeyboard is initialized before UIApplication.window is created
134- . withUnretained ( panRecognizer)
135- . subscribe { gestureRecognizer, _ in
136- UIApplication . shared. windows. first? . addGestureRecognizer ( gestureRecognizer)
137- }
138- . disposed ( by: disposeBag)
139- }
25+ // MARK: Public
26+
27+ /// Get a singleton instance.
28+ public static let instance = RxKeyboard ( )
29+
30+ /// An observable keyboard frame.
31+ public let frame : Driver < CGRect >
32+
33+ /// An observable visible height of keyboard. Emits keyboard height if the keyboard is visible
34+ /// or `0` if the keyboard is not visible.
35+ public let visibleHeight : Driver < CGFloat >
36+
37+ /// Same with `visibleHeight` but only emits values when keyboard is about to show. This is
38+ /// useful when adjusting scroll view content offset.
39+ public let willShowVisibleHeight : Driver < CGFloat >
40+
41+ /// An observable visibility of keyboard. Emits keyboard visibility
42+ /// when changed keyboard show and hide.
43+ public let isHidden : Driver < Bool >
44+
45+ // MARK: Private
46+
47+ private let disposeBag = DisposeBag ( )
48+ private let panRecognizer = UIPanGestureRecognizer ( )
49+
50+ // MARK: Initializing
51+
52+ override init ( ) {
53+
54+ let keyboardWillChangeFrame = UIResponder . keyboardWillChangeFrameNotification
55+ let keyboardWillHide = UIResponder . keyboardWillHideNotification
56+ let keyboardFrameEndKey = UIResponder . keyboardFrameEndUserInfoKey
57+
58+ let defaultFrame = CGRect (
59+ x: 0 ,
60+ y: UIScreen . main. bounds. height,
61+ width: UIScreen . main. bounds. width,
62+ height: 0
63+ )
64+ let frameVariable = BehaviorRelay < CGRect > ( value: defaultFrame)
65+ self . frame = frameVariable. asDriver ( ) . distinctUntilChanged ( )
66+ self . visibleHeight = self . frame. map { UIScreen . main. bounds. height - $0. origin. y }
67+ self . willShowVisibleHeight = self . visibleHeight
68+ . scan ( ( visibleHeight: 0 , isShowing: false ) ) { lastState, newVisibleHeight in
69+ return ( visibleHeight: newVisibleHeight, isShowing: lastState. visibleHeight == 0 && newVisibleHeight > 0 )
70+ }
71+ . filter { state in state. isShowing }
72+ . map { state in state. visibleHeight }
73+ self . isHidden = self . visibleHeight. map ( { $0 == 0.0 } ) . distinctUntilChanged ( )
74+ super. init ( )
75+
76+ // keyboard will change frame
77+ let willChangeFrame = NotificationCenter . default. rx. notification ( keyboardWillChangeFrame)
78+ . map { notification -> CGRect in
79+ let rectValue = notification. userInfo ? [ keyboardFrameEndKey] as? NSValue
80+ return rectValue? . cgRectValue ?? defaultFrame
81+ }
82+ . map { frame -> CGRect in
83+ if frame. origin. y < 0 { // if went to wrong frame
84+ var newFrame = frame
85+ newFrame. origin. y = UIScreen . main. bounds. height - newFrame. height
86+ return newFrame
87+ }
88+ return frame
89+ }
90+
91+ // keyboard will hide
92+ let willHide = NotificationCenter . default. rx. notification ( keyboardWillHide)
93+ . map { notification -> CGRect in
94+ let rectValue = notification. userInfo ? [ keyboardFrameEndKey] as? NSValue
95+ return rectValue? . cgRectValue ?? defaultFrame
96+ }
97+ . map { frame -> CGRect in
98+ if frame. origin. y < 0 { // if went to wrong frame
99+ var newFrame = frame
100+ newFrame. origin. y = UIScreen . main. bounds. height
101+ return newFrame
102+ }
103+ return frame
104+ }
105+
106+ // pan gesture
107+ let didPan = self . panRecognizer. rx. event
108+ . withLatestFrom ( frameVariable. asObservable ( ) ) { ( $0, $1) }
109+ . flatMap { ( gestureRecognizer, frame) -> Observable < CGRect > in
110+ guard case . changed = gestureRecognizer. state,
111+ let window = UIApplication . shared. windows. first,
112+ frame. origin. y < UIScreen . main. bounds. height
113+ else { return . empty( ) }
114+ let origin = gestureRecognizer. location ( in: window)
115+ var newFrame = frame
116+ newFrame. origin. y = max ( origin. y, UIScreen . main. bounds. height - frame. height)
117+ return . just( newFrame)
118+ }
119+
120+ // merge into single sequence
121+ Observable . of ( didPan, willChangeFrame, willHide) . merge ( )
122+ . bind ( to: frameVariable)
123+ . disposed ( by: self . disposeBag)
124+
125+ // gesture recognizer
126+ self . panRecognizer. delegate = self
127+
128+ UIApplication . rx. didFinishLaunching // when RxKeyboard is initialized before UIApplication.window is created
129+ . subscribe ( onNext: { _ in
130+ UIApplication . shared. windows. first? . addGestureRecognizer ( self . panRecognizer)
131+ } )
132+ . disposed ( by: self . disposeBag)
133+ }
134+
140135}
141136
142137
143138// MARK: - UIGestureRecognizerDelegate
144139
145140extension RxKeyboard : UIGestureRecognizerDelegate {
146141
147- public func gestureRecognizer( _ gestureRecognizer: UIGestureRecognizer , shouldReceive touch: UITouch ) -> Bool {
148- let point = touch. location ( in: gestureRecognizer. view)
149- var view = gestureRecognizer. view? . hitTest ( point, with: nil )
150-
151- while let candidate = view {
152- if let scrollView = candidate as? UIScrollView ,
153- case . interactive = scrollView. keyboardDismissMode {
154- return true
155- }
156- view = candidate. superview
157- }
158-
159- return false
142+ public func gestureRecognizer(
143+ _ gestureRecognizer: UIGestureRecognizer ,
144+ shouldReceive touch: UITouch
145+ ) -> Bool {
146+ let point = touch. location ( in: gestureRecognizer. view)
147+ var view = gestureRecognizer. view? . hitTest ( point, with: nil )
148+ while let candidate = view {
149+ if let scrollView = candidate as? UIScrollView ,
150+ case . interactive = scrollView. keyboardDismissMode {
151+ return true
152+ }
153+ view = candidate. superview
160154 }
155+ return false
156+ }
161157
162- public func gestureRecognizer(
163- _ gestureRecognizer: UIGestureRecognizer ,
164- shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer
165- ) -> Bool {
166- return gestureRecognizer === panRecognizer
167- }
168- }
169-
170- private extension Notification . Name {
171-
172- static let keyboardWillChangeFrame = UIResponder . keyboardWillChangeFrameNotification
173- static let keyboardWillHide = UIResponder . keyboardWillHideNotification
174- }
175-
176- private extension String {
158+ public func gestureRecognizer(
159+ _ gestureRecognizer: UIGestureRecognizer ,
160+ shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer
161+ ) -> Bool {
162+ return gestureRecognizer === self . panRecognizer
163+ }
177164
178- static let keyboardFrameEndKey = UIResponder . keyboardFrameEndUserInfoKey
179165}
180-
181166#endif
182167
0 commit comments