diff --git a/Example/Example.xcodeproj/project.pbxproj b/Example/Example.xcodeproj/project.pbxproj index bff6821..d2ffb68 100644 --- a/Example/Example.xcodeproj/project.pbxproj +++ b/Example/Example.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 46B30DCD2583F63100A25E66 /* KeyboardAnimationsExampleViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46B30DCC2583F63100A25E66 /* KeyboardAnimationsExampleViewController.swift */; }; F1C4EBE5218A37D700B8A9F7 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1C4EBE4218A37D700B8A9F7 /* AppDelegate.swift */; }; F1C4EBE7218A37D700B8A9F7 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1C4EBE6218A37D700B8A9F7 /* ViewController.swift */; }; F1C4EBEA218A37D700B8A9F7 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F1C4EBE8218A37D700B8A9F7 /* Main.storyboard */; }; @@ -70,6 +71,7 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 46B30DCC2583F63100A25E66 /* KeyboardAnimationsExampleViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyboardAnimationsExampleViewController.swift; sourceTree = ""; }; F1C4EBE1218A37D700B8A9F7 /* Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Example.app; sourceTree = BUILT_PRODUCTS_DIR; }; F1C4EBE4218A37D700B8A9F7 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; F1C4EBE6218A37D700B8A9F7 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; @@ -139,6 +141,7 @@ children = ( F1C4EBE4218A37D700B8A9F7 /* AppDelegate.swift */, F1C4EBE6218A37D700B8A9F7 /* ViewController.swift */, + 46B30DCC2583F63100A25E66 /* KeyboardAnimationsExampleViewController.swift */, F1C4EBE8218A37D700B8A9F7 /* Main.storyboard */, F1C4EBEB218A37D800B8A9F7 /* Assets.xcassets */, F1C4EBED218A37D800B8A9F7 /* LaunchScreen.storyboard */, @@ -346,6 +349,7 @@ buildActionMask = 2147483647; files = ( F1C4EBE7218A37D700B8A9F7 /* ViewController.swift in Sources */, + 46B30DCD2583F63100A25E66 /* KeyboardAnimationsExampleViewController.swift in Sources */, F1C4EBE5218A37D700B8A9F7 /* AppDelegate.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/Example/Example/Base.lproj/Main.storyboard b/Example/Example/Base.lproj/Main.storyboard index 6b74573..1e16679 100644 --- a/Example/Example/Base.lproj/Main.storyboard +++ b/Example/Example/Base.lproj/Main.storyboard @@ -1,10 +1,11 @@ - + - + + @@ -34,6 +35,7 @@ + @@ -49,59 +51,59 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Example/Example/KeyboardAnimationsExampleViewController.swift b/Example/Example/KeyboardAnimationsExampleViewController.swift new file mode 100644 index 0000000..6d1b00f --- /dev/null +++ b/Example/Example/KeyboardAnimationsExampleViewController.swift @@ -0,0 +1,63 @@ +import UIKit + +class KeyboardAnimationsExampleViewController: UIViewController { + @IBOutlet weak var textView: UITextField! + @IBOutlet weak var bottomConstraintForAnimation: NSLayoutConstraint! + private var notificationObservers: [NSObjectProtocol] = [] + + override func viewDidLoad() { + super.viewDidLoad() + startObservingKeyboardChanges() + } + + deinit { + stopObservingKeyboardChanges() + } + + @IBAction func didTapButton(_ sender: Any) { + if textView.isFirstResponder { + textView.resignFirstResponder() + } else { + textView.becomeFirstResponder() + } + } + + private func keybaordDidOpen(_ endFrame: CGRect) { + bottomConstraintForAnimation.constant = endFrame.height - self.view.safeAreaInsets.bottom + 10 + view.setNeedsLayout() + view.layoutIfNeeded() + } + + func startObservingKeyboardChanges() { + let willShowObserver = NotificationCenter.default.addObserver(forName: UIResponder.keyboardWillShowNotification, object: nil, queue: .main) { (notification) in + UIView.animate(withKeyboard: notification) { (_, endFrame) in + self.keybaordDidOpen(endFrame) + } + } + + let willHideObserver = NotificationCenter.default.addObserver(forName: UIResponder.keyboardWillHideNotification, object: nil, queue: .main) { (notification) in + UIView.animate(withKeyboard: notification) { (beginFrame, endFrame) in + self.bottomConstraintForAnimation.constant = 20 + self.view.setNeedsLayout() + self.view.layoutIfNeeded() + } + } + + let willChangeFrameObserver = NotificationCenter.default.addObserver(forName: UIResponder.keyboardWillChangeFrameNotification, object: nil, queue: .main) { (notification) in + UIView.animate(withKeyboard: notification) { (_, endFrame) in + self.keybaordDidOpen(endFrame) + } + } + + notificationObservers.append(willShowObserver) + notificationObservers.append(willHideObserver) + notificationObservers.append(willChangeFrameObserver) + } + + private func stopObservingKeyboardChanges() { + notificationObservers.forEach { (observer) in + NotificationCenter.default.removeObserver(observer) + } + notificationObservers = [] + } +} diff --git a/Example/Example/ViewController.swift b/Example/Example/ViewController.swift index eaf181b..44ccfc4 100644 --- a/Example/Example/ViewController.swift +++ b/Example/Example/ViewController.swift @@ -33,6 +33,11 @@ class ViewController: UITableViewController self.showFancyButtons() }) ]), + DemoSection(title: "Keyboard Animations", rows: [ + DemoRow(title: "Keyboard Animations", action: { + self.performSegue(withIdentifier: "KeyboardAnimationsSegue", sender: self) + }) + ]) ] } diff --git a/WordPressUI.podspec b/WordPressUI.podspec index f7b1412..ff33b2d 100644 --- a/WordPressUI.podspec +++ b/WordPressUI.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "WordPressUI" - s.version = "1.8.0" + s.version = "1.9.0-beta.1" s.summary = "Home of reusable WordPress UI components." s.description = <<-DESC diff --git a/WordPressUI/Extensions/UIView+Animations.swift b/WordPressUI/Extensions/UIView+Animations.swift index 9587a63..366f277 100644 --- a/WordPressUI/Extensions/UIView+Animations.swift +++ b/WordPressUI/Extensions/UIView+Animations.swift @@ -1,5 +1,4 @@ -import Foundation - +import UIKit // MARK: UIView Animation Helpers // @@ -153,6 +152,23 @@ extension UIView { } } + /// Coordinates an animation block alongside a keyboard's notification animation event. + /// - Parameters: + /// - notification: A notficiation from a keyboard change event (keyboardWillShowNotification, keyboardWillHideNotification, etc) + /// - animations: The animation block to be preformed. The block will provide the rects from keyboardFrameBeginUserInfoKey and keyboardFrameEndUserInfoKey to the animation block. + /// + public static func animate(withKeyboard notification: Notification, _ animations: @escaping (CGRect, CGRect) -> Void ) { + guard let userInfo = notification.userInfo else { return } + let duration: TimeInterval = userInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as? TimeInterval ?? 0 + let beginFrame: CGRect = (userInfo[UIResponder.keyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue ?? CGRect.zero + let endFrame: CGRect = (userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue ?? CGRect.zero + let animationCurve: AnimationOptions = AnimationOptions(rawValue: (userInfo[UIResponder.keyboardAnimationCurveUserInfoKey] as? UInt ?? 0)) + + UIView.animate(withDuration: duration, delay: 0, options: animationCurve, animations: { + animations(beginFrame, endFrame) + }, completion: nil) + } + /// Private Constants /// private struct Animations {