├── .github
└── FUNDING.yml
├── .gitignore
├── .swiftpm
└── xcode
│ └── package.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ └── IDEWorkspaceChecks.plist
├── LICENSE
├── Package.swift
├── README.md
└── Sources
└── KeyboardToolbars
└── KeyboardToolbars.swift
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
4 | patreon: arnavmotwani
5 | open_collective: # Replace with a single Open Collective username
6 | ko_fi: # Replace with a single Ko-fi username
7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | liberapay: # Replace with a single Liberapay username
10 | issuehunt: # Replace with a single IssueHunt username
11 | otechie: # Replace with a single Otechie username
12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
13 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | /.build
3 | /Packages
4 | /*.xcodeproj
5 | xcuserdata/
6 |
--------------------------------------------------------------------------------
/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.swiftpm/xcode/package.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Arnav Motwani
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/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: "KeyboardToolbars",
8 | platforms: [.iOS(.v14)],
9 | products: [
10 | // Products define the executables and libraries a package produces, and make them visible to other packages.
11 | .library(
12 | name: "KeyboardToolbars",
13 | targets: ["KeyboardToolbars"]),
14 | ],
15 | dependencies: [
16 | // Dependencies declare other packages that this package depends on.
17 | // .package(url: /* package url */, from: "1.0.0"),
18 | ],
19 | targets: [
20 | // Targets are the basic building blocks of a package. A target can define a module or a test suite.
21 | // Targets can depend on other targets in this package, and on products in packages this package depends on.
22 | .target(
23 | name: "KeyboardToolbars",
24 | dependencies: []),
25 | ]
26 | )
27 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # KeyboardToolbars
2 |
3 | **A SwiftUI pacakge that makes adding floating toolbars to keyboards as simple as adding a single modifier.**
4 |
5 | ## Requirements:
6 | The package is only compatible with iOS 14 and iPadOS 14 for now.
7 |
8 | ## Installation:
9 | In Xcode go to `File -> Swift Packages -> Add Package Dependency` and paste in the repo's url: `https://github.com/ArnavMotwani/KeyboardToolbarsSwiftUI.git` then either select a version or the main branch (I will update the main branch more frequently with minor changes, while the version number will only increase with significant changes)
10 |
11 | ## Usage:
12 | Currently the package can only be used to add a hide keyboard button above a keyboard. All you need to do is `import KeyboardToolbars` and apply the `.addHideKeyboardButton()` to the root of a view.
13 |
14 | Note: This package is dependant on the safe area of SwiftUI. The `.ignoresSafeArea()` and `.edgesIgnoringSafeArea()` modifiers will cause the toolbar to hide behind the keyboard when these modifier as applied to the same view as `.addHideKeyboardButton()`.
15 |
16 | ## Example:
17 | ```swift
18 | import SwiftUI
19 | import KeyboardToolbars
20 |
21 | struct KeyboardToolbarsTest: View {
22 |
23 | @State private var text = ""
24 |
25 | var body: some View {
26 | Form {
27 | TextField("Text", text: $text)
28 | }
29 | .addHideKeyboardButton()
30 | }
31 | }
32 |
33 | struct KeyboardToolbarsTest_Previews: PreviewProvider {
34 | static var previews: some View {
35 | KeyboardToolbarsTest()
36 | }
37 | }
38 | ```
39 |
40 | ```swift
41 | import SwiftUI
42 | import KeyboardToolbars
43 |
44 | struct KeyboardToolbarsTest: View {
45 |
46 | @State private var text = ""
47 |
48 | var body: some View {
49 | ZStack {
50 | Color.gray.ignoresSafeArea()
51 | TextField("Text", text: $text)
52 | }
53 | .addHideKeyboardButton()
54 | }
55 | }
56 |
57 | struct KeyboardToolbarsTest_Previews: PreviewProvider {
58 | static var previews: some View {
59 | KeyboardToolbarsTest()
60 | }
61 | }
62 | ```
63 |
64 | ## Planned updates:
65 |
66 | - The ability to customize the toolbar
67 | - Ability to add custom buttons to the toolbar.
68 |
--------------------------------------------------------------------------------
/Sources/KeyboardToolbars/KeyboardToolbars.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 | import Combine
3 |
4 | //Extension to view to make applying modifier more natural
5 | extension View {
6 | public func addHideKeyboardButton() -> some View {
7 | self.modifier(HideKeyboardButtonOverlay())
8 | }
9 | }
10 |
11 | //Main modifier, detects when keyboard is open and then overlays the toolbar view
12 | struct HideKeyboardButtonOverlay: ViewModifier {
13 |
14 | #if os(iOS)
15 | @Environment(\.horizontalSizeClass) private var horizontalSizeClass
16 | #endif
17 |
18 | @ViewBuilder
19 | func body(content: Content) -> some View {
20 | if horizontalSizeClass == .compact {
21 | content
22 | .overlay(HideKeyboardButton(),alignment: .bottomTrailing)
23 | } else {
24 | content
25 | }
26 | }
27 | }
28 |
29 | //Main toolbar view
30 | struct HideKeyboardButton: View {
31 | var body: some View {
32 | Button(action: {
33 | hideKeyboard()
34 | }, label: {
35 | Image(systemName: "keyboard.chevron.compact.down")
36 | .font(Font.system(size: 25, weight: .regular))
37 | .padding(8)
38 | .background(Color.white.opacity(0.8).cornerRadius(8))
39 | .padding()
40 | })
41 | .modifier(AdaptOpacityWithKeyboard(hideOnShow: false))
42 | .animation(.default)
43 | }
44 | }
45 |
46 | //Modifier that changes opactity of toolbar when keyboard is opened.
47 | struct AdaptOpacityWithKeyboard: ViewModifier {
48 |
49 | let hideOnShow: Bool
50 | @State var opacity: Double = 0
51 |
52 | func body(content: Content) -> some View {
53 | content
54 | .opacity(opacity)
55 | .onAppear(perform: subscribeToKeyboardChanges)
56 | }
57 |
58 | private let keyboardHeightOnOpening = NotificationCenter.default
59 | .publisher(for: UIResponder.keyboardWillShowNotification)
60 | .map { _ in true }
61 |
62 |
63 | private let keyboardHeightOnHiding = NotificationCenter.default
64 | .publisher(for: UIResponder.keyboardWillHideNotification)
65 | .map { _ in false }
66 |
67 | private func subscribeToKeyboardChanges() {
68 |
69 | _ = Publishers.Merge(keyboardHeightOnOpening, keyboardHeightOnHiding)
70 | .subscribe(on: RunLoop.main)
71 | .sink { show in
72 | if show {
73 | self.opacity = hideOnShow ? 0:1
74 | } else {
75 | self.opacity = hideOnShow ? 1:0
76 | }
77 |
78 | }
79 | }
80 | }
81 |
82 | //Main function to hide keyboard
83 | #if canImport(UIKit)
84 | extension View {
85 | func hideKeyboard() {
86 | UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
87 | }
88 | }
89 | #endif
90 |
--------------------------------------------------------------------------------