Skip to content
Permalink
Browse files

Add KeyboardAvoiding target files

  • Loading branch information...
a2 committed Jun 7, 2019
1 parent d29cbac commit 2867841f31fa29b13d7bab7e0ee702db520016b4
@@ -0,0 +1,31 @@
// swift-tools-version:5.1
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription

let package = Package(
name: "KeyboardAvoiding",
platforms: [
.iOS(.v13)
],
products: [
// Products define the executables and libraries produced by a package, and make them visible to other packages.
.library(
name: "KeyboardAvoiding",
targets: ["KeyboardAvoiding"]),
],
dependencies: [
// Dependencies declare other packages that this package depends on.
// .package(url: /* package url */, from: "1.0.0"),
],
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
// Targets can depend on other targets in this package, and on products in packages which this package depends on.
.target(
name: "KeyboardAvoiding",
dependencies: []),
.testTarget(
name: "KeyboardAvoidingTests",
dependencies: ["KeyboardAvoiding"]),
]
)
@@ -0,0 +1,78 @@
import SwiftUI

class KeyboardAvoidingHostingController<Content>: UIHostingController<Content> where Content: View {
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillChangeFrame), name: UIResponder.keyboardWillChangeFrameNotification, object: nil)
}

override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillChangeFrameNotification, object: nil)
}

@objc private func keyboardWillChangeFrame(_ notification: Notification) {
guard isViewLoaded, let window = view.window, let userInfo = notification.userInfo else {
return
}

guard let duration = userInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as? TimeInterval else {
return
}

guard let rawAnimationCurve = userInfo[UIResponder.keyboardAnimationCurveUserInfoKey] as? UInt else {
return
}

guard let endFrame = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect else {
return
}

let endFrameInWindow = window.convert(endFrame, from: nil)
let endFrameInView = view.convert(endFrameInWindow, from: nil)
let endFrameIntersection = view.bounds.intersection(endFrameInView)
let keyboardHeight = view.bounds.maxY - endFrameIntersection.minY

let options = UIView.AnimationOptions(rawValue: rawAnimationCurve << 16)
UIView.animate(withDuration: duration, delay: 0, options: options, animations: {
self.additionalSafeAreaInsets.bottom = keyboardHeight
self.view.layoutIfNeeded()
})
}
}

struct KeyboardAvoidingViewController<Content>: UIViewControllerRepresentable where Content: View {
var rootView: Content

func makeUIViewController(context: Context) -> KeyboardAvoidingHostingController<Content> {
return KeyboardAvoidingHostingController(rootView: rootView)
}

func updateUIViewController(_ uiViewController: KeyboardAvoidingHostingController<Content>, context: Context) {
uiViewController.rootView = rootView
}
}

public struct KeyboardAvoidingView<Content>: View where Content: View {
let content: Content

public init(@ViewBuilder content: () -> Content) {
self.content = content()
}

public var body: some View {
return KeyboardAvoidingViewController(rootView: content)
}
}

public struct KeyboardAvoiding: ViewModifier {
public func body(content: _ViewModifier_Content<KeyboardAvoiding>) -> KeyboardAvoidingView<_ViewModifier_Content<KeyboardAvoiding>> {
return KeyboardAvoidingView { content }
}
}

public extension View {
func keyboardAvoiding() -> Self.Modified<KeyboardAvoiding> {
return modifier(KeyboardAvoiding())
}
}
@@ -0,0 +1,14 @@
import XCTest
@testable import KeyboardAvoiding

final class KeyboardAvoidingTests: XCTestCase {
func testExample() {
// This is an example of a functional test case.
// Use XCTAssert and related functions to verify your tests produce the correct
// results.
}

static var allTests = [
("testExample", testExample),
]
}
@@ -0,0 +1,9 @@
import XCTest

#if !canImport(ObjectiveC)
public func allTests() -> [XCTestCaseEntry] {
return [
testCase(KeyboardAvoidingTests.allTests),
]
}
#endif
@@ -0,0 +1,7 @@
import XCTest

import KeyboardAvoidingTests

var tests = [XCTestCaseEntry]()
tests += KeyboardAvoidingTests.allTests()
XCTMain(tests)

0 comments on commit 2867841

Please sign in to comment.
You can’t perform that action at this time.