Skip to content

Commit af6d19d

Browse files
authored
refactor: split KeyboardMovementObserver implementation (#1071)
## 📜 Description Split `KeyboardMovementObserver` implementation into several files. ## 💡 Motivation and Context The main issue is that whenever I add new lines of the code into the file I instantly get complaints from `swiftlint` that I exceed max allowed number of lines. I don't want to disable this rule, because I strongly agree that fields with 300+ lines of code are hard to read/memoize in your head. In fact `KeyboardMovementObserver` implementation consist from 4 different (but toughly related parts): - KVO (interactive keyboard); - DisplayLink (to watch keyboard movement during animation) - lifecycles methods (mount/unmount) - listeners (subscription to keyboard events) - animation (initialize animation driver to deliver better synchronization when reading keyboard coordinates via `DisplayLink`) And such structured stuff very easily can be splitted across different files. For that I used `extension` approach and grouped logically all parts into groups. Now the structure of this file is more molecular and extendable, so it'll simplify support in the future 😎 ## 📢 Changelog <!-- High level overview of important changes --> <!-- For example: fixed status bar manipulation; added new types declarations; --> <!-- If your changes don't affect one of platform/language below - then remove this platform/language --> ### iOS - created `KeyboardMovementObserver+Listeners.swift` file - created `KeyboardMovementObserver+Watcher.swift` file; - created `KeyboardMovementObserver+Interactive.swift` file; - created `KeyboardMovementObserver+Lifecycles.swift` file; - created `KeyboardMovementObserver+Animation.swift` file; ## 🤔 How Has This Been Tested? Tested via CI. ## 📝 Checklist - [x] CI successfully passed - [x] I added new mocks and corresponding unit-tests if library API was changed
1 parent b6e3594 commit af6d19d

10 files changed

Lines changed: 347 additions & 306 deletions

ios/observers/KeyboardMovementObserver.swift

Lines changed: 0 additions & 306 deletions
This file was deleted.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
//
2+
// KeyboardMovementObserver+Animation.swift
3+
// Pods
4+
//
5+
// Created by Kiryl Ziusko on 07/08/2025.
6+
//
7+
8+
extension KeyboardMovementObserver {
9+
func initializeAnimation(fromValue: Double, toValue: Double) {
10+
for key in ["position", "opacity"] {
11+
if let keyboardAnimation = keyboardTrackingView.view?.layer.presentation()?.animation(forKey: key) {
12+
if let springAnimation = keyboardAnimation as? CASpringAnimation {
13+
animation = SpringAnimation(animation: springAnimation, fromValue: fromValue, toValue: toValue)
14+
} else if let basicAnimation = keyboardAnimation as? CABasicAnimation {
15+
animation = TimingAnimation(animation: basicAnimation, fromValue: fromValue, toValue: toValue)
16+
}
17+
return
18+
}
19+
}
20+
}
21+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
//
2+
// KeyboardMovementObserver+Interactive.swift
3+
// Pods
4+
//
5+
// Created by Kiryl Ziusko on 07/08/2025.
6+
//
7+
8+
extension KeyboardMovementObserver {
9+
func setupKVObserver() {
10+
guard interactiveKeyboardObserver == nil, let view = keyboardTrackingView.view else { return }
11+
12+
interactiveKeyboardObserver = view.observe(\.center, options: .new) { [weak self] _, change in
13+
guard let self = self, let changeValue = change.newValue else { return }
14+
15+
self.keyboardDidMoveInteractively(changeValue: changeValue)
16+
}
17+
}
18+
19+
func removeKVObserver() {
20+
interactiveKeyboardObserver?.invalidate()
21+
interactiveKeyboardObserver = nil
22+
}
23+
24+
private func keyboardDidMoveInteractively(changeValue: CGPoint) {
25+
if UIResponder.isKeyboardPreloading {
26+
return
27+
}
28+
// if we are currently animating keyboard -> we need to ignore values from KVO
29+
if !displayLink.isPaused {
30+
return
31+
}
32+
33+
let position = keyboardTrackingView.interactive(point: changeValue)
34+
35+
if position == KeyboardTrackingView.invalidPosition {
36+
return
37+
}
38+
39+
if position == 0 {
40+
// it will be triggered before `keyboardWillDisappear` and
41+
// we don't need to trigger `onInteractive` handler for that
42+
// since it will be handled in `keyboardWillDisappear` function
43+
return
44+
}
45+
46+
prevKeyboardPosition = position
47+
48+
onEvent(
49+
"onKeyboardMoveInteractive",
50+
position as NSNumber,
51+
position / CGFloat(keyboardHeight) as NSNumber,
52+
-1,
53+
tag
54+
)
55+
}
56+
}

0 commit comments

Comments
 (0)