From bbc14ce85a613289cc76d9fa513fa988c8ae380b Mon Sep 17 00:00:00 2001 From: Dinesh Kumar <> Date: Tue, 2 Feb 2021 15:03:27 +0530 Subject: [PATCH 1/2] Allow views to move based on keyboard even when not having first responders --- Source/Infra/EKContentView.swift | 25 ++++++++++++++----- .../EKAttributes+PositionConstraints.swift | 20 +++++++++++++-- 2 files changed, 37 insertions(+), 8 deletions(-) diff --git a/Source/Infra/EKContentView.swift b/Source/Infra/EKContentView.swift index ba3bd520..505b512d 100644 --- a/Source/Infra/EKContentView.swift +++ b/Source/Infra/EKContentView.swift @@ -167,7 +167,9 @@ class EKContentView: UIView { // Setup keyboard constraints switch attributes.positionConstraints.keyboardRelation { - case .bind(offset: let offset): + case .bindWhenResponder(offset: let offset): + fallthrough + case .bindAlways(offset: let offset): if let screenEdgeResistance = offset.screenEdgeResistance { resistanceConstraint = layoutToSuperview(.top, relation: .greaterThanOrEqual, offset: screenEdgeResistance, priority: .defaultLow) } @@ -542,9 +544,12 @@ extension EKContentView { private func animate(by userInfo: [AnyHashable: Any]?, entrance: Bool) { - // Guard that the entry is bound to the keyboard - guard case .bind(offset: let offset) = attributes.positionConstraints.keyboardRelation else { - return + // Guard that the entry is bound to the keyboard and has offset + guard + attributes.positionConstraints.keyboardRelation.isBound, + let offset = attributes.positionConstraints.keyboardRelation.offset + else { + return } // Convert the user info into keyboard attributes @@ -569,7 +574,7 @@ extension EKContentView { } @objc func keyboardWillShow(_ notification: Notification) { - guard containsFirstResponder else { + guard shouldAnimateOnKeyboardChanges() else { return } keyboardState = .visible @@ -585,11 +590,19 @@ extension EKContentView { } @objc func keyboardWillChangeFrame(_ notification: Notification) { - guard containsFirstResponder else { + guard shouldAnimateOnKeyboardChanges() else { return } animate(by: notification.userInfo, entrance: true) } + + private func shouldAnimateOnKeyboardChanges() -> Bool { + switch attributes.positionConstraints.keyboardRelation { + case .unbind: return false + case .bindAlways(offset: _): return true + case .bindWhenResponder(offset: _): return containsFirstResponder + } + } } // MARK: Responds to user interactions (tap / pan / swipe / touches) diff --git a/Source/Model/EntryAttributes/EKAttributes+PositionConstraints.swift b/Source/Model/EntryAttributes/EKAttributes+PositionConstraints.swift index 7c3e539d..e26aab62 100644 --- a/Source/Model/EntryAttributes/EKAttributes+PositionConstraints.swift +++ b/Source/Model/EntryAttributes/EKAttributes+PositionConstraints.swift @@ -107,11 +107,17 @@ public extension EKAttributes { } } + /** Bind the entry's bottom to the keyboard's top with an offset when keyboard contains first reponder + Additionally, the top edge of the screen can have a resistance offset which the entry isn't able to cross. + The resistance is mostly used when the device orientation changes and the entry's frame crosses the screen bounds. + Current isn't supported with center entry position.*/ + case bindWhenResponder(offset: Offset) + /** Bind the entry's bottom to the keyboard's top with an offset. Additionally, the top edge of the screen can have a resistance offset which the entry isn't able to cross. The resistance is mostly used when the device orientation changes and the entry's frame crosses the screen bounds. Current isn't supported with center entry position.*/ - case bind(offset: Offset) + case bindAlways(offset: Offset) /** Entry is unbound to the keyboard. It's location doesn't change. */ case unbind @@ -119,12 +125,22 @@ public extension EKAttributes { /** Returns true if the entry is bound to the keyboard */ public var isBound: Bool { switch self { - case .bind(offset: _): + case .bindWhenResponder(offset: _), .bindAlways(offset: _): return true case .unbind: return false } } + + var offset: Offset? { + switch self { + case .bindAlways(offset: let offset): + return offset + case .bindWhenResponder(offset: let offset): + return offset + default: return nil + } + } } /** Rotation related position constraints */ From 734ebe610d7fe27290d1680e3f82fceacc400fa5 Mon Sep 17 00:00:00 2001 From: Dinesh Kumar <> Date: Thu, 4 Feb 2021 19:18:35 +0530 Subject: [PATCH 2/2] Rename back to bind to avoid breaking change --- Source/Infra/EKContentView.swift | 4 ++-- .../EntryAttributes/EKAttributes+PositionConstraints.swift | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Source/Infra/EKContentView.swift b/Source/Infra/EKContentView.swift index 505b512d..543ddf4c 100644 --- a/Source/Infra/EKContentView.swift +++ b/Source/Infra/EKContentView.swift @@ -167,7 +167,7 @@ class EKContentView: UIView { // Setup keyboard constraints switch attributes.positionConstraints.keyboardRelation { - case .bindWhenResponder(offset: let offset): + case .bind(offset: let offset): fallthrough case .bindAlways(offset: let offset): if let screenEdgeResistance = offset.screenEdgeResistance { @@ -600,7 +600,7 @@ extension EKContentView { switch attributes.positionConstraints.keyboardRelation { case .unbind: return false case .bindAlways(offset: _): return true - case .bindWhenResponder(offset: _): return containsFirstResponder + case .bind(offset: _): return containsFirstResponder } } } diff --git a/Source/Model/EntryAttributes/EKAttributes+PositionConstraints.swift b/Source/Model/EntryAttributes/EKAttributes+PositionConstraints.swift index e26aab62..0366c0df 100644 --- a/Source/Model/EntryAttributes/EKAttributes+PositionConstraints.swift +++ b/Source/Model/EntryAttributes/EKAttributes+PositionConstraints.swift @@ -111,7 +111,7 @@ public extension EKAttributes { Additionally, the top edge of the screen can have a resistance offset which the entry isn't able to cross. The resistance is mostly used when the device orientation changes and the entry's frame crosses the screen bounds. Current isn't supported with center entry position.*/ - case bindWhenResponder(offset: Offset) + case bind(offset: Offset) /** Bind the entry's bottom to the keyboard's top with an offset. Additionally, the top edge of the screen can have a resistance offset which the entry isn't able to cross. @@ -125,7 +125,7 @@ public extension EKAttributes { /** Returns true if the entry is bound to the keyboard */ public var isBound: Bool { switch self { - case .bindWhenResponder(offset: _), .bindAlways(offset: _): + case .bind(offset: _), .bindAlways(offset: _): return true case .unbind: return false @@ -136,7 +136,7 @@ public extension EKAttributes { switch self { case .bindAlways(offset: let offset): return offset - case .bindWhenResponder(offset: let offset): + case .bind(offset: let offset): return offset default: return nil }