diff --git a/Example/Example/ExampleViewController.swift b/Example/Example/InputAccessoryExampleViewController.swift similarity index 94% rename from Example/Example/ExampleViewController.swift rename to Example/Example/InputAccessoryExampleViewController.swift index cb38a8d..a8f13e6 100644 --- a/Example/Example/ExampleViewController.swift +++ b/Example/Example/InputAccessoryExampleViewController.swift @@ -1,5 +1,5 @@ // -// ExampleViewController.swift +// InputAccessoryExampleViewController.swift // Example // // Created by Nathan Tannar on 2018-06-03. @@ -27,7 +27,7 @@ enum MessageInputBarStyle: String { } } -final class ExampleViewController: UITableViewController { +final class InputAccessoryExampleViewController: UITableViewController { // MARK: - Properties diff --git a/Example/Example/Style Examples/MessageInputBarStyle.swift b/Example/Example/Style Examples/MessageInputBarStyle.swift new file mode 100644 index 0000000..361f660 --- /dev/null +++ b/Example/Example/Style Examples/MessageInputBarStyle.swift @@ -0,0 +1,9 @@ +// +// MessageInputBarStyle.swift +// Example +// +// Created by Nathan Tannar on 2018-06-18. +// Copyright © 2018 MessageKit. All rights reserved. +// + +import Foundation diff --git a/Example/Example/SubviewExampleViewController.swift b/Example/Example/SubviewExampleViewController.swift new file mode 100644 index 0000000..5fc1364 --- /dev/null +++ b/Example/Example/SubviewExampleViewController.swift @@ -0,0 +1,9 @@ +// +// SubviewExampleViewController.swift +// Example +// +// Created by Nathan Tannar on 2018-06-18. +// Copyright © 2018 MessageKit. All rights reserved. +// + +import Foundation diff --git a/MessageInputBar.xcodeproj/project.pbxproj b/MessageInputBar.xcodeproj/project.pbxproj index 88cccf3..1dd5a6c 100644 --- a/MessageInputBar.xcodeproj/project.pbxproj +++ b/MessageInputBar.xcodeproj/project.pbxproj @@ -38,6 +38,9 @@ 386FB1CA20C49F5B006A93BA /* InputBarItemTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 386FB1C620C49EE8006A93BA /* InputBarItemTests.swift */; }; 386FB1CB20C49F60006A93BA /* InputTextViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 386FB1C420C49EE7006A93BA /* InputTextViewTests.swift */; }; 386FB1CC20C49F60006A93BA /* MessageInputBarTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 386FB1C520C49EE7006A93BA /* MessageInputBarTests.swift */; }; + 38F14CEF20E319F400E7CBEB /* KeyboardManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38F14CEE20E319F300E7CBEB /* KeyboardManager.swift */; }; + 38F14CF120E31A1400E7CBEB /* KeyboardNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38F14CF020E31A1400E7CBEB /* KeyboardNotification.swift */; }; + 38F14CF320E31B8900E7CBEB /* NSNotification+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38F14CF220E31B8900E7CBEB /* NSNotification+Extensions.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -88,6 +91,9 @@ 386FB1C420C49EE7006A93BA /* InputTextViewTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InputTextViewTests.swift; sourceTree = ""; }; 386FB1C520C49EE7006A93BA /* MessageInputBarTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageInputBarTests.swift; sourceTree = ""; }; 386FB1C620C49EE8006A93BA /* InputBarItemTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InputBarItemTests.swift; sourceTree = ""; }; + 38F14CEE20E319F300E7CBEB /* KeyboardManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = KeyboardManager.swift; path = Example/Example/KeyboardManager.swift; sourceTree = SOURCE_ROOT; }; + 38F14CF020E31A1400E7CBEB /* KeyboardNotification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyboardNotification.swift; sourceTree = ""; }; + 38F14CF220E31B8900E7CBEB /* NSNotification+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSNotification+Extensions.swift"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -124,6 +130,7 @@ children = ( 386FB1A420C49B39006A93BA /* UIView+Extensions.swift */, 380F6E9820C4BF0D0095975F /* NSMutableAttributedString+Extensions.swift */, + 38F14CF220E31B8900E7CBEB /* NSNotification+Extensions.swift */, ); path = Extensions; sourceTree = ""; @@ -149,6 +156,8 @@ 380F6E7A20C4ADC00095975F /* Models */ = { isa = PBXGroup; children = ( + 38F14CF020E31A1400E7CBEB /* KeyboardNotification.swift */, + 38F14CEE20E319F300E7CBEB /* KeyboardManager.swift */, 386FB1A220C49A9B006A93BA /* NSConstraintLayoutSet.swift */, ); path = Models; @@ -402,13 +411,16 @@ 380F6EA220C4D85A0095975F /* AttachmentManagerDelegate.swift in Sources */, 380F6E7C20C4BB0E0095975F /* InputPlugin.swift in Sources */, 386FB19E20C499B3006A93BA /* InputTextView.swift in Sources */, + 38F14CF320E31B8900E7CBEB /* NSNotification+Extensions.swift in Sources */, 386FB19F20C499B3006A93BA /* MessageInputBarDelegate.swift in Sources */, 380F6E7520C4A9A80095975F /* InputItem.swift in Sources */, 380F6E8720C4BD720095975F /* AutocompleteSession.swift in Sources */, + 38F14CEF20E319F400E7CBEB /* KeyboardManager.swift in Sources */, 380F6EA520C4D86D0095975F /* AttachmentManager.swift in Sources */, 380F6E8F20C4BE6F0095975F /* AutocompleteManagerDataSource.swift in Sources */, 386FB1A020C499B3006A93BA /* MessageInputBar.swift in Sources */, 386FB1A120C499B3006A93BA /* SeparatorLine.swift in Sources */, + 38F14CF120E31A1400E7CBEB /* KeyboardNotification.swift in Sources */, 380F6E9620C4BEA60095975F /* AutocompleteCell.swift in Sources */, 386FB19D20C499B3006A93BA /* InputStackView.swift in Sources */, 380F6E8A20C4BE5C0095975F /* String+Extensions.swift in Sources */, diff --git a/Sources/Extensions/NSNotification+Extensions.swift b/Sources/Extensions/NSNotification+Extensions.swift new file mode 100644 index 0000000..fdf3e74 --- /dev/null +++ b/Sources/Extensions/NSNotification+Extensions.swift @@ -0,0 +1,85 @@ +/* + MIT License + + Copyright (c) 2017-2018 MessageKit + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + */ + +import Foundation + +extension NSNotification { + + internal var event: KeyboardManager.KeyboardEvent { + switch self.name { + case .UIKeyboardWillShow: + return .willShow + case .UIKeyboardDidShow: + return .didShow + case .UIKeyboardWillHide: + return .willHide + case .UIKeyboardDidHide: + return .didHide + case .UIKeyboardWillChangeFrame: + return .willChangeFrame + case .UIKeyboardDidChangeFrame: + return .didChangeFrame + default: + return .unknown + } + } + + internal var timeInterval: TimeInterval? { + guard let value = userInfo?[UIKeyboardAnimationDurationUserInfoKey] as? NSNumber else { return nil } + return TimeInterval(truncating: value) + } + + internal var animationCurve: UIViewAnimationCurve? { + guard let index = (userInfo?[UIKeyboardAnimationCurveUserInfoKey] as? NSNumber)?.intValue, let curve = UIViewAnimationCurve(rawValue:index) else { return nil } + return curve + } + + internal var animationOptions: UIViewAnimationOptions { + guard let curve = animationCurve else { return [] } + switch curve { + case .easeIn: + return .curveEaseIn + case .easeOut: + return .curveEaseOut + case .easeInOut: + return .curveEaseInOut + case .linear: + return .curveLinear + } + } + + internal var startFrame: CGRect? { + return (userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue + } + + internal var endFrame: CGRect? { + return (userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue + } + + internal var isForCurrentApp: Bool? { + return (userInfo?[UIKeyboardIsLocalUserInfoKey] as? NSNumber)?.boolValue + } + +} + diff --git a/Sources/MessageInputBar.swift b/Sources/MessageInputBar.swift index dde7e92..3907093 100644 --- a/Sources/MessageInputBar.swift +++ b/Sources/MessageInputBar.swift @@ -270,6 +270,10 @@ open class MessageInputBar: UIView { /// The plugins injected into the MessageInputBar open var plugins: [InputPlugin] = [] + open override var keyCommands: [UIKeyCommand]? { + return [UIKeyCommand(input: "\r", modifierFlags: .command, action: #selector(didSelectSendButton))] + } + // MARK: - Auto-Layout Management private var textViewLayoutSet: NSLayoutConstraintSet? @@ -758,6 +762,7 @@ open class MessageInputBar: UIView { /// Calls the delegates `didPressSendButtonWith` method /// Assumes that the InputTextView's text has been set to empty and calls `inputTextViewDidChange()` /// Invalidates each of the inputManagers + @objc open func didSelectSendButton() { delegate?.messageInputBar(self, didPressSendButtonWith: inputTextView.text) } diff --git a/Sources/Models/KeyboardManager.swift b/Sources/Models/KeyboardManager.swift new file mode 100644 index 0000000..fcd71ab --- /dev/null +++ b/Sources/Models/KeyboardManager.swift @@ -0,0 +1,303 @@ +/* + MIT License + + Copyright (c) 2017-2018 MessageKit + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + */ + +import UIKit + +open class KeyboardManager: NSObject, UIGestureRecognizerDelegate { + + /// Keyboard events that can happen. Translates directly to `UIKeyboard` notifications from UIKit. + public enum KeyboardEvent { + /// Event raised by UIKit's `.UIKeyboardWillShow`. + case willShow + + /// Event raised by UIKit's `.UIKeyboardDidShow`. + case didShow + + /// Event raised by UIKit's `.UIKeyboardWillShow`. + case willHide + + /// Event raised by UIKit's `.UIKeyboardDidHide`. + case didHide + + /// Event raised by UIKit's `.UIKeyboardWillChangeFrame`. + case willChangeFrame + + /// Event raised by UIKit's `.UIKeyboardDidChangeFrame`. + case didChangeFrame + + /// Non-keyboard based event raised by UIKit + case unknown + } + + /// A callback that passes a `KeyboardNotification` as an input + public typealias EventCallback = (KeyboardNotification)->Void + + /// A weak reference to a view bounded to the top of the keyboard to act as an `InputAccessoryView` + /// but kept within the bounds of the `UIViewController`s view + open weak var inputAccessoryView: UIView? + + /// A flag that indicates if a portion of the keyboard is visible on the screen + private(set) public var isKeyboardHidden: Bool = true + + /// The `NSLayoutConstraintSet` that holds the `inputAccessoryView` to the bottom if its superview + private var constraints: NSLayoutConstraintSet? + + /// A weak reference to a `UIScrollView` that has been attached for interactive keyboard dismissal + private weak var scrollView: UIScrollView? + + /// The `EventCallback` actions for each `KeyboardEvent`. Default value is EMPTY + private var callbacks: [KeyboardEvent: EventCallback] = [:] + + /// The pan gesture that handles dragging on the `scrollView` + private var panGesture: UIPanGestureRecognizer? + + /// A cached notification used as a starting point when a user dragging the `scrollView` down + /// to interactively dismiss the keyboard + private var cachedNotification: KeyboardNotification? + + // MARK: - Initialization + + /// Creates a `KeyboardManager` object an binds the view as fake `InputAccessoryView` + /// + /// - Parameter inputAccessoryView: The view to bind to the top of the keyboard but within its superview + public convenience init(inputAccessoryView: UIView) { + self.init() + self.bind(inputAccessoryView: inputAccessoryView) + } + + /// Creates a `KeyboardManager` object that observes the state of the keyboard + public override init() { + super.init() + addObservers() + } + + required public init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + // MARK: - De-Initialization + + deinit { + NotificationCenter.default.removeObserver(self) + } + + // MARK: - Keyboard Observer + + /// Add an observer for each keyboard notification + private func addObservers() { + NotificationCenter.default.addObserver(self, + selector: #selector(keyboardWillShow(notification:)), + name: NSNotification.Name.UIKeyboardWillShow, + object: nil) + NotificationCenter.default.addObserver(self, + selector: #selector(keyboardDidShow(notification:)), + name: NSNotification.Name.UIKeyboardDidShow, + object: nil) + NotificationCenter.default.addObserver(self, + selector: #selector(keyboardWillHide(notification:)), + name: NSNotification.Name.UIKeyboardWillHide, + object: nil) + NotificationCenter.default.addObserver(self, + selector: #selector(keyboardDidHide(notification:)), + name: NSNotification.Name.UIKeyboardDidHide, + object: nil) + NotificationCenter.default.addObserver(self, + selector: #selector(keyboardWillChangeFrame(notification:)), + name: NSNotification.Name.UIKeyboardWillChangeFrame, + object: nil) + NotificationCenter.default.addObserver(self, + selector: #selector(keyboardDidChangeFrame(notification:)), + name: NSNotification.Name.UIKeyboardDidChangeFrame, + object: nil) + } + + // MARK: - Mutate Callback Dictionary + + /// Sets the `EventCallback` for a `KeyboardEvent` + /// + /// - Parameters: + /// - event: KeyboardEvent + /// - callback: EventCallback + /// - Returns: Self + @discardableResult + open func on(event: KeyboardEvent, do callback: EventCallback?) -> Self { + callbacks[event] = callback + return self + } + + /// Constrains the `inputAccessoryView` to the bottom of its superview and sets the + /// `.willChangeFrame` and `.willHide` event callbacks such that it mimics an `InputAccessoryView` + /// that is bound to the top of the keyboard + /// + /// - Parameter inputAccessoryView: The view to bind to the top of the keyboard but within its superview + /// - Returns: Self + @discardableResult + open func bind(inputAccessoryView: UIView) -> Self { + + self.inputAccessoryView = inputAccessoryView + assert(inputAccessoryView.superview != nil, "`inputAccessoryView` must have a superview") + guard let superview = inputAccessoryView.superview else { fatalError() } + inputAccessoryView.translatesAutoresizingMaskIntoConstraints = false + constraints = NSLayoutConstraintSet( + bottom: inputAccessoryView.bottomAnchor.constraint(equalTo: superview.bottomAnchor), + left: inputAccessoryView.leftAnchor.constraint(equalTo: superview.leftAnchor), + right: inputAccessoryView.rightAnchor.constraint(equalTo: superview.rightAnchor) + ).activate() + + callbacks[.willChangeFrame] = { [weak self] (notification) in + let keyboardHeight = notification.endFrame.height + guard + self?.isKeyboardHidden == false || self?.constraints?.bottom?.constant == 0, + notification.isForCurrentApp else { return } + self?.animateAlongside(notification) { + self?.constraints?.bottom?.constant = -keyboardHeight + self?.inputAccessoryView?.superview?.layoutIfNeeded() + } + } + callbacks[.willHide] = { [weak self] (notification) in + guard notification.isForCurrentApp else { return } + self?.animateAlongside(notification) { [weak self] in + self?.constraints?.bottom?.constant = 0 + self?.inputAccessoryView?.superview?.layoutIfNeeded() + } + } + return self + } + + /// Adds a `UIPanGestureRecognizer` to the `scrollView` to enable interactive dismissal` + /// + /// - Parameter scrollView: UIScrollView + /// - Returns: Self + @discardableResult + open func bind(to scrollView: UIScrollView) -> Self { + self.scrollView = scrollView + self.scrollView?.keyboardDismissMode = .interactive // allows dismissing keyboard interactively + let recognizer = UIPanGestureRecognizer(target: self, action: #selector(handlePanGestureRecognizer)) + recognizer.delegate = self + self.panGesture = recognizer + self.scrollView?.addGestureRecognizer(recognizer) + return self + } + + // MARK: - Keyboard Notifications + + /// An observer method called last in the lifecycle of a keyboard becoming visible + /// + /// - Parameter notification: NSNotification + @objc + open func keyboardDidShow(notification: NSNotification) { + guard let keyboardNotification = KeyboardNotification(from: notification) else { return } + callbacks[.didShow]?(keyboardNotification) + } + + /// An observer method called last in the lifecycle of a keyboard becoming hidden + /// + /// - Parameter notification: NSNotification + @objc + open func keyboardDidHide(notification: NSNotification) { + isKeyboardHidden = true + guard let keyboardNotification = KeyboardNotification(from: notification) else { return } + callbacks[.didHide]?(keyboardNotification) + } + + /// An observer method called third in the lifecycle of a keyboard becoming visible/hidden + /// + /// - Parameter notification: NSNotification + @objc + open func keyboardDidChangeFrame(notification: NSNotification) { + guard let keyboardNotification = KeyboardNotification(from: notification) else { return } + callbacks[.didChangeFrame]?(keyboardNotification) + cachedNotification = keyboardNotification + } + + /// An observer method called first in the lifecycle of a keyboard becoming visible/hidden + /// + /// - Parameter notification: NSNotification + @objc + open func keyboardWillChangeFrame(notification: NSNotification) { + guard let keyboardNotification = KeyboardNotification(from: notification) else { return } + callbacks[.willChangeFrame]?(keyboardNotification) + cachedNotification = keyboardNotification + } + + /// An observer method called second in the lifecycle of a keyboard becoming visible + /// + /// - Parameter notification: NSNotification + @objc + open func keyboardWillShow(notification: NSNotification) { + isKeyboardHidden = false + guard let keyboardNotification = KeyboardNotification(from: notification) else { return } + callbacks[.willShow]?(keyboardNotification) + } + + /// An observer method called second in the lifecycle of a keyboard becoming hidden + /// + /// - Parameter notification: NSNotification + @objc + open func keyboardWillHide(notification: NSNotification) { + guard let keyboardNotification = KeyboardNotification(from: notification) else { return } + callbacks[.willHide]?(keyboardNotification) + } + + // MARK: - Helper Methods + + private func animateAlongside(_ notification: KeyboardNotification, animations: @escaping ()->Void) { + UIView.animate(withDuration: notification.timeInterval, delay: 0, options: [notification.animationOptions, .allowAnimatedContent, .beginFromCurrentState], animations: animations, completion: nil) + } + + // MARK: - UIGestureRecognizerDelegate + + /// Starts with the cached `KeyboardNotification` and calculates a new `endFrame` based + /// on the `UIPanGestureRecognizer` then calls the `.willChangeFrame` `EventCallback` action + /// + /// - Parameter recognizer: UIPanGestureRecognizer + @objc + open func handlePanGestureRecognizer(recognizer: UIPanGestureRecognizer) { + guard + var keyboardNotification = cachedNotification, + case .changed = recognizer.state, + let view = recognizer.view, + let window = UIApplication.shared.windows.first + else { return } + + let location = recognizer.location(in: view) + let absoluteLocation = view.convert(location, to: window) + var frame = keyboardNotification.endFrame + frame.origin.y = max(absoluteLocation.y, window.bounds.height - frame.height) + frame.size.height = window.bounds.height - frame.origin.y + keyboardNotification.endFrame = frame + callbacks[.willChangeFrame]?(keyboardNotification) + } + + /// Only receive a `UITouch` event when the `scrollView`'s keyboard dismiss mode is interactive + open func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool { + return scrollView?.keyboardDismissMode == .interactive + } + + /// Only recognice simultaneous gestures when its the `panGesture` + open func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { + return gestureRecognizer === panGesture + } + +} diff --git a/Sources/Models/KeyboardNotification.swift b/Sources/Models/KeyboardNotification.swift new file mode 100644 index 0000000..825864a --- /dev/null +++ b/Sources/Models/KeyboardNotification.swift @@ -0,0 +1,51 @@ +/* + MIT License + + Copyright (c) 2017-2018 MessageKit + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + */ + +import Foundation + +public struct KeyboardNotification { + + public let event: KeyboardManager.KeyboardEvent + + public let timeInterval: TimeInterval + + public let animationOptions: UIViewAnimationOptions + + public let isForCurrentApp: Bool + + public var startFrame: CGRect + + public var endFrame: CGRect + + public init?(from notification: NSNotification) { + guard notification.event != .unknown else { return nil } + self.event = notification.event + self.timeInterval = notification.timeInterval ?? 0.25 + self.animationOptions = notification.animationOptions + self.isForCurrentApp = notification.isForCurrentApp ?? true + self.startFrame = notification.startFrame ?? .zero + self.endFrame = notification.endFrame ?? .zero + } + +}