Skip to content

Commit 27d578d

Browse files
committed
Lenient screen parsing. Fallback to window.screen.
1 parent 6aebeae commit 27d578d

File tree

1 file changed

+21
-40
lines changed

1 file changed

+21
-40
lines changed

ListableUI/Sources/KeyboardObserver/KeyboardObserver.swift

Lines changed: 21 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -146,29 +146,22 @@ public final class KeyboardObserver {
146146

147147
/// How the keyboard overlaps the view provided. If the view is not on screen (eg, no window),
148148
/// or the observer has not yet learned about the keyboard's position, this method returns nil.
149-
public func currentFrame(in view : UIView) -> KeyboardFrame? {
149+
func currentFrame(in view: UIView) -> KeyboardFrame? {
150150

151-
guard view.window != nil else {
151+
guard let window = view.window else {
152152
return nil
153153
}
154154

155155
guard let notification = latestNotification else {
156156
return nil
157157
}
158158

159-
let frame: CGRect
159+
let screen = notification.screen ?? window.screen
160160

161-
if #available(iOS 16.1, *) {
162-
frame = notification.screen.coordinateSpace.convert(
163-
notification.endingFrame,
164-
to: view
165-
)
166-
} else {
167-
frame = view.convert(
168-
notification.endingFrame,
169-
from: nil
170-
)
171-
}
161+
let frame = screen.coordinateSpace.convert(
162+
notification.endingFrame,
163+
to: view
164+
)
172165

173166
if frame.intersects(view.bounds) {
174167
return .overlapping(frame: frame)
@@ -225,23 +218,19 @@ extension KeyboardObserver {
225218
var animationDuration: Double = 0.0
226219
var animationCurve: UIView.AnimationCurve = .easeInOut
227220

228-
@available(iOS 16.1, *)
229-
var screen: UIScreen {
230-
get {
231-
guard let screen = _screen else {
232-
fatalError("UIScreen value was not initialized from notification object.")
233-
}
234-
return screen
235-
}
236-
set {
237-
_screen = newValue
238-
}
239-
}
240-
241-
// Note: Using this to work around: "Stored properties cannot be marked
242-
// potentially unavailable with '@available'"
243-
// Can be removed when deployment target is >= 16.1. @available(iOS 16.1, *)
244-
private var _screen: UIScreen?
221+
/// The `UIScreen` that the keyboard appears on.
222+
///
223+
/// This may influence the `KeyboardFrame` calculation when the app is not in full screen,
224+
/// such as in Split View, Slide Over, and Stage Manager.
225+
///
226+
/// - note: In iOS 16.1 and later, every `keyboardWillChangeFrameNotification` and
227+
/// `keyboardDidChangeFrameNotification` is _supposed_ to include a `UIScreen`
228+
/// in a the notification, however we've had reports that this isn't always the case (at least when
229+
/// using the iOS 16.1 simulator runtime). If a screen is _not_ included in an iOS 16.1+ notification,
230+
/// we do not throw a `ParseError` as it would cause the entire notification to be discarded.
231+
///
232+
/// [Apple Documentation](https://developer.apple.com/documentation/uikit/uiresponder/1621623-keyboardwillchangeframenotificat)
233+
var screen: UIScreen?
245234

246235
init(with notification: Notification) throws {
247236

@@ -269,14 +258,7 @@ extension KeyboardObserver {
269258

270259
self.animationCurve = animationCurve
271260

272-
if #available(iOS 16.1, *) {
273-
guard let screen = notification.object as? UIScreen else {
274-
throw ParseError.missingScreen
275-
}
276-
277-
self.screen = screen
278-
}
279-
261+
screen = notification.object as? UIScreen
280262
}
281263

282264
enum ParseError: Error, Equatable {
@@ -285,7 +267,6 @@ extension KeyboardObserver {
285267
case missingEndingFrame
286268
case missingAnimationDuration
287269
case missingAnimationCurve
288-
case missingScreen
289270
}
290271
}
291272
}

0 commit comments

Comments
 (0)