├── .gitignore
├── .swiftpm
└── xcode
│ └── package.xcworkspace
│ └── xcshareddata
│ └── IDEWorkspaceChecks.plist
├── Package.swift
├── README.md
├── Sources
└── SwiftUI Keyboard Observer
│ ├── SoftwareKeyboard-EnvironmentKey.swift
│ ├── SoftwareKeyboard-Extension.swift
│ ├── SoftwareKeyboard.swift
│ ├── SoftwareKeyboardObserver.swift
│ └── SwiftUI_Keyboard_Observer.swift
├── Tests
└── SwiftUI Keyboard ObserverTests
│ └── SwiftUI_Keyboard_ObserverTests.swift
└── sample.gif
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | /.build
3 | /Packages
4 | /*.xcodeproj
5 | xcuserdata/
6 | DerivedData/
7 | .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
8 |
--------------------------------------------------------------------------------
/.swiftpm/xcode/package.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version:5.3
2 | // The swift-tools-version declares the minimum version of Swift required to build this package.
3 |
4 | import PackageDescription
5 |
6 | let package = Package(
7 | name: "SwiftUI Keyboard Observer",
8 | platforms: [
9 | .iOS(.v14)
10 | ],
11 | products: [
12 | // Products define the executables and libraries a package produces, and make them visible to other packages.
13 | .library(
14 | name: "SwiftUI Keyboard Observer",
15 | targets: ["SwiftUI Keyboard Observer"]),
16 | ],
17 | dependencies: [
18 | // Dependencies declare other packages that this package depends on.
19 | // .package(url: /* package url */, from: "1.0.0"),
20 | ],
21 | targets: [
22 | // Targets are the basic building blocks of a package. A target can define a module or a test suite.
23 | // Targets can depend on other targets in this package, and on products in packages this package depends on.
24 | .target(
25 | name: "SwiftUI Keyboard Observer",
26 | dependencies: []),
27 | .testTarget(
28 | name: "SwiftUI Keyboard ObserverTests",
29 | dependencies: ["SwiftUI Keyboard Observer"]),
30 | ]
31 | )
32 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # SwiftUI Keyboard Observer
2 |
3 | This package adds a view extension which adds an iOS software keyboard observer to the environment.
4 |
5 | More documentation and updates to come. This is a very early draft. I appreciate any feedback or contributions!
6 |
7 | ```swift
8 | import SwiftUI
9 | import SwiftUI_Keyboard_Observer
10 |
11 | @main
12 | struct MyApp: App {
13 | var body: some Scene {
14 | WindowGroup {
15 | ContentView()
16 | .observeSoftwareKeyboard()
17 | }
18 | }
19 | }
20 |
21 | struct ContentView: View {
22 | @State private var text: String = ""
23 | @Environment(\.softwareKeyboard) var softwareKeyboard
24 |
25 | var body: some View {
26 | VStack {
27 | Form {
28 | TextField("Text Field:", text: $text)
29 | }
30 |
31 | if softwareKeyboard?.isVisible == true {
32 | Button("Dismiss Keyboard") {
33 | softwareKeyboard?.dismiss()
34 | }
35 | .padding()
36 | }
37 | }
38 | }
39 | }
40 | ```
41 |
42 | 
43 |
--------------------------------------------------------------------------------
/Sources/SwiftUI Keyboard Observer/SoftwareKeyboard-EnvironmentKey.swift:
--------------------------------------------------------------------------------
1 | //
2 | // File.swift
3 | //
4 | //
5 | // Created by Chris Gaafary on 4/9/21.
6 | //
7 | import SwiftUI
8 |
9 | public struct KeyboardIsVisible: EnvironmentKey {
10 | public static let defaultValue: SoftwareKeyboard? = nil
11 | }
12 |
13 | public extension EnvironmentValues {
14 | var softwareKeyboard: SoftwareKeyboard? {
15 | get { self[KeyboardIsVisible.self] }
16 | set { self[KeyboardIsVisible.self] = newValue }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/Sources/SwiftUI Keyboard Observer/SoftwareKeyboard-Extension.swift:
--------------------------------------------------------------------------------
1 | //
2 | // File.swift
3 | //
4 | //
5 | // Created by Chris Gaafary on 4/9/21.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct SoftwareKeyboardContainer: View {
11 | @ViewBuilder var content: () -> Content
12 | @StateObject var softwareKeyboardObserver = SoftwareKeyboardObserver()
13 |
14 | var body: some View {
15 | content()
16 | .environment(\.softwareKeyboard, softwareKeyboardObserver.softwareKeyboard)
17 | }
18 | }
19 |
20 | public extension View {
21 | func observeSoftwareKeyboard() -> some View {
22 | SoftwareKeyboardContainer {
23 | self
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/Sources/SwiftUI Keyboard Observer/SoftwareKeyboard.swift:
--------------------------------------------------------------------------------
1 | //
2 | // File.swift
3 | //
4 | //
5 | // Created by Chris Gaafary on 4/9/21.
6 | //
7 |
8 | import SwiftUI
9 | import Combine
10 |
11 | public struct SoftwareKeyboard {
12 | public enum Status {
13 | case willShow, willHide, didShow, didHide
14 | }
15 |
16 | public let status: Status
17 |
18 | public var isVisible: Bool {
19 | switch status {
20 | case .willShow, .didShow:
21 | return true
22 | case .willHide, .didHide:
23 | return false
24 | }
25 | }
26 |
27 | public func dismiss() {
28 | UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
29 | }
30 | }
31 |
32 |
--------------------------------------------------------------------------------
/Sources/SwiftUI Keyboard Observer/SoftwareKeyboardObserver.swift:
--------------------------------------------------------------------------------
1 | //
2 | // File.swift
3 | //
4 | //
5 | // Created by Chris Gaafary on 4/9/21.
6 | //
7 |
8 | import SwiftUI
9 | import Combine
10 |
11 | public class SoftwareKeyboardObserver: ObservableObject {
12 | @Published private(set) var softwareKeyboard: SoftwareKeyboard?
13 |
14 | public init() {
15 | NotificationCenter.default
16 | .publisher(for: UIApplication.keyboardWillShowNotification)
17 | .map { _ in SoftwareKeyboard(status: .willShow) }
18 | .assign(to: &$softwareKeyboard)
19 |
20 | NotificationCenter.default
21 | .publisher(for: UIApplication.keyboardDidShowNotification)
22 | .map { _ in SoftwareKeyboard(status: .didShow) }
23 | .assign(to: &$softwareKeyboard)
24 |
25 | NotificationCenter.default
26 | .publisher(for: UIApplication.keyboardWillHideNotification)
27 | .map { _ in SoftwareKeyboard(status: .willHide) }
28 | .assign(to: &$softwareKeyboard)
29 |
30 | NotificationCenter.default
31 | .publisher(for: UIApplication.keyboardDidHideNotification)
32 | .map { _ in SoftwareKeyboard(status: .didHide) }
33 | .assign(to: &$softwareKeyboard)
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/Sources/SwiftUI Keyboard Observer/SwiftUI_Keyboard_Observer.swift:
--------------------------------------------------------------------------------
1 | struct SwiftUI_Keyboard_Observer {
2 | var text = "Hello, World!"
3 | }
4 |
--------------------------------------------------------------------------------
/Tests/SwiftUI Keyboard ObserverTests/SwiftUI_Keyboard_ObserverTests.swift:
--------------------------------------------------------------------------------
1 | import XCTest
2 | @testable import SwiftUI_Keyboard_Observer
3 |
4 | final class SwiftUI_Keyboard_ObserverTests: XCTestCase {
5 | func testExample() {
6 | // This is an example of a functional test case.
7 | // Use XCTAssert and related functions to verify your tests produce the correct
8 | // results.
9 | XCTAssertEqual(SwiftUI_Keyboard_Observer().text, "Hello, World!")
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/sample.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cgaaf/SwiftUI-Keyboard-Observer/bd45c696069a1ac770fcc2bc63c29cfafaa90233/sample.gif
--------------------------------------------------------------------------------