Skip to content

Commit

Permalink
Code review. Also, centralize calculation of final contentInset amoun…
Browse files Browse the repository at this point in the history
…ts, to ensure provided insets play nicely with the keyboard.
  • Loading branch information
kyleve committed Feb 26, 2020
1 parent e7caea5 commit ff932db
Show file tree
Hide file tree
Showing 3 changed files with 185 additions and 91 deletions.
86 changes: 40 additions & 46 deletions BlueprintUICommonControls/Sources/Internal/KeyboardObserver.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,13 @@
import UIKit


protocol KeyboardObserverDelegate : AnyObject
{
func keyboardFrameWillChange(observer : KeyboardObserver)
protocol KeyboardObserverDelegate : AnyObject {

func keyboardFrameWillChange(
for observer : KeyboardObserver,
animationDuration : Double,
options : UIView.AnimationOptions
)
}

/**
Expand All @@ -20,8 +24,8 @@ protocol KeyboardObserverDelegate : AnyObject
iOS Docs for keyboard management:
https://developer.apple.com/library/archive/documentation/StringsTextFonts/Conceptual/TextAndWebiPhoneOS/KeyboardManagement/KeyboardManagement.html
*/
final class KeyboardObserver
{
final class KeyboardObserver {
private let center : NotificationCenter

weak var delegate : KeyboardObserverDelegate?
Expand All @@ -30,8 +34,8 @@ final class KeyboardObserver
// MARK: Initialization
//

init(center : NotificationCenter = .default)
{
init(center : NotificationCenter = .default) {
self.center = center

self.center.addObserver(self, selector: #selector(keyboardFrameChanged(_:)), name: UIWindow.keyboardWillChangeFrameNotification, object: nil)
Expand All @@ -44,14 +48,14 @@ final class KeyboardObserver
// MARK: Handling Changes
//

enum KeyboardFrame : Equatable
{
enum KeyboardFrame : Equatable {
case notVisible
case visible(frame: CGRect)
}

func currentFrame(in view : UIView) -> KeyboardFrame?
{
func currentFrame(in view : UIView) -> KeyboardFrame? {
guard view.window != nil else {
return nil
}
Expand All @@ -69,55 +73,45 @@ final class KeyboardObserver
}
}

private func animate(with info : NotificationInfo, _ animations : @escaping () -> ())
{
if info.animationDuration > 0.0 {
/**
Create an animation curve with the correct curve for showing or hiding the keyboard.

This is unfortunately a private UIView curve. However, we can map it to the animation options' curve
like so: https://stackoverflow.com/questions/26939105/keyboard-animation-curve-as-int
*/
let animationOptions = UIView.AnimationOptions(rawValue: info.animationCurve << 16)

UIView.animate(
withDuration: info.animationDuration,
delay: 0.0,
options: animationOptions,
animations: animations
)
} else {
animations()
}
}

//
// MARK: Receiving Updates
//

private func receivedUpdatedKeyboardInfo(_ new : NotificationInfo)
{
private func receivedUpdatedKeyboardInfo(_ new : NotificationInfo) {
let old = self.latestNotification

self.latestNotification = new

/// Only communicate a frame change to the delegate if the frame actually changed.

if let old = old {
guard old.endingFrame != new.endingFrame else {
return
}
}

self.animate(with: new) {
self.delegate?.keyboardFrameWillChange(observer: self)
}
/**
Create an animation curve with the correct curve for showing or hiding the keyboard.

This is unfortunately a private UIView curve. However, we can map it to the animation options' curve
like so: https://stackoverflow.com/questions/26939105/keyboard-animation-curve-as-int
*/
let animationOptions = UIView.AnimationOptions(rawValue: new.animationCurve << 16)

self.delegate?.keyboardFrameWillChange(
for: self,
animationDuration: new.animationDuration,
options: animationOptions
)
}

//
// MARK: Notification Listeners
//

@objc func keyboardFrameChanged(_ notification : Notification)
{
@objc func keyboardFrameChanged(_ notification : Notification) {
do {
let info = try NotificationInfo(with: notification)
self.receivedUpdatedKeyboardInfo(info)
Expand All @@ -129,15 +123,15 @@ final class KeyboardObserver

extension KeyboardObserver
{
struct NotificationInfo : Equatable
{
struct NotificationInfo : Equatable {
var endingFrame : CGRect = .zero

var animationDuration : Double = 0.0
var animationCurve : UInt = 0

init(with notification : Notification) throws
{
init(with notification : Notification) throws {
guard let userInfo = notification.userInfo else {
throw ParseError.missingUserInfo
}
Expand All @@ -161,8 +155,8 @@ extension KeyboardObserver
self.animationCurve = animationCurve
}

enum ParseError : Error, Equatable
{
enum ParseError : Error, Equatable {
case missingUserInfo
case missingEndingFrame
case missingAnimationDuration
Expand Down
Loading

0 comments on commit ff932db

Please sign in to comment.