8
8
9
9
import UIKit
10
10
11
-
12
- public class KeyboardLayoutGuide : UILayoutGuide {
11
+ public extension UIView {
13
12
14
- private var token : NSKeyValueObservation ? = nil
13
+ private struct AssociatedKeys {
14
+ static var keyboardLayoutGuide = " keyboardLayoutGuide "
15
+ }
16
+
17
+ /// A layout guide representing the inset for the keyboard.
18
+ /// Use this layout guide’s top anchor to create constraints pinning to the top of the keyboard.
19
+ public var keyboardLayoutGuide : KeyboardLayoutGuide {
20
+ get {
21
+ if let obj = objc_getAssociatedObject ( self , & AssociatedKeys. keyboardLayoutGuide) as? KeyboardLayoutGuide {
22
+ return obj
23
+ }
24
+ let new = KeyboardLayoutGuide ( )
25
+ addLayoutGuide ( new)
26
+ new. setUp ( )
27
+ objc_setAssociatedObject ( self , & AssociatedKeys. keyboardLayoutGuide, new as Any , . OBJC_ASSOCIATION_RETAIN_NONATOMIC)
28
+ return new
29
+ }
30
+ }
31
+ }
32
+
33
+ open class KeyboardLayoutGuide : UILayoutGuide {
15
34
16
35
public required init ? ( coder aDecoder: NSCoder ) {
17
36
fatalError ( " init(coder:) has not been implemented " )
@@ -20,31 +39,24 @@ public class KeyboardLayoutGuide: UILayoutGuide {
20
39
public override init ( ) {
21
40
super. init ( )
22
41
23
- // Observe keyboardWillShow and keyboardWillHide notifications.
42
+ // Observe keyboardWillChangeFrame notifications
24
43
let nc = NotificationCenter . default
25
44
nc. addObserver ( self ,
26
- selector: #selector( keyboardWillShow ( _: ) ) ,
27
- name: . UIKeyboardWillShow ,
45
+ selector: #selector( keyboardWillChangeFrame ( _: ) ) ,
46
+ name: . UIKeyboardWillChangeFrame ,
28
47
object: nil )
29
- nc. addObserver ( self ,
30
- selector: #selector( keyboardWillHide ( _: ) ) ,
31
- name: . UIKeyboardWillHide,
32
- object: nil )
33
-
34
- // Observe owningView so that we can setup our layoutGuide
35
- // once the user has called `view.addLayoutGuide`
36
- token = observe ( \. owningView) { object, _ in
37
- if let view = object. owningView {
38
- object. setUp ( inView: view)
39
- }
40
- }
41
48
}
42
49
43
- private func setUp( inView view: UIView ) {
44
- heightAnchor. constraint ( equalToConstant: 0 ) . isActive = true
45
- leftAnchor. constraint ( equalTo: view. leftAnchor) . isActive = true
46
- rightAnchor. constraint ( equalTo: view. rightAnchor) . isActive = true
47
- let viewBottomAnchor : NSLayoutYAxisAnchor !
50
+ internal func setUp( ) {
51
+ guard let view = owningView else {
52
+ return
53
+ }
54
+ NSLayoutConstraint . activate ( [
55
+ heightAnchor. constraint ( equalToConstant: 0 ) ,
56
+ leftAnchor. constraint ( equalTo: view. leftAnchor) ,
57
+ rightAnchor. constraint ( equalTo: view. rightAnchor) ,
58
+ ] )
59
+ let viewBottomAnchor : NSLayoutYAxisAnchor
48
60
if #available( iOS 11 . 0 , * ) {
49
61
viewBottomAnchor = view. safeAreaLayoutGuide. bottomAnchor
50
62
} else {
@@ -54,22 +66,16 @@ public class KeyboardLayoutGuide: UILayoutGuide {
54
66
}
55
67
56
68
@objc
57
- func keyboardWillShow ( _ note: Notification ) {
69
+ private func keyboardWillChangeFrame ( _ note: Notification ) {
58
70
if var height = note. keyboardHeight {
59
- if #available( iOS 11 . 0 , * ) {
71
+ if #available( iOS 11 . 0 , * ) , height > 0 {
60
72
height -= ( owningView? . safeAreaInsets. bottom) !
61
73
}
62
74
heightConstraint? . constant = height
63
75
animate ( note)
64
76
}
65
77
}
66
78
67
- @objc
68
- func keyboardWillHide( _ note: Notification ) {
69
- heightConstraint? . constant = 0
70
- animate ( note)
71
- }
72
-
73
79
private func animate( _ note: Notification ) {
74
80
if isVisible ( view: self . owningView!) {
75
81
self . owningView? . layoutIfNeeded ( )
@@ -88,7 +94,7 @@ public class KeyboardLayoutGuide: UILayoutGuide {
88
94
// MARK: - Helpers
89
95
90
96
extension UILayoutGuide {
91
- var heightConstraint : NSLayoutConstraint ? {
97
+ internal var heightConstraint : NSLayoutConstraint ? {
92
98
guard let target = owningView else { return nil }
93
99
for c in target. constraints {
94
100
if let fi = c. firstItem as? UILayoutGuide , fi == self && c. firstAttribute == . height {
@@ -104,7 +110,9 @@ extension Notification {
104
110
guard let v = userInfo ? [ UIKeyboardFrameEndUserInfoKey] as? NSValue else {
105
111
return nil
106
112
}
107
- return v. cgRectValue. size. height
113
+ // Weirdly enough UIKeyboardFrameEndUserInfoKey doesn't have the same behaviour
114
+ // in ios 10 or iOS 11 so we can't rely on v.cgRectValue.width
115
+ return UIScreen . main. bounds. height - v. cgRectValue. minY
108
116
}
109
117
}
110
118
0 commit comments