Skip to content

Commit

Permalink
fix(#10): not cancelled animations on iOS
Browse files Browse the repository at this point in the history
When soft input is quickly closed or opened, animations were not interrupted,
so it could end up with some blank area under content left, or content could have been cut

That commit resolves issue by adding .beginCurrentState option to UIView.animate method and
by adding some additional checks whether view is being translated up or down
  • Loading branch information
mateusz1913 committed Aug 4, 2021
1 parent 16af1c3 commit be330b3
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 13 deletions.
31 changes: 22 additions & 9 deletions ios/AvoidSoftInput.swift
Expand Up @@ -8,6 +8,8 @@ class AvoidSoftInput: RCTEventEmitter {

var isEnabled: Bool = false
var isRootViewSlideUp: Bool = false
var isRootViewSlidingDown: Bool = false
var isRootViewSlidingUp: Bool = false
var scrollContentInset: UIEdgeInsets = UIEdgeInsets.zero
var scrollIndicatorInsets: UIEdgeInsets = UIEdgeInsets.zero
var rootViewOriginY: CGFloat? = nil
Expand Down Expand Up @@ -39,20 +41,24 @@ class AvoidSoftInput: RCTEventEmitter {
return
}
let keyboardFrame = keyboardSize.cgRectValue

if hasListeners {
self.sendEvent(withName: SOFT_INPUT_SHOWN, body: [SOFT_INPUT_HEIGHT_KEY: keyboardFrame.height])
}
if isRootViewSlideUp || isEnabled == false {

if isRootViewSlideUp || isRootViewSlidingUp || isEnabled == false {
return
}

isRootViewSlidingUp = true

guard let viewController = RCTPresentedViewController(), let focusedInput = findFirstResponder(view: viewController.view), let rootView = viewController.view else {
isRootViewSlidingUp = false
return
}

if checkIfNestedInAvoidSoftInputView(view: focusedInput) {
isRootViewSlidingUp = false
return
}

Expand All @@ -62,11 +68,12 @@ class AvoidSoftInput: RCTEventEmitter {
self.focusedInput = focusedInput

guard let keyboardOffset = computeKeyboardOffset(keyboardHeight: keyboardFrame.height, firstResponder: focusedInput, containerView: rootView, rootView: rootView) else {
isRootViewSlidingUp = false
return
}

self.bottomOffset = keyboardOffset + _avoidOffset
UIView.animate(withDuration: 0.66, delay: 0.3) {
UIView.animate(withDuration: 0.66, delay: 0.3, options: [.beginFromCurrentState]) {
if let scrollView = findScrollViewForFirstResponder(view: focusedInput, rootView: rootView) {
let contentInsets = UIEdgeInsets.init(top: 0.0, left: 0.0, bottom: self.bottomOffset, right: 0.0)
self.scrollContentInset = scrollView.contentInset
Expand All @@ -79,28 +86,33 @@ class AvoidSoftInput: RCTEventEmitter {
}
}
} completion: { isCompleted in
self.isRootViewSlidingUp = false
self.isRootViewSlideUp = true
}
}

@objc func keyboardWillHide(notification: NSNotification) {
@objc func keyboardWillHide(notification: NSNotification) {
if hasListeners {
self.sendEvent(withName: SOFT_INPUT_HIDDEN, body: [SOFT_INPUT_HEIGHT_KEY: 0])
}

if !isRootViewSlideUp || isEnabled == false {
if (!isRootViewSlideUp && !isRootViewSlidingUp) || isRootViewSlidingDown || isEnabled == false {
return
}

isRootViewSlidingDown = true

guard let viewController = RCTPresentedViewController(), let focusedInput = focusedInput, let rootView = viewController.view else {
isRootViewSlidingDown = false
return
}

if checkIfNestedInAvoidSoftInputView(view: focusedInput) {
isRootViewSlidingDown = false
return
}
UIView.animate(withDuration: 0.22) {

UIView.animate(withDuration: 0.22, delay: 0, options: [.beginFromCurrentState]) {
if let scrollView = findScrollViewForFirstResponder(view: focusedInput, rootView: rootView) {
scrollView.contentInset = self.scrollContentInset
scrollView.scrollIndicatorInsets = self.scrollIndicatorInsets
Expand All @@ -110,6 +122,7 @@ class AvoidSoftInput: RCTEventEmitter {
rootView.frame.origin.y += self.bottomOffset
}
} completion: { isCompleted in
self.isRootViewSlidingDown = false
self.rootViewOriginY = nil
self.focusedInput = nil
self.isRootViewSlideUp = false
Expand Down
19 changes: 15 additions & 4 deletions ios/AvoidSoftInputView.swift
Expand Up @@ -4,6 +4,8 @@ class AvoidSoftInputView: RCTView {
var focusedInput: UIView? = nil
var originY: CGFloat? = nil
var isViewSlideUp: Bool = false
var isViewSlidingDown: Bool = false
var isViewSlidingUp: Bool = false
var scrollContentInset: UIEdgeInsets = UIEdgeInsets.zero
var scrollIndicatorInsets: UIEdgeInsets = UIEdgeInsets.zero
var bottomOffset: CGFloat = 0
Expand Down Expand Up @@ -41,11 +43,14 @@ class AvoidSoftInputView: RCTView {
onShow([SOFT_INPUT_HEIGHT_KEY: keyboardFrame.height])
}

if self.isViewSlideUp {
if self.isViewSlideUp || self.isViewSlidingUp {
return
}

self.isViewSlidingUp = true

guard let viewController = RCTPresentedViewController(), let focusedInput = findFirstResponder(view: viewController.view), let rootView = viewController.view else {
self.isViewSlidingUp = false
return
}

Expand All @@ -55,11 +60,12 @@ class AvoidSoftInputView: RCTView {
self.focusedInput = focusedInput

guard let keyboardOffset = computeKeyboardOffset(keyboardHeight: keyboardFrame.height, firstResponder: focusedInput, containerView: self, rootView: rootView) else {
self.isViewSlidingUp = false
return
}

self.bottomOffset = keyboardOffset + _avoidOffset
UIView.animate(withDuration: 0.66, delay: 0.3) {
UIView.animate(withDuration: 0.66, delay: 0.3, options: [.beginFromCurrentState]) {
if let scrollView = findScrollViewForFirstResponder(view: focusedInput, rootView: self) {
let contentInsets = UIEdgeInsets.init(top: 0.0, left: 0.0, bottom: self.bottomOffset, right: 0.0)
self.scrollContentInset = scrollView.contentInset
Expand All @@ -70,6 +76,7 @@ class AvoidSoftInputView: RCTView {
self.frame.origin.y = originY - self.bottomOffset
}
} completion: { isCompleted in
self.isViewSlidingUp = false
self.isViewSlideUp = true
}
}
Expand All @@ -79,15 +86,18 @@ class AvoidSoftInputView: RCTView {
onHide([SOFT_INPUT_HEIGHT_KEY: 0])
}

if !self.isViewSlideUp {
if (!self.isViewSlideUp && !self.isViewSlidingUp) || self.isViewSlidingDown {
return
}

self.isViewSlidingDown = true

guard let focusedInput = focusedInput else {
self.isViewSlidingDown = false
return
}

UIView.animate(withDuration: 0.22) {
UIView.animate(withDuration: 0.22, delay: 0, options: [.beginFromCurrentState]) {
if let scrollView = findScrollViewForFirstResponder(view: focusedInput, rootView: self) {
scrollView.contentInset = self.scrollContentInset
scrollView.scrollIndicatorInsets = self.scrollIndicatorInsets
Expand All @@ -101,6 +111,7 @@ class AvoidSoftInputView: RCTView {
self.focusedInput = nil
self.bottomOffset = 0
self.isViewSlideUp = false
self.isViewSlidingDown = false
}
}
}

0 comments on commit be330b3

Please sign in to comment.