├── .github ├── ISSUE_TEMPLATE │ ├── ---bug-report.md │ ├── --feature-request.md │ └── --question-support.md ├── issue_template.md ├── pull_request_template.md └── workflows │ ├── ci_master_framework.yml │ ├── ci_pr_example.yml │ └── ci_pr_framework.yml ├── .gitignore ├── CHANGELOG.md ├── Example ├── Example.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── Resources │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ ├── Contents.json │ │ ├── NT Logo Blue.imageset │ │ │ ├── Contents.json │ │ │ └── NT Logo Blue.png │ │ ├── anonymous.imageset │ │ │ ├── Contents.json │ │ │ └── icons8-anonymous_mask.png │ │ ├── avatar.imageset │ │ │ ├── Contents.json │ │ │ └── icons8-avatar.png │ │ ├── ic_at.imageset │ │ │ ├── Contents.json │ │ │ └── icons8-email_filled.png │ │ ├── ic_bold.imageset │ │ │ ├── Contents.json │ │ │ └── icons8-bold.png │ │ ├── ic_camera.imageset │ │ │ ├── Contents.json │ │ │ └── icons8-compact_camera_filled.png │ │ ├── ic_code.imageset │ │ │ ├── Contents.json │ │ │ └── icons8-source_code.png │ │ ├── ic_eye.imageset │ │ │ ├── Contents.json │ │ │ └── icons8-visible.png │ │ ├── ic_hashtag.imageset │ │ │ ├── Contents.json │ │ │ └── icons8-hashtag_filled.png │ │ ├── ic_italic.imageset │ │ │ ├── Contents.json │ │ │ └── icons8-italic.png │ │ ├── ic_keyboard.imageset │ │ │ ├── Contents.json │ │ │ └── icons8-keyboard.png │ │ ├── ic_library.imageset │ │ │ ├── Contents.json │ │ │ └── icons8-stack_of_photos_filled.png │ │ ├── ic_link.imageset │ │ │ ├── Contents.json │ │ │ └── icons8-link.png │ │ ├── ic_list.imageset │ │ │ ├── Contents.json │ │ │ └── icons8-list.png │ │ ├── ic_plus.imageset │ │ │ ├── Contents.json │ │ │ └── icons8-plus_math.png │ │ ├── ic_send.imageset │ │ │ ├── Contents.json │ │ │ └── icons8-sent_filled.png │ │ ├── ic_up.imageset │ │ │ ├── Contents.json │ │ │ └── icons8-up_arrow.png │ │ ├── ic_upload.imageset │ │ │ ├── Contents.json │ │ │ └── icons8-upload.png │ │ ├── ic_user.imageset │ │ │ ├── Contents.json │ │ │ └── icons8-user_filled.png │ │ ├── icons8-collapse.imageset │ │ │ ├── Contents.json │ │ │ └── icons8-collapse.png │ │ ├── icons8-expand.imageset │ │ │ ├── Contents.json │ │ │ └── icons8-expand.png │ │ ├── nathan.imageset │ │ │ ├── 15272998 copy.jpeg │ │ │ └── Contents.json │ │ ├── ninja.imageset │ │ │ ├── Contents.json │ │ │ └── icons8-ninja_head-1.png │ │ └── rick.imageset │ │ │ ├── Contents.json │ │ │ └── icons8-rick_sanchez.png │ └── Info.plist └── Sources │ ├── AppDelegate.swift │ ├── Cells │ ├── ConversationCell.swift │ ├── ImageCell.swift │ └── InboxCell.swift │ ├── Community Examples │ └── ButtonAnimationExample.swift │ ├── Example ViewControllers │ ├── AdditionalBottomSpaceExampleViewController.swift │ ├── CommonTableViewController.swift │ ├── InputAccessoryExampleViewController.swift │ ├── READMEPreviewViewController.swift │ ├── SubviewExampleViewController.swift │ └── SwiftUIExample.swift │ ├── Example.entitlements │ ├── InputBar Examples │ ├── FacebookInputBar.swift │ ├── GitHawkInputBar.swift │ ├── InputBarStyle.swift │ ├── NoTextViewInputBar.swift │ ├── SlackInputBar.swift │ └── iMessageInputBar.swift │ ├── InputBarStyleSelectionController.swift │ ├── LaunchScreen.storyboard │ └── Sample Data │ ├── Lorem.swift │ ├── Random.swift │ └── SampleData.swift ├── GETTING_STARTED.md ├── GitHubActions └── build.sh ├── LICENSE ├── Package.swift ├── README.md ├── Screenshots ├── .DS_Store ├── Layout.png ├── Preview.gif ├── ScreenshotA.png ├── ScreenshotB.png ├── ScreenshotC.png ├── ScreenshotD.png ├── ScreenshotE.png └── ScreenshotF.png ├── Sources ├── Controls │ ├── InputBarButtonItem.swift │ └── InputBarSendButton.swift ├── Extensions │ ├── NSMutableAttributedString+Extensions.swift │ ├── NSNotification+Extensions.swift │ ├── String+Extensions.swift │ ├── UITextView+Extensions.swift │ └── UIView+AutoLayout.swift ├── InputBarAccessoryView.swift ├── KeyboardManager │ ├── KeyboardEvent.swift │ ├── KeyboardManager.swift │ └── KeyboardNotification.swift ├── Models │ ├── HorizontalEdgePadding.swift │ └── NSConstraintLayoutSet.swift ├── Plugins │ ├── AttachmentManager │ │ ├── AttachmentManager.swift │ │ ├── Protocols │ │ │ ├── AttachmentManagerDataSource.swift │ │ │ └── AttachmentManagerDelegate.swift │ │ └── Views │ │ │ ├── AttachmentCell.swift │ │ │ ├── AttachmentsView.swift │ │ │ └── ImageAttachmentCell.swift │ └── AutocompleteManager │ │ ├── AutocompleteManager.swift │ │ ├── Models │ │ ├── AutocompleteCompletion.swift │ │ └── AutocompleteSession.swift │ │ ├── Protocols │ │ ├── AutocompleteManagerDataSource.swift │ │ └── AutocompleteManagerDelegate.swift │ │ └── Views │ │ ├── AutocompleteCell.swift │ │ └── AutocompleteTableView.swift ├── Protocols │ ├── InputBarAccessoryViewDelegate.swift │ ├── InputItem.swift │ └── InputPlugin.swift ├── Supporting │ ├── Info.plist │ ├── InputBarAccessoryView+Availability.swift │ └── InputBarAccessoryView-Bridging-Header.h ├── ViewControllers │ └── InputBarViewController.swift └── Views │ ├── InputStackView.swift │ ├── InputTextView.swift │ └── SeparatorLine.swift └── docs ├── Classes.html ├── Classes ├── AttachmentCell.html ├── AttachmentCollectionView.html ├── AttachmentManager.html ├── AttachmentManager │ └── Attachment.html ├── AttachmentsView.html ├── AutocompleteCell.html ├── AutocompleteManager.html ├── AutocompleteSession.html ├── AutocompleteTableView.html ├── ImageAttachmentCell.html ├── InputBarAccessoryView.html ├── InputBarAccessoryView │ ├── UIStackViewPosition.html │ └── setStackViewItems(_:forStack:animated:).html ├── InputBarButtonItem.html ├── InputBarButtonItem │ └── Spacing.html ├── InputBarSendButton.html ├── InputBarViewController.html ├── InputStackView.html ├── InputStackView │ └── Position.html ├── InputTextView.html ├── KeyboardManager.html ├── NSLayoutConstraintSet.html ├── SeparatorLine.html └── TypingIndicator.html ├── Enums.html ├── Enums └── KeyboardEvent.html ├── Extensions.html ├── Extensions ├── NSAttributedString.html ├── NSAttributedString │ └── Key.html ├── NSMutableAttributedString.html ├── UITextView.html └── UIView.html ├── Protocols.html ├── Protocols ├── AttachmentManagerDataSource.html ├── AttachmentManagerDelegate.html ├── AutocompleteManagerDataSource.html ├── AutocompleteManagerDelegate.html ├── InputBarAccessoryViewDelegate.html ├── InputItem.html ├── InputManager.html ├── InputPlugin.html └── TypingIndicatorDelegate.html ├── Structs.html ├── Structs ├── AutocompleteCompletion.html ├── AutocompleteSession.html ├── HorizontalEdgePadding.html └── KeyboardNotification.html ├── Typealiases.html ├── _config.yml ├── badge.svg ├── css ├── highlight.css └── jazzy.css ├── docsets ├── .docset │ └── Contents │ │ ├── Info.plist │ │ └── Resources │ │ ├── Documents │ │ ├── Classes.html │ │ ├── Classes │ │ │ ├── AttachmentCell.html │ │ │ ├── AttachmentCollectionView.html │ │ │ ├── AttachmentManager.html │ │ │ ├── AttachmentManager │ │ │ │ └── Attachment.html │ │ │ ├── AttachmentsView.html │ │ │ ├── AutocompleteCell.html │ │ │ ├── AutocompleteManager.html │ │ │ ├── AutocompleteSession.html │ │ │ ├── AutocompleteTableView.html │ │ │ ├── ImageAttachmentCell.html │ │ │ ├── InputBarAccessoryView.html │ │ │ ├── InputBarAccessoryView │ │ │ │ ├── UIStackViewPosition.html │ │ │ │ └── setStackViewItems(_:forStack:animated:).html │ │ │ ├── InputBarButtonItem.html │ │ │ ├── InputBarButtonItem │ │ │ │ └── Spacing.html │ │ │ ├── InputBarSendButton.html │ │ │ ├── InputBarViewController.html │ │ │ ├── InputStackView.html │ │ │ ├── InputStackView │ │ │ │ └── Position.html │ │ │ ├── InputTextView.html │ │ │ ├── KeyboardManager.html │ │ │ ├── NSLayoutConstraintSet.html │ │ │ ├── SeparatorLine.html │ │ │ └── TypingIndicator.html │ │ ├── Enums.html │ │ ├── Enums │ │ │ └── KeyboardEvent.html │ │ ├── Extensions.html │ │ ├── Extensions │ │ │ ├── NSAttributedString.html │ │ │ ├── NSAttributedString │ │ │ │ └── Key.html │ │ │ ├── NSMutableAttributedString.html │ │ │ ├── UITextView.html │ │ │ └── UIView.html │ │ ├── Protocols.html │ │ ├── Protocols │ │ │ ├── AttachmentManagerDataSource.html │ │ │ ├── AttachmentManagerDelegate.html │ │ │ ├── AutocompleteManagerDataSource.html │ │ │ ├── AutocompleteManagerDelegate.html │ │ │ ├── InputBarAccessoryViewDelegate.html │ │ │ ├── InputItem.html │ │ │ ├── InputManager.html │ │ │ ├── InputPlugin.html │ │ │ └── TypingIndicatorDelegate.html │ │ ├── Structs.html │ │ ├── Structs │ │ │ ├── AutocompleteCompletion.html │ │ │ ├── AutocompleteSession.html │ │ │ ├── HorizontalEdgePadding.html │ │ │ └── KeyboardNotification.html │ │ ├── Typealiases.html │ │ ├── _config.yml │ │ ├── badge.svg │ │ ├── css │ │ │ ├── highlight.css │ │ │ └── jazzy.css │ │ ├── img │ │ │ ├── carat.png │ │ │ ├── dash.png │ │ │ ├── gh.png │ │ │ └── spinner.gif │ │ ├── index.html │ │ ├── js │ │ │ ├── jazzy.js │ │ │ ├── jazzy.search.js │ │ │ ├── jquery.min.js │ │ │ ├── lunr.min.js │ │ │ └── typeahead.jquery.js │ │ ├── search.json │ │ └── undocumented.json │ │ └── docSet.dsidx ├── .tgz ├── InputBarAccessoryView.docset │ └── Contents │ │ ├── Info.plist │ │ └── Resources │ │ ├── Documents │ │ ├── Classes.html │ │ ├── Classes │ │ │ ├── AttachmentCell.html │ │ │ ├── AttachmentCollectionView.html │ │ │ ├── AttachmentManager.html │ │ │ ├── AttachmentManager │ │ │ │ └── Attachment.html │ │ │ ├── AttachmentsView.html │ │ │ ├── AutocompleteCell.html │ │ │ ├── AutocompleteManager.html │ │ │ ├── AutocompleteSession.html │ │ │ ├── AutocompleteTableView.html │ │ │ ├── ImageAttachmentCell.html │ │ │ ├── InputBarAccessoryView.html │ │ │ ├── InputBarAccessoryView │ │ │ │ ├── UIStackViewPosition.html │ │ │ │ └── setStackViewItems(_:forStack:animated:).html │ │ │ ├── InputBarButtonItem.html │ │ │ ├── InputBarButtonItem │ │ │ │ └── Spacing.html │ │ │ ├── InputBarSendButton.html │ │ │ ├── InputBarViewController.html │ │ │ ├── InputStackView.html │ │ │ ├── InputStackView │ │ │ │ └── Position.html │ │ │ ├── InputTextView.html │ │ │ ├── KeyboardManager.html │ │ │ ├── NSLayoutConstraintSet.html │ │ │ ├── SeparatorLine.html │ │ │ └── TypingIndicator.html │ │ ├── Enums.html │ │ ├── Enums │ │ │ └── KeyboardEvent.html │ │ ├── Extensions.html │ │ ├── Extensions │ │ │ ├── NSAttributedString.html │ │ │ ├── NSAttributedString │ │ │ │ └── Key.html │ │ │ ├── NSMutableAttributedString.html │ │ │ └── UIView.html │ │ ├── Protocols.html │ │ ├── Protocols │ │ │ ├── AttachmentManagerDataSource.html │ │ │ ├── AttachmentManagerDelegate.html │ │ │ ├── AutocompleteManagerDataSource.html │ │ │ ├── AutocompleteManagerDelegate.html │ │ │ ├── InputBarAccessoryViewDelegate.html │ │ │ ├── InputItem.html │ │ │ ├── InputManager.html │ │ │ ├── InputPlugin.html │ │ │ └── TypingIndicatorDelegate.html │ │ ├── Structs.html │ │ ├── Structs │ │ │ ├── AutocompleteCompletion.html │ │ │ ├── AutocompleteSession.html │ │ │ ├── HorizontalEdgePadding.html │ │ │ └── KeyboardNotification.html │ │ ├── Typealiases.html │ │ ├── _config.yml │ │ ├── badge.svg │ │ ├── css │ │ │ ├── highlight.css │ │ │ └── jazzy.css │ │ ├── img │ │ │ ├── carat.png │ │ │ ├── dash.png │ │ │ └── gh.png │ │ ├── index.html │ │ ├── js │ │ │ ├── jazzy.js │ │ │ └── jquery.min.js │ │ ├── search.json │ │ └── undocumented.json │ │ └── docSet.dsidx └── InputBarAccessoryView.tgz ├── img ├── carat.png ├── dash.png ├── gh.png └── spinner.gif ├── index.html ├── js ├── jazzy.js ├── jazzy.search.js ├── jquery.min.js ├── lunr.min.js └── typeahead.jquery.js ├── search.json └── undocumented.json /.github/ISSUE_TEMPLATE/---bug-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001F41B Bug report" 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug? 6 | assignees: '' 7 | 8 | --- 9 | 10 | 15 | 16 | **Describe the bug** 17 | A clear and concise description of what the bug is. 18 | 19 | **To Reproduce** 20 | Steps/code to reproduce the behavior: 21 | 33 | 34 | **Expected behavior** 35 | A clear and concise description of what you expected to happen. 36 | 37 | **Screenshots** 38 | If applicable, add screenshots to help explain your problem. 39 | 40 | **Environment** 41 | - What version of InputBarAccessoryView are you using? 42 | - What version of iOS are you running on? 43 | - What version of Swift are you running on? 44 | - What device(s) are you testing on? Are these simulators? 45 | - Is the issue you're experiencing reproducable in the example app? 46 | 47 | **Additional context** 48 | Add any other context about the problem here. 49 | 50 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/--feature-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001F4A1Feature request" 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: feature request 6 | assignees: '' 7 | 8 | --- 9 | 10 | 11 | 12 | **Is your feature request related to a problem? Please describe.** 13 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 14 | 15 | **Describe the solution you'd like** 16 | A clear and concise description of what you want to happen. 17 | 18 | **Describe alternatives you've considered** 19 | A clear and concise description of any alternative solutions or features you've considered. 20 | 21 | **Additional context** 22 | Add any other context or screenshots about the feature request here. 23 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/--question-support.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "❓ Question/Support" 3 | about: Ask a question about how to use InputBarAccessoryView 4 | title: '' 5 | labels: question 6 | assignees: '' 7 | 8 | --- 9 | 10 | 34 | -------------------------------------------------------------------------------- /.github/issue_template.md: -------------------------------------------------------------------------------- 1 | 30 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | 4 | 5 | What does this implement/fix? Explain your changes. 6 | --------------------------------------------------- 7 | … 8 | 9 | Does this close any currently open issues? 10 | ------------------------------------------ 11 | … 12 | 13 | 14 | Any relevant logs, error output, etc? 15 | ------------------------------------- 16 | 19 | 20 | Any other comments? 21 | ------------------- 22 | … 23 | 24 | Where has this been tested? 25 | --------------------------- 26 | **Devices/Simulators:** … 27 | 28 | **iOS Version:** … 29 | 30 | **Swift Version:** … 31 | 32 | **InputBarAccessoryView Version:** … 33 | 34 | 35 | -------------------------------------------------------------------------------- /.github/workflows/ci_master_framework.yml: -------------------------------------------------------------------------------- 1 | name: Build Framework master 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | tests: 10 | name: Build Framework 11 | runs-on: macOS-15 12 | steps: 13 | - name: Checkout the Git repository 14 | uses: actions/checkout@v4 15 | - name: Build framework 16 | run: ./GitHubActions/build.sh framework 17 | -------------------------------------------------------------------------------- /.github/workflows/ci_pr_example.yml: -------------------------------------------------------------------------------- 1 | name: Build Example app 2 | 3 | on: pull_request 4 | 5 | jobs: 6 | tests: 7 | name: Build Example app 8 | runs-on: macOS-15 9 | steps: 10 | - name: Checkout the Git repository 11 | uses: actions/checkout@v4 12 | - name: Build and run example project 13 | run: ./GitHubActions/build.sh example 14 | -------------------------------------------------------------------------------- /.github/workflows/ci_pr_framework.yml: -------------------------------------------------------------------------------- 1 | name: Build Framework 2 | 3 | on: pull_request 4 | 5 | jobs: 6 | tests: 7 | name: Build Framework 8 | runs-on: macOS-15 9 | steps: 10 | - name: Checkout the Git repository 11 | uses: actions/checkout@v4 12 | - name: Build framework 13 | run: ./GitHubActions/build.sh framework 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OS X 2 | .DS_Store 3 | 4 | # Xcode 5 | 6 | ## Build generated 7 | build/ 8 | .build 9 | .swiftpm 10 | DerivedData 11 | 12 | ## Various settings 13 | *.pbxuser 14 | !default.pbxuser 15 | *.mode1v3 16 | !default.mode1v3 17 | *.mode2v3 18 | !default.mode2v3 19 | *.perspectivev3 20 | !default.perspectivev3 21 | xcuserdata 22 | 23 | ## Other 24 | *.xccheckout 25 | *.moved-aside 26 | *.xcuserstate 27 | *.xcscmblueprint 28 | 29 | ## Obj-C/Swift specific 30 | *.hmap 31 | *.ipa 32 | 33 | # CocoaPods 34 | Pods 35 | 36 | # Carthage 37 | Carthage 38 | -------------------------------------------------------------------------------- /Example/Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Example/Example.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Example/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /Example/Resources/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Example/Resources/Assets.xcassets/NT Logo Blue.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "NT Logo Blue.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/Resources/Assets.xcassets/NT Logo Blue.imageset/NT Logo Blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nathantannar4/InputBarAccessoryView/dfdc822b0d4a0d1b5777100347c399069e9bfc62/Example/Resources/Assets.xcassets/NT Logo Blue.imageset/NT Logo Blue.png -------------------------------------------------------------------------------- /Example/Resources/Assets.xcassets/anonymous.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "icons8-anonymous_mask.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/Resources/Assets.xcassets/anonymous.imageset/icons8-anonymous_mask.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nathantannar4/InputBarAccessoryView/dfdc822b0d4a0d1b5777100347c399069e9bfc62/Example/Resources/Assets.xcassets/anonymous.imageset/icons8-anonymous_mask.png -------------------------------------------------------------------------------- /Example/Resources/Assets.xcassets/avatar.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "icons8-avatar.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/Resources/Assets.xcassets/avatar.imageset/icons8-avatar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nathantannar4/InputBarAccessoryView/dfdc822b0d4a0d1b5777100347c399069e9bfc62/Example/Resources/Assets.xcassets/avatar.imageset/icons8-avatar.png -------------------------------------------------------------------------------- /Example/Resources/Assets.xcassets/ic_at.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "icons8-email_filled.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/Resources/Assets.xcassets/ic_at.imageset/icons8-email_filled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nathantannar4/InputBarAccessoryView/dfdc822b0d4a0d1b5777100347c399069e9bfc62/Example/Resources/Assets.xcassets/ic_at.imageset/icons8-email_filled.png -------------------------------------------------------------------------------- /Example/Resources/Assets.xcassets/ic_bold.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "icons8-bold.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/Resources/Assets.xcassets/ic_bold.imageset/icons8-bold.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nathantannar4/InputBarAccessoryView/dfdc822b0d4a0d1b5777100347c399069e9bfc62/Example/Resources/Assets.xcassets/ic_bold.imageset/icons8-bold.png -------------------------------------------------------------------------------- /Example/Resources/Assets.xcassets/ic_camera.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "icons8-compact_camera_filled.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/Resources/Assets.xcassets/ic_camera.imageset/icons8-compact_camera_filled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nathantannar4/InputBarAccessoryView/dfdc822b0d4a0d1b5777100347c399069e9bfc62/Example/Resources/Assets.xcassets/ic_camera.imageset/icons8-compact_camera_filled.png -------------------------------------------------------------------------------- /Example/Resources/Assets.xcassets/ic_code.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "icons8-source_code.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/Resources/Assets.xcassets/ic_code.imageset/icons8-source_code.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nathantannar4/InputBarAccessoryView/dfdc822b0d4a0d1b5777100347c399069e9bfc62/Example/Resources/Assets.xcassets/ic_code.imageset/icons8-source_code.png -------------------------------------------------------------------------------- /Example/Resources/Assets.xcassets/ic_eye.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "icons8-visible.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/Resources/Assets.xcassets/ic_eye.imageset/icons8-visible.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nathantannar4/InputBarAccessoryView/dfdc822b0d4a0d1b5777100347c399069e9bfc62/Example/Resources/Assets.xcassets/ic_eye.imageset/icons8-visible.png -------------------------------------------------------------------------------- /Example/Resources/Assets.xcassets/ic_hashtag.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "icons8-hashtag_filled.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/Resources/Assets.xcassets/ic_hashtag.imageset/icons8-hashtag_filled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nathantannar4/InputBarAccessoryView/dfdc822b0d4a0d1b5777100347c399069e9bfc62/Example/Resources/Assets.xcassets/ic_hashtag.imageset/icons8-hashtag_filled.png -------------------------------------------------------------------------------- /Example/Resources/Assets.xcassets/ic_italic.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "icons8-italic.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/Resources/Assets.xcassets/ic_italic.imageset/icons8-italic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nathantannar4/InputBarAccessoryView/dfdc822b0d4a0d1b5777100347c399069e9bfc62/Example/Resources/Assets.xcassets/ic_italic.imageset/icons8-italic.png -------------------------------------------------------------------------------- /Example/Resources/Assets.xcassets/ic_keyboard.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "icons8-keyboard.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/Resources/Assets.xcassets/ic_keyboard.imageset/icons8-keyboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nathantannar4/InputBarAccessoryView/dfdc822b0d4a0d1b5777100347c399069e9bfc62/Example/Resources/Assets.xcassets/ic_keyboard.imageset/icons8-keyboard.png -------------------------------------------------------------------------------- /Example/Resources/Assets.xcassets/ic_library.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "icons8-stack_of_photos_filled.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/Resources/Assets.xcassets/ic_library.imageset/icons8-stack_of_photos_filled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nathantannar4/InputBarAccessoryView/dfdc822b0d4a0d1b5777100347c399069e9bfc62/Example/Resources/Assets.xcassets/ic_library.imageset/icons8-stack_of_photos_filled.png -------------------------------------------------------------------------------- /Example/Resources/Assets.xcassets/ic_link.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "icons8-link.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/Resources/Assets.xcassets/ic_link.imageset/icons8-link.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nathantannar4/InputBarAccessoryView/dfdc822b0d4a0d1b5777100347c399069e9bfc62/Example/Resources/Assets.xcassets/ic_link.imageset/icons8-link.png -------------------------------------------------------------------------------- /Example/Resources/Assets.xcassets/ic_list.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "icons8-list.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/Resources/Assets.xcassets/ic_list.imageset/icons8-list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nathantannar4/InputBarAccessoryView/dfdc822b0d4a0d1b5777100347c399069e9bfc62/Example/Resources/Assets.xcassets/ic_list.imageset/icons8-list.png -------------------------------------------------------------------------------- /Example/Resources/Assets.xcassets/ic_plus.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "icons8-plus_math.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/Resources/Assets.xcassets/ic_plus.imageset/icons8-plus_math.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nathantannar4/InputBarAccessoryView/dfdc822b0d4a0d1b5777100347c399069e9bfc62/Example/Resources/Assets.xcassets/ic_plus.imageset/icons8-plus_math.png -------------------------------------------------------------------------------- /Example/Resources/Assets.xcassets/ic_send.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "icons8-sent_filled.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/Resources/Assets.xcassets/ic_send.imageset/icons8-sent_filled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nathantannar4/InputBarAccessoryView/dfdc822b0d4a0d1b5777100347c399069e9bfc62/Example/Resources/Assets.xcassets/ic_send.imageset/icons8-sent_filled.png -------------------------------------------------------------------------------- /Example/Resources/Assets.xcassets/ic_up.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "icons8-up_arrow.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/Resources/Assets.xcassets/ic_up.imageset/icons8-up_arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nathantannar4/InputBarAccessoryView/dfdc822b0d4a0d1b5777100347c399069e9bfc62/Example/Resources/Assets.xcassets/ic_up.imageset/icons8-up_arrow.png -------------------------------------------------------------------------------- /Example/Resources/Assets.xcassets/ic_upload.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "icons8-upload.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/Resources/Assets.xcassets/ic_upload.imageset/icons8-upload.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nathantannar4/InputBarAccessoryView/dfdc822b0d4a0d1b5777100347c399069e9bfc62/Example/Resources/Assets.xcassets/ic_upload.imageset/icons8-upload.png -------------------------------------------------------------------------------- /Example/Resources/Assets.xcassets/ic_user.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "icons8-user_filled.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/Resources/Assets.xcassets/ic_user.imageset/icons8-user_filled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nathantannar4/InputBarAccessoryView/dfdc822b0d4a0d1b5777100347c399069e9bfc62/Example/Resources/Assets.xcassets/ic_user.imageset/icons8-user_filled.png -------------------------------------------------------------------------------- /Example/Resources/Assets.xcassets/icons8-collapse.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "icons8-collapse.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/Resources/Assets.xcassets/icons8-collapse.imageset/icons8-collapse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nathantannar4/InputBarAccessoryView/dfdc822b0d4a0d1b5777100347c399069e9bfc62/Example/Resources/Assets.xcassets/icons8-collapse.imageset/icons8-collapse.png -------------------------------------------------------------------------------- /Example/Resources/Assets.xcassets/icons8-expand.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "icons8-expand.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/Resources/Assets.xcassets/icons8-expand.imageset/icons8-expand.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nathantannar4/InputBarAccessoryView/dfdc822b0d4a0d1b5777100347c399069e9bfc62/Example/Resources/Assets.xcassets/icons8-expand.imageset/icons8-expand.png -------------------------------------------------------------------------------- /Example/Resources/Assets.xcassets/nathan.imageset/15272998 copy.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nathantannar4/InputBarAccessoryView/dfdc822b0d4a0d1b5777100347c399069e9bfc62/Example/Resources/Assets.xcassets/nathan.imageset/15272998 copy.jpeg -------------------------------------------------------------------------------- /Example/Resources/Assets.xcassets/nathan.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "15272998 copy.jpeg", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/Resources/Assets.xcassets/ninja.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "icons8-ninja_head-1.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/Resources/Assets.xcassets/ninja.imageset/icons8-ninja_head-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nathantannar4/InputBarAccessoryView/dfdc822b0d4a0d1b5777100347c399069e9bfc62/Example/Resources/Assets.xcassets/ninja.imageset/icons8-ninja_head-1.png -------------------------------------------------------------------------------- /Example/Resources/Assets.xcassets/rick.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "icons8-rick_sanchez.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/Resources/Assets.xcassets/rick.imageset/icons8-rick_sanchez.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nathantannar4/InputBarAccessoryView/dfdc822b0d4a0d1b5777100347c399069e9bfc62/Example/Resources/Assets.xcassets/rick.imageset/icons8-rick_sanchez.png -------------------------------------------------------------------------------- /Example/Resources/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | InputBarAccessoryView 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | FMWK 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UIStatusBarStyle 32 | UIStatusBarStyleLightContent 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | UISupportedInterfaceOrientations~ipad 40 | 41 | UIInterfaceOrientationPortrait 42 | UIInterfaceOrientationPortraitUpsideDown 43 | UIInterfaceOrientationLandscapeLeft 44 | UIInterfaceOrientationLandscapeRight 45 | 46 | UIViewControllerBasedStatusBarAppearance 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /Example/Sources/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // Example 4 | // 5 | // Created by Nathan Tannar on 8/18/17. 6 | // Copyright © 2017-2020 Nathan Tannar. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import InputBarAccessoryView 11 | 12 | @UIApplicationMain 13 | class AppDelegate: UIResponder, UIApplicationDelegate { 14 | 15 | var window: UIWindow? 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 18 | 19 | window = UIWindow(frame: UIScreen.main.bounds) 20 | window?.rootViewController = UINavigationController(rootViewController: InputBarStyleSelectionController()) 21 | window?.makeKeyAndVisible() 22 | 23 | return true 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Example/Sources/Cells/ConversationCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ConversationCell.swift 3 | // Example 4 | // 5 | // Created by Nathan Tannar on 2018-06-06. 6 | // Copyright © 2018 Nathan Tannar. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ConversationCell: UITableViewCell { 12 | override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { 13 | super.init(style: .subtitle, reuseIdentifier: reuseIdentifier) 14 | } 15 | 16 | required init?(coder aDecoder: NSCoder) { 17 | fatalError("init(coder:) has not been implemented") 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Example/Sources/Cells/ImageCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImageCell.swift 3 | // Example 4 | // 5 | // Copyright © 2017-2020 Nathan Tannar. 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // 25 | // Created by Nathan Tannar on 1/29/18. 26 | // 27 | 28 | import UIKit 29 | 30 | class ImageCell: UICollectionViewCell { 31 | 32 | class var reuseIdentifier: String { 33 | return "ImageCell" 34 | } 35 | 36 | let imageView = UIImageView() 37 | 38 | override init(frame: CGRect) { 39 | super.init(frame: frame) 40 | setupView() 41 | } 42 | 43 | required init?(coder aDecoder: NSCoder) { 44 | fatalError("init(coder:) has not been implemented") 45 | } 46 | 47 | func setupView() { 48 | addSubview(imageView) 49 | imageView.contentMode = .scaleAspectFit 50 | } 51 | 52 | override func layoutSubviews() { 53 | super.layoutSubviews() 54 | imageView.frame = bounds 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Example/Sources/Cells/InboxCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // InboxCell.swift 3 | // Example 4 | // 5 | // Created by Nathan Tannar on 2018-06-06. 6 | // Copyright © 2018 Nathan Tannar. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class InboxCell: UITableViewCell { 12 | override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { 13 | super.init(style: .subtitle, reuseIdentifier: reuseIdentifier) 14 | } 15 | 16 | required init?(coder aDecoder: NSCoder) { 17 | fatalError("init(coder:) has not been implemented") 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Example/Sources/Community Examples/ButtonAnimationExample.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ButtonAnimationExample.swift 3 | // Example 4 | // 5 | // Created by Andrew Breckenridge on 9/24/20. 6 | // Copyright © 2020 Nathan Tannar. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import InputBarAccessoryView 11 | 12 | class ButtonAnimationExample: InputAccessoryExampleViewController { 13 | 14 | var isShowingSendButton: Bool = true { 15 | didSet { 16 | if oldValue != isShowingSendButton { 17 | inputBar.sendButton.frame.origin.y = inputBar.rightStackView.frame.height / 2 18 | self.inputBar.sendButton.setSize(isShowingSendButton ? CGSize(width: 38, height: 38) : .zero, animated: true) 19 | } 20 | } 21 | } 22 | 23 | override func viewDidLoad() { 24 | super.viewDidLoad() 25 | 26 | self.inputBar.shouldManageSendButtonEnabledState = false 27 | 28 | isShowingSendButton = false 29 | } 30 | 31 | override func inputBar(_ inputBar: InputBarAccessoryView, textViewTextDidChangeTo text: String) { 32 | self.isShowingSendButton = !text.isEmpty 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Example/Sources/Example ViewControllers/AdditionalBottomSpaceExampleViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AdditionalBottomSpaceExampleViewController.swift 3 | // Example 4 | // 5 | // Created by Martin Púčik on 16.05.2022. 6 | // Copyright © 2022 Nathan Tannar. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | import InputBarAccessoryView 12 | 13 | final class AdditionalBottomSpaceExampleViewController: CommonTableViewController { 14 | 15 | private lazy var keyboardManager = KeyboardManager() 16 | 17 | private lazy var additionalBottomBar: UIView = { 18 | let view = UIView() 19 | view.backgroundColor = .systemBlue 20 | view.translatesAutoresizingMaskIntoConstraints = false 21 | return view 22 | }() 23 | 24 | override func viewDidLoad() { 25 | super.viewDidLoad() 26 | 27 | view.addSubview(inputBar) 28 | view.addSubview(additionalBottomBar) 29 | 30 | NSLayoutConstraint.activate([ 31 | additionalBottomBar.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor), 32 | additionalBottomBar.leadingAnchor.constraint(equalTo: view.leadingAnchor), 33 | additionalBottomBar.trailingAnchor.constraint(equalTo: view.trailingAnchor), 34 | additionalBottomBar.heightAnchor.constraint(equalToConstant: 50) 35 | ]) 36 | 37 | view.layoutIfNeeded() 38 | 39 | keyboardManager.additionalInputViewBottomConstraintConstant = { 40 | var safeBottomInset: CGFloat = self.view.safeAreaInsets.bottom 41 | if let windowBottomInset = UIWindowScene.activeScene?.windows.first?.safeAreaInsets.bottom, 42 | safeBottomInset != windowBottomInset { 43 | safeBottomInset = windowBottomInset 44 | } 45 | return -(self.additionalBottomBar.frame.height + safeBottomInset) 46 | } 47 | 48 | // Binding the inputBar will set the needed callback actions to position the inputBar on top of the keyboard 49 | keyboardManager.bind(inputAccessoryView: inputBar) 50 | 51 | // Binding to the tableView will enabled interactive dismissal 52 | keyboardManager.bind(to: tableView) 53 | } 54 | 55 | override func viewWillDisappear(_ animated: Bool) { 56 | super.viewWillDisappear(animated) 57 | 58 | /// This replicates instagram's behavior when commenting in a post. As of 2020-09, it appears like they have one of the best product experiences of this handling the keyboard when dismissing the UIViewController 59 | self.inputBar.inputTextView.resignFirstResponder() 60 | } 61 | 62 | override func viewDidAppear(_ animated: Bool) { 63 | super.viewDidAppear(animated) 64 | 65 | self.inputBar.inputTextView.becomeFirstResponder() 66 | } 67 | } 68 | 69 | private extension UIWindowScene { 70 | static var activeScene: UIWindowScene? { 71 | let connectedScenes = UIApplication.shared.connectedScenes 72 | if connectedScenes.count > 1 { 73 | return connectedScenes.first(where: { $0.activationState == .foregroundActive }) as? UIWindowScene 74 | } else { 75 | return connectedScenes.first as? UIWindowScene 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /Example/Sources/Example ViewControllers/InputAccessoryExampleViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // InputAccessoryExampleViewController.swift 3 | // Example 4 | // 5 | // Created by Nathan Tannar on 8/18/17. 6 | // Copyright © 2017-2020 Nathan Tannar. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import InputBarAccessoryView 11 | 12 | class InputAccessoryExampleViewController: CommonTableViewController { 13 | 14 | // MARK: - Properties 15 | 16 | override var inputAccessoryView: UIView? { 17 | return inputBar 18 | } 19 | 20 | override var canBecomeFirstResponder: Bool { 21 | return true 22 | } 23 | 24 | // MARK: - View Life Cycle 25 | 26 | override func viewDidLoad() { 27 | super.viewDidLoad() 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /Example/Sources/Example ViewControllers/READMEPreviewViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // READMEPreviewViewController.swift 3 | // Example 4 | // 5 | // Created by Nathan Tannar on 2018-11-16. 6 | // Copyright © 2018 Nathan Tannar. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import InputBarAccessoryView 11 | 12 | final class READMEPreviewViewController: InputBarViewController { 13 | 14 | lazy var autocompleteManager: AutocompleteManager = { [unowned self] in 15 | let manager = AutocompleteManager(for: self.inputBar.inputTextView) 16 | manager.delegate = self 17 | manager.dataSource = self 18 | manager.maxSpaceCountDuringCompletion = 1 19 | return manager 20 | }() 21 | 22 | override func viewDidLoad() { 23 | super.viewDidLoad() 24 | view.backgroundColor = .systemBlue 25 | inputBar.inputTextView.autocorrectionType = .no 26 | inputBar.inputTextView.autocapitalizationType = .none 27 | inputBar.inputTextView.keyboardType = .twitter 28 | let size = UIFont.preferredFont(forTextStyle: .body).pointSize 29 | autocompleteManager.register(prefix: "@", with: [.font: UIFont.preferredFont(forTextStyle: .body),.foregroundColor: UIColor.systemBlue,.backgroundColor: UIColor.systemBlue.withAlphaComponent(0.1)]) 30 | autocompleteManager.register(prefix: "#", with: [.font: UIFont.boldSystemFont(ofSize: size)]) 31 | inputBar.inputPlugins = [autocompleteManager] 32 | } 33 | 34 | override func inputBar(_ inputBar: InputBarAccessoryView, didPressSendButtonWith text: String) { 35 | 36 | setStateSending() 37 | DispatchQueue.global(qos: .background).async { [weak self] in 38 | sleep(2) 39 | DispatchQueue.main.async { [weak self] in 40 | self?.setStateReady() 41 | } 42 | } 43 | } 44 | 45 | private func setStateSending() { 46 | inputBar.inputTextView.text = "" 47 | inputBar.inputTextView.placeholder = "Sending..." 48 | inputBar.inputTextView.isEditable = false 49 | inputBar.sendButton.startAnimating() 50 | } 51 | 52 | private func setStateReady() { 53 | inputBar.inputTextView.text = "" 54 | inputBar.inputTextView.placeholder = "Aa" 55 | inputBar.inputTextView.isEditable = true 56 | inputBar.sendButton.stopAnimating() 57 | } 58 | } 59 | 60 | extension READMEPreviewViewController: AutocompleteManagerDelegate, AutocompleteManagerDataSource { 61 | 62 | // MARK: - AutocompleteManagerDataSource 63 | 64 | func autocompleteManager(_ manager: AutocompleteManager, autocompleteSourceFor prefix: String) -> [AutocompleteCompletion] { 65 | 66 | if prefix == "@" { 67 | let name = SampleData.shared.currentUser.name 68 | .lowercased().replacingOccurrences(of: " ", with: ".") 69 | return [AutocompleteCompletion(text: name)] 70 | } else { 71 | return ["InputBarAccessoryView", "iOS"].map { AutocompleteCompletion(text: $0) } 72 | } 73 | } 74 | 75 | func autocompleteManager(_ manager: AutocompleteManager, tableView: UITableView, cellForRowAt indexPath: IndexPath, for session: AutocompleteSession) -> UITableViewCell { 76 | 77 | guard let cell = tableView.dequeueReusableCell(withIdentifier: AutocompleteCell.reuseIdentifier, for: indexPath) as? AutocompleteCell else { 78 | fatalError("Oops, some unknown error occurred") 79 | } 80 | if session.prefix == "@" { 81 | let user = SampleData.shared.currentUser 82 | cell.imageView?.image = user.image 83 | cell.imageViewEdgeInsets = UIEdgeInsets(top: 4, left: 4, bottom: 4, right: 4) 84 | cell.imageView?.layer.cornerRadius = 8 85 | cell.imageView?.layer.borderWidth = 1 86 | cell.imageView?.layer.borderColor = UIColor.systemBlue.cgColor 87 | cell.imageView?.layer.masksToBounds = true 88 | } 89 | cell.textLabel?.attributedText = manager.attributedText(matching: session, fontSize: 15, keepPrefix: session.prefix == "#" ) 90 | return cell 91 | } 92 | 93 | // MARK: - AutocompleteManagerDelegate 94 | 95 | func autocompleteManager(_ manager: AutocompleteManager, shouldBecomeVisible: Bool) { 96 | setAutocompleteManager(active: shouldBecomeVisible) 97 | } 98 | 99 | // MARK: - AutocompleteManagerDelegate Helper 100 | 101 | func setAutocompleteManager(active: Bool) { 102 | let topStackView = inputBar.topStackView 103 | if active && !topStackView.arrangedSubviews.contains(autocompleteManager.tableView) { 104 | topStackView.insertArrangedSubview(autocompleteManager.tableView, at: topStackView.arrangedSubviews.count) 105 | topStackView.layoutIfNeeded() 106 | } else if !active && topStackView.arrangedSubviews.contains(autocompleteManager.tableView) { 107 | topStackView.removeArrangedSubview(autocompleteManager.tableView) 108 | topStackView.layoutIfNeeded() 109 | } 110 | inputBar.invalidateIntrinsicContentSize() 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /Example/Sources/Example ViewControllers/SubviewExampleViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SubviewExampleViewController.swift 3 | // Example 4 | // 5 | // Created by Nathan Tannar on 8/18/17. 6 | // Copyright © 2017-2020 Nathan Tannar. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import InputBarAccessoryView 11 | 12 | final class SubviewExampleViewController: CommonTableViewController { 13 | 14 | // MARK: - Properties 15 | 16 | private let keyboardManager = KeyboardManager() 17 | 18 | // MARK: - View Life Cycle 19 | 20 | override func viewDidLoad() { 21 | super.viewDidLoad() 22 | 23 | view.addSubview(inputBar) 24 | 25 | keyboardManager.shouldApplyAdditionBottomSpaceToInteractiveDismissal = true 26 | // Binding the inputBar will set the needed callback actions to position the inputBar on top of the keyboard 27 | keyboardManager.bind(inputAccessoryView: inputBar, withAdditionalBottomSpace: { 28 | return 0 29 | return -self.view.safeAreaInsets.bottom 30 | return -(self.inputBar.frame.height + self.view.safeAreaInsets.bottom) 31 | }) 32 | 33 | // Binding to the tableView will enabled interactive dismissal 34 | keyboardManager.bind(to: tableView) 35 | } 36 | 37 | override func viewWillDisappear(_ animated: Bool) { 38 | super.viewWillDisappear(animated) 39 | 40 | /// This replicates instagram's behavior when commenting in a post. As of 2020-09, it appears like they have one of the best product experiences of this handling the keyboard when dismissing the UIViewController 41 | self.inputBar.inputTextView.resignFirstResponder() 42 | } 43 | 44 | override func viewDidAppear(_ animated: Bool) { 45 | super.viewDidAppear(animated) 46 | 47 | /// The opposite of `viewWillDisappear(_:)` 48 | self.inputBar.inputTextView.becomeFirstResponder() 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /Example/Sources/Example ViewControllers/SwiftUIExample.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftUIExample.swift 3 | // Example 4 | // 5 | // Created by Andrew Breckenridge on 10/3/20. 6 | // Copyright © 2020 Nathan Tannar. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import SwiftUI 11 | import InputBarAccessoryView 12 | 13 | var built: CommonTableViewController? 14 | 15 | struct CommonTableViewControllerUI: UIViewControllerRepresentable { 16 | let style: InputBarStyle 17 | let conversation: SampleData.Conversation 18 | 19 | var inputBar: InputBarAccessoryView { 20 | if built == nil { 21 | built = CommonTableViewController(style: style, conversation: conversation) 22 | } 23 | return built!.inputBar 24 | } 25 | 26 | func makeUIViewController(context: Context) -> some UIViewController { 27 | if built == nil { 28 | built = CommonTableViewController(style: style, conversation: conversation) 29 | } 30 | return built! 31 | } 32 | 33 | func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) { 34 | } 35 | } 36 | 37 | struct InputBarUI: UIViewRepresentable { 38 | let view: InputBarAccessoryView 39 | 40 | func makeUIView(context: Context) -> some UIView { 41 | return view 42 | } 43 | 44 | func updateUIView(_ uiView: UIViewType, context: Context) { 45 | } 46 | } 47 | 48 | 49 | public struct SwiftUIExample: View { 50 | let style: InputBarStyle 51 | let conversation: SampleData.Conversation 52 | 53 | var table: CommonTableViewControllerUI { 54 | CommonTableViewControllerUI(style: style, conversation: conversation) 55 | } 56 | 57 | 58 | public var body: some View { 59 | VStack { 60 | self.table 61 | InputBarUI(view: table.inputBar) 62 | } 63 | } 64 | 65 | } 66 | 67 | extension SwiftUIExample { 68 | static func make(style: InputBarStyle, conversation: SampleData.Conversation) -> Self { 69 | return Self.init(style: style, conversation: conversation) 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /Example/Sources/Example.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.network.client 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Example/Sources/InputBar Examples/FacebookInputBar.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FacebookInputBar.swift 3 | // Example 4 | // 5 | // Created by Nathan Tannar on 2018-06-06. 6 | // Copyright © 2018 Nathan Tannar. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import InputBarAccessoryView 11 | 12 | final class FacebookInputBar: InputBarAccessoryView { 13 | 14 | override init(frame: CGRect) { 15 | super.init(frame: frame) 16 | configure() 17 | } 18 | 19 | required init?(coder aDecoder: NSCoder) { 20 | fatalError("init(coder:) has not been implemented") 21 | } 22 | 23 | func configure() { 24 | let button = InputBarButtonItem() 25 | button.onKeyboardSwipeGesture { item, gesture in 26 | if gesture.direction == .left { 27 | item.inputBarAccessoryView?.setLeftStackViewWidthConstant(to: 0, animated: true) 28 | } else if gesture.direction == .right { 29 | item.inputBarAccessoryView?.setLeftStackViewWidthConstant(to: 36, animated: true) 30 | } 31 | } 32 | button.setSize(CGSize(width: 36, height: 36), animated: false) 33 | button.setImage(#imageLiteral(resourceName: "ic_plus").withRenderingMode(.alwaysTemplate), for: .normal) 34 | button.imageView?.contentMode = .scaleAspectFit 35 | button.tintColor = .systemBlue 36 | inputTextView.textContainerInset = UIEdgeInsets(top: 8, left: 16, bottom: 8, right: 16) 37 | inputTextView.placeholderLabelInsets = UIEdgeInsets(top: 8, left: 20, bottom: 8, right: 20) 38 | if #available(iOS 13, *) { 39 | inputTextView.layer.borderColor = UIColor.systemGray2.cgColor 40 | } else { 41 | inputTextView.layer.borderColor = UIColor.lightGray.cgColor 42 | } 43 | inputTextView.layer.borderWidth = 1.0 44 | inputTextView.layer.cornerRadius = 16.0 45 | inputTextView.layer.masksToBounds = true 46 | inputTextView.scrollIndicatorInsets = UIEdgeInsets(top: 8, left: 0, bottom: 8, right: 0) 47 | setLeftStackViewWidthConstant(to: 36, animated: false) 48 | setStackViewItems([button], forStack: .left, animated: false) 49 | 50 | shouldAnimateTextDidChangeLayout = true 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /Example/Sources/InputBar Examples/GitHawkInputBar.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GitHawkInputBar.swift 3 | // Example 4 | // 5 | // Created by Nathan Tannar on 2018-06-06. 6 | // Copyright © 2018 Nathan Tannar. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import InputBarAccessoryView 11 | 12 | final class GitHawkInputBar: InputBarAccessoryView { 13 | 14 | private let githawkImages: [UIImage] = [#imageLiteral(resourceName: "ic_eye"), #imageLiteral(resourceName: "ic_bold"), #imageLiteral(resourceName: "ic_italic"), #imageLiteral(resourceName: "ic_at"), #imageLiteral(resourceName: "ic_list"), #imageLiteral(resourceName: "ic_code"), #imageLiteral(resourceName: "ic_link"), #imageLiteral(resourceName: "ic_hashtag"), #imageLiteral(resourceName: "ic_upload")] 15 | 16 | override init(frame: CGRect) { 17 | super.init(frame: frame) 18 | configure() 19 | } 20 | 21 | required init?(coder aDecoder: NSCoder) { 22 | fatalError("init(coder:) has not been implemented") 23 | } 24 | 25 | func configure() { 26 | inputTextView.placeholder = "Leave a comment" 27 | sendButton.contentEdgeInsets = UIEdgeInsets(top: 5, left: 5, bottom: 5, right: 5) 28 | sendButton.setSize(CGSize(width: 36, height: 36), animated: false) 29 | sendButton.image = #imageLiteral(resourceName: "ic_send").withRenderingMode(.alwaysTemplate) 30 | sendButton.title = nil 31 | sendButton.tintColor = tintColor 32 | 33 | let layout = UICollectionViewFlowLayout() 34 | layout.scrollDirection = .horizontal 35 | layout.itemSize = CGSize(width: 20, height: 20) 36 | layout.sectionInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 20) 37 | let collectionView = AttachmentCollectionView(frame: .zero, collectionViewLayout: layout) 38 | collectionView.intrinsicContentHeight = 20 39 | collectionView.dataSource = self 40 | collectionView.showsHorizontalScrollIndicator = false 41 | collectionView.register(ImageCell.self, forCellWithReuseIdentifier: ImageCell.reuseIdentifier) 42 | bottomStackView.addArrangedSubview(collectionView) 43 | collectionView.reloadData() 44 | } 45 | 46 | } 47 | 48 | extension GitHawkInputBar: UICollectionViewDataSource { 49 | 50 | func numberOfSections(in collectionView: UICollectionView) -> Int { 51 | return githawkImages.count 52 | } 53 | 54 | func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { 55 | return 1 56 | } 57 | 58 | func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { 59 | 60 | let cell = collectionView.dequeueReusableCell(withReuseIdentifier: ImageCell.reuseIdentifier, for: indexPath) as! ImageCell 61 | cell.imageView.image = githawkImages[indexPath.section].withRenderingMode(.alwaysTemplate) 62 | cell.imageView.tintColor = .black 63 | return cell 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /Example/Sources/InputBar Examples/InputBarStyle.swift: -------------------------------------------------------------------------------- 1 | // 2 | // InputBarStyle.swift 3 | // Example 4 | // 5 | // Created by Nathan Tannar on 8/18/17. 6 | // Copyright © 2017-2020 Nathan Tannar. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import InputBarAccessoryView 11 | 12 | enum InputBarStyle: String, CaseIterable { 13 | 14 | case imessage = "iMessage" 15 | case slack = "Slack" 16 | case githawk = "GitHawk" 17 | case facebook = "Facebook" 18 | case noTextView = "No InputTextView" 19 | case `default` = "Default" 20 | 21 | func generate() -> InputBarAccessoryView { 22 | switch self { 23 | case .imessage: return iMessageInputBar() 24 | case .slack: return SlackInputBar() 25 | case .githawk: return GitHawkInputBar() 26 | case .facebook: return FacebookInputBar() 27 | case .noTextView: return NoTextViewInputBar() 28 | case .default: return InputBarAccessoryView() 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Example/Sources/InputBar Examples/NoTextViewInputBar.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NoTextViewInputBar.swift 3 | // Example 4 | // 5 | // Created by Nathan Tannar on 2018-11-21. 6 | // Copyright © 2018 Nathan Tannar. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import InputBarAccessoryView 11 | 12 | final class NoTextViewInputBar: InputBarAccessoryView { 13 | 14 | let joinButton: UIButton = { 15 | let button = UIButton() 16 | button.setTitle("Join Chat", for: .normal) 17 | button.setTitleColor(.white, for: .normal) 18 | button.layer.cornerRadius = 10 19 | button.backgroundColor = .systemBlue 20 | return button 21 | }() 22 | 23 | override init(frame: CGRect) { 24 | super.init(frame: frame) 25 | configure() 26 | } 27 | 28 | required init?(coder aDecoder: NSCoder) { 29 | fatalError("init(coder:) has not been implemented") 30 | } 31 | 32 | func configure() { 33 | joinButton.heightAnchor.constraint(equalToConstant: 44).isActive = true 34 | setStackViewItems([], forStack: .right, animated: false) 35 | setRightStackViewWidthConstant(to: 0, animated: false) 36 | setMiddleContentView(joinButton, animated: false) 37 | 38 | joinButton.addTarget(self, action: #selector(joinPressed), for: .touchUpInside) 39 | } 40 | 41 | @objc func joinPressed() { 42 | setMiddleContentView(inputTextView, animated: false) 43 | setStackViewItems([sendButton], forStack: .right, animated: false) 44 | setRightStackViewWidthConstant(to: 52, animated: false) 45 | } 46 | } 47 | 48 | -------------------------------------------------------------------------------- /Example/Sources/InputBar Examples/SlackInputBar.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SlackInputBar.swift 3 | // Example 4 | // 5 | // Created by Nathan Tannar on 2018-06-06. 6 | // Copyright © 2018 Nathan Tannar. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import InputBarAccessoryView 11 | 12 | final class SlackInputBar: InputBarAccessoryView { 13 | 14 | override init(frame: CGRect) { 15 | super.init(frame: frame) 16 | configure() 17 | } 18 | 19 | required init?(coder aDecoder: NSCoder) { 20 | fatalError("init(coder:) has not been implemented") 21 | } 22 | 23 | func configure() { 24 | let items = [ 25 | makeButton(named: "ic_camera").onTextViewDidChange { button, textView in 26 | button.isEnabled = textView.text.isEmpty 27 | }.onSelected { 28 | $0.tintColor = .systemBlue 29 | }, 30 | makeButton(named: "ic_at").onSelected { 31 | self.inputPlugins.forEach { _ = $0.handleInput(of: "@" as AnyObject) } 32 | $0.tintColor = .systemBlue 33 | }, 34 | makeButton(named: "ic_hashtag").onSelected { 35 | self.inputPlugins.forEach { _ = $0.handleInput(of: "#" as AnyObject) } 36 | $0.tintColor = .systemBlue 37 | }, 38 | .flexibleSpace, 39 | makeButton(named: "ic_library") 40 | .onSelected { 41 | $0.tintColor = .systemBlue 42 | let imagePicker = UIImagePickerController() 43 | imagePicker.delegate = self 44 | imagePicker.sourceType = .photoLibrary 45 | (UIApplication.shared.delegate as? AppDelegate)?.window?.rootViewController?.present(imagePicker, animated: true, completion: nil) 46 | }, 47 | sendButton 48 | .configure { 49 | $0.layer.cornerRadius = 8 50 | $0.layer.borderWidth = 1.5 51 | $0.layer.borderColor = $0.titleColor(for: .disabled)?.cgColor 52 | $0.setTitleColor(.white, for: .normal) 53 | $0.setTitleColor(.white, for: .highlighted) 54 | $0.setSize(CGSize(width: 52, height: 30), animated: false) 55 | }.onDisabled { 56 | $0.layer.borderColor = $0.titleColor(for: .disabled)?.cgColor 57 | $0.backgroundColor = .clear 58 | }.onEnabled { 59 | $0.backgroundColor = .systemBlue 60 | $0.layer.borderColor = UIColor.clear.cgColor 61 | }.onSelected { 62 | // We use a transform becuase changing the size would cause the other views to relayout 63 | $0.transform = CGAffineTransform(scaleX: 1.2, y: 1.2) 64 | }.onDeselected { 65 | $0.transform = CGAffineTransform.identity 66 | } 67 | ] 68 | items.forEach { $0.tintColor = .lightGray } 69 | 70 | // We can change the container insets if we want 71 | inputTextView.textContainerInset = UIEdgeInsets(top: 8, left: 0, bottom: 8, right: 0) 72 | inputTextView.placeholderLabelInsets = UIEdgeInsets(top: 8, left: 5, bottom: 8, right: 5) 73 | 74 | let maxSizeItem = InputBarButtonItem() 75 | .configure { 76 | $0.image = UIImage(named: "icons8-expand")?.withRenderingMode(.alwaysTemplate) 77 | $0.tintColor = .darkGray 78 | $0.setSize(CGSize(width: 20, height: 20), animated: false) 79 | }.onSelected { 80 | let oldValue = $0.inputBarAccessoryView?.shouldForceTextViewMaxHeight ?? false 81 | $0.image = oldValue ? UIImage(named: "icons8-expand")?.withRenderingMode(.alwaysTemplate) : UIImage(named: "icons8-collapse")?.withRenderingMode(.alwaysTemplate) 82 | self.setShouldForceMaxTextViewHeight(to: !oldValue, animated: true) 83 | } 84 | rightStackView.alignment = .top 85 | setStackViewItems([maxSizeItem], forStack: .right, animated: false) 86 | setRightStackViewWidthConstant(to: 20, animated: false) 87 | 88 | // Finally set the items 89 | setStackViewItems(items, forStack: .bottom, animated: false) 90 | 91 | shouldAnimateTextDidChangeLayout = true 92 | } 93 | 94 | 95 | 96 | private func makeButton(named: String) -> InputBarButtonItem { 97 | return InputBarButtonItem() 98 | .configure { 99 | $0.spacing = .fixed(10) 100 | $0.image = UIImage(named: named)?.withRenderingMode(.alwaysTemplate) 101 | $0.setSize(CGSize(width: 30, height: 30), animated: false) 102 | }.onSelected { 103 | $0.tintColor = .systemBlue 104 | }.onDeselected { 105 | $0.tintColor = UIColor.lightGray 106 | }.onTouchUpInside { _ in 107 | print("Item Tapped") 108 | } 109 | } 110 | 111 | } 112 | 113 | extension SlackInputBar: UIImagePickerControllerDelegate, UINavigationControllerDelegate { 114 | 115 | func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) { 116 | // Local variable inserted by Swift 4.2 migrator. 117 | let info = convertFromUIImagePickerControllerInfoKeyDictionary(info) 118 | 119 | 120 | picker.dismiss(animated: true, completion: { 121 | if let pickedImage = info[convertFromUIImagePickerControllerInfoKey(UIImagePickerController.InfoKey.originalImage)] as? UIImage { 122 | self.inputPlugins.forEach { _ = $0.handleInput(of: pickedImage) } 123 | } 124 | }) 125 | } 126 | } 127 | 128 | // Helper function inserted by Swift 4.2 migrator. 129 | fileprivate func convertFromUIImagePickerControllerInfoKeyDictionary(_ input: [UIImagePickerController.InfoKey: Any]) -> [String: Any] { 130 | return Dictionary(uniqueKeysWithValues: input.map {key, value in (key.rawValue, value)}) 131 | } 132 | 133 | // Helper function inserted by Swift 4.2 migrator. 134 | fileprivate func convertFromUIImagePickerControllerInfoKey(_ input: UIImagePickerController.InfoKey) -> String { 135 | return input.rawValue 136 | } 137 | -------------------------------------------------------------------------------- /Example/Sources/InputBar Examples/iMessageInputBar.swift: -------------------------------------------------------------------------------- 1 | // 2 | // iMessageInputBar.swift 3 | // Example 4 | // 5 | // Created by Nathan Tannar on 2018-06-06. 6 | // Copyright © 2018 Nathan Tannar. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import InputBarAccessoryView 11 | 12 | final class iMessageInputBar: InputBarAccessoryView { 13 | 14 | override init(frame: CGRect) { 15 | super.init(frame: frame) 16 | configure() 17 | } 18 | 19 | required init?(coder aDecoder: NSCoder) { 20 | fatalError("init(coder:) has not been implemented") 21 | } 22 | 23 | func configure() { 24 | inputTextView.textContainerInset = UIEdgeInsets(top: 8, left: 16, bottom: 8, right: 36) 25 | inputTextView.placeholderLabelInsets = UIEdgeInsets(top: 8, left: 20, bottom: 8, right: 36) 26 | if #available(iOS 13, *) { 27 | inputTextView.layer.borderColor = UIColor.systemGray2.cgColor 28 | } else { 29 | inputTextView.layer.borderColor = UIColor.lightGray.cgColor 30 | } 31 | inputTextView.layer.borderWidth = 1.0 32 | inputTextView.layer.cornerRadius = 16.0 33 | inputTextView.layer.masksToBounds = true 34 | inputTextView.scrollIndicatorInsets = UIEdgeInsets(top: 8, left: 0, bottom: 8, right: 0) 35 | setRightStackViewWidthConstant(to: 38, animated: false) 36 | setStackViewItems([sendButton, InputBarButtonItem.fixedSpace(2)], forStack: .right, animated: false) 37 | sendButton.imageView?.backgroundColor = tintColor 38 | sendButton.contentEdgeInsets = UIEdgeInsets(top: 2, left: 2, bottom: 2, right: 2) 39 | sendButton.setSize(CGSize(width: 36, height: 36), animated: false) 40 | sendButton.image = #imageLiteral(resourceName: "ic_up") 41 | sendButton.title = nil 42 | sendButton.imageView?.layer.cornerRadius = 16 43 | sendButton.backgroundColor = .clear 44 | middleContentViewPadding.right = -38 45 | separatorLine.isHidden = true 46 | isTranslucent = true 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /Example/Sources/InputBarStyleSelectionController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // InputBarStyleSelectionController.swift 3 | // Example 4 | // 5 | // Created by Nathan Tannar on 8/18/17. 6 | // Copyright © 2017-2020 Nathan Tannar. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import SwiftUI 11 | 12 | class InputBarStyleSelectionController: UITableViewController { 13 | 14 | let styles = InputBarStyle.allCases 15 | 16 | let tabBarExampleIndexPath = IndexPath(row: 6, section: 2) 17 | 18 | override func viewDidLoad() { 19 | super.viewDidLoad() 20 | tableView.delegate = self 21 | tableView.dataSource = self 22 | tableView.tableFooterView = UIView() 23 | title = "InputBarAccessoryView" 24 | navigationItem.backBarButtonItem = UIBarButtonItem(title: "Styles", style: .plain, target: nil, action: nil) 25 | } 26 | 27 | override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { 28 | return [ 29 | 0: "InputBarViewController", 30 | 1: "InputAccessoryView", 31 | 2: "Subview", 32 | 3: "FAQ/Community Examples" 33 | ][section]! 34 | } 35 | 36 | override func numberOfSections(in tableView: UITableView) -> Int { 37 | return 4 38 | } 39 | 40 | override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 41 | switch section { 42 | case 0: return 1 43 | case 1...2: return styles.count 44 | case 3: return 4 45 | default: fatalError("unknown section \(section)") 46 | } 47 | } 48 | 49 | override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 50 | let cell = UITableViewCell() 51 | switch (indexPath.section, indexPath.row) { 52 | case (0, _): cell.textLabel?.text = "README Preview" 53 | case (1...2, _): cell.textLabel?.text = styles[indexPath.row].rawValue 54 | case (3, 0): cell.textLabel?.text = "Tab bar example (Slack style)" 55 | case (3, 1): cell.textLabel?.text = "Addition bottom space example (Slack)" 56 | case (3, 2): cell.textLabel?.text = "Send button animations" 57 | case (3, 3): cell.textLabel?.text = "SwiftUI example" 58 | default: assertionFailure("unrecognized \(indexPath). Are you trying to add an additional example?") 59 | } 60 | 61 | cell.accessoryType = .disclosureIndicator 62 | return cell 63 | } 64 | 65 | override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 66 | let convo = SampleData.shared.getConversations(count: 1)[0] 67 | switch indexPath.section { 68 | case 0: 69 | navigationController?.pushViewController(READMEPreviewViewController(), animated: true) 70 | case 1: 71 | let controller = InputAccessoryExampleViewController(style: styles[indexPath.row], conversation: convo) 72 | navigationController?.pushViewController(controller, animated: true) 73 | case 2: 74 | let controller = SubviewExampleViewController(style: styles[indexPath.row], conversation: convo) 75 | navigationController?.pushViewController(controller, animated: true) 76 | case 3: 77 | switch indexPath.row { 78 | case 0: 79 | let tabBarController = UITabBarController() 80 | let contained = SubviewExampleViewController(style: InputBarStyle.slack, conversation: convo) 81 | tabBarController.viewControllers = [contained] 82 | contained.tabBarItem = UITabBarItem(title: "Slack", image: UIImage(systemName: "number"), tag: 0) 83 | navigationController?.pushViewController(tabBarController, animated: true) 84 | case 1: 85 | let example = AdditionalBottomSpaceExampleViewController(style: .slack, conversation: convo) 86 | navigationController?.pushViewController(example, animated: true) 87 | case 2: 88 | let example = ButtonAnimationExample(style: .imessage, conversation: convo) 89 | navigationController?.pushViewController(example, animated: true) 90 | case 3: 91 | let example = UIHostingController(rootView: SwiftUIExample.make(style: .imessage, conversation: convo)) 92 | navigationController?.pushViewController(example, animated: true) 93 | default: 94 | fatalError("Unknown row \(indexPath.row) in Community Examples section. Are you trying to add a new example?") 95 | } 96 | default: 97 | fatalError("Unknown Section \(indexPath.section). Are you trying to add a new example?") 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /Example/Sources/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /Example/Sources/Sample Data/SampleData.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SampleData.swift 3 | // Example 4 | // 5 | // Created by Nathan Tannar on 2/6/18. 6 | // Copyright © 2018 Nathan Tannar. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class SampleData { 12 | 13 | class Conversation { 14 | 15 | let title: String 16 | 17 | var messages: [Message] 18 | 19 | var users: [User] 20 | 21 | var lastMessage: Message? { return messages.last } 22 | 23 | init(users: [User], messages: [Message]) { 24 | self.users = users 25 | self.messages = messages 26 | self.title = Lorem.words(nbWords: 4).capitalized 27 | } 28 | } 29 | 30 | class Message { 31 | 32 | let text: String 33 | let user: User 34 | 35 | init(user: User, text: String) { 36 | self.user = user 37 | self.text = text 38 | } 39 | } 40 | 41 | class User { 42 | 43 | let id: String = UUID().uuidString 44 | let image: UIImage 45 | let name: String 46 | 47 | init(name: String, image: UIImage) { 48 | self.image = image 49 | self.name = name 50 | } 51 | } 52 | 53 | static var shared = SampleData() 54 | 55 | let users = [User(name: "Avatar", image: #imageLiteral(resourceName: "avatar")), User(name: "Ninja", image: #imageLiteral(resourceName: "ninja")), User(name: "Anonymous", image: #imageLiteral(resourceName: "anonymous")), User(name: "Rick Sanchez", image: #imageLiteral(resourceName: "rick")), User(name: "Nathan Tannar", image: #imageLiteral(resourceName: "nathan"))] 56 | 57 | var currentUser: User { return users.last! } 58 | 59 | private init() {} 60 | 61 | func getConversations(count: Int) -> [Conversation] { 62 | 63 | var conversations = [Conversation]() 64 | for _ in 0.. 3 | 4 | # Getting Started 5 | 6 | ## AutocompleteManager 7 | 8 | The `AutocompleteManager` holds the logic and views required for the autocomplete functionality which makes it easy to subclass and modify if you wish to add additional logic! Then you can set the `InputBarAccessoryView`'s autocompleteManager property to your own 9 | 10 | ### Customization 11 | 12 | ```swift 13 | /// If the autocomplete matches should be made by casting the strings to lowercase. 14 | /// Default value is `FALSE` 15 | open var isCaseSensitive = false 16 | 17 | /// Adds an additional space after the autocompleted text when true. 18 | /// Default value is `TRUE` 19 | open var appendSpaceOnCompletion = true 20 | 21 | /// Keeps the prefix typed when text is autocompleted. 22 | /// Default value is `TRUE` 23 | open var keepPrefixOnCompletion = true 24 | 25 | /// The prefices that the manager will recognize 26 | /// Default value is `["@"]` 27 | open var autocompletePrefixes: [Character] = ["@"] 28 | 29 | /// The delimiters that cause a current autocomplete session to become invalidated when typed. 30 | /// Default value is `[" ", "\n"]` 31 | open var autocompleteDelimiters: [Character] = [" ", "\n"] 32 | 33 | /// The default text attributes 34 | open var defaultTextAttributes: [NSAttributedStringKey: Any] = 35 | [.font: UIFont.preferredFont(forTextStyle: .body), .foregroundColor: UIColor.black] 36 | 37 | /// The text attributes applied to highlighted substrings for each prefix 38 | /// Default value applys blue tint highlighting to the `@` prefix 39 | open var autocompleteTextAttributes: [Character: [NSAttributedStringKey: Any]] = 40 | ["@": [.font: UIFont.preferredFont(forTextStyle: .body), 41 | .foregroundColor: UIColor(red: 0, green: 122/255, blue: 1, alpha: 1), 42 | .backgroundColor: UIColor(red: 0, green: 122/255, blue: 1, alpha: 0.1)]] 43 | ``` 44 | 45 | ### AutocompleteManagerDataSource 46 | 47 | By default an `AutocompleteCell` is returned to the `AutocompleteManager` that's title labels text is bolded to match the entered text. 48 | 49 | ```swift 50 | /// The autocomplete options for the registered prefix. 51 | func autocompleteManager(_ manager: AutocompleteManager, autocompleteSourceFor prefix: Character) -> [AutocompleteCompletion] 52 | 53 | /// The cell to populate the `AutocompleteTableView` with 54 | func autocompleteManager(_ manager: AutocompleteManager, tableView: UITableView, cellForRowAt indexPath: IndexPath, for session: AutocompleteSession) -> UITableViewCell 55 | ``` 56 | 57 | ### AutocompleteManagerDelegate 58 | 59 | ```swift 60 | /// Can be used to determine if the AutocompleteManager should be inserted into 61 | func autocompleteManager(_ manager: AutocompleteManager, shouldBecomeVisible: Bool) 62 | ``` 63 | 64 | ## Attachment Manager 65 | 66 | The `AttachmentManager` is an easy way to display images or other additional content above the `InputTextView` 67 | 68 | ```swift 69 | public enum Attachment { 70 | case image(UIImage) 71 | case url(URL) 72 | case data(Data) 73 | case other(AnyObject) 74 | } 75 | ``` 76 | 77 | ### Customization 78 | 79 | ```swift 80 | /// A flag you can use to determine if you want the manager to be always visible 81 | open var isPersistent = false { didSet { attachmentView.reloadData() } } 82 | 83 | /// A flag to determine if the AddAttachmentCell is visible 84 | open var showAddAttachmentCell = true { didSet { attachmentView.reloadData() } } 85 | ``` 86 | 87 | ### Usage 88 | 89 | ```swift 90 | /// Performs an animated insertion of an attachment at an index 91 | open func insertAttachment(_ attachment: Attachment, at index: Int) 92 | 93 | /// Performs an animated removal of an attachment at an index 94 | open func removeAttachment(at index: Int) 95 | ``` 96 | 97 | ### AttachmentManagerDelegate 98 | 99 | ```swift 100 | /// Can be used to determine if the AttachmentManager should be inserted into an InputStackView 101 | func attachmentManager(_ manager: AttachmentManager, shouldBecomeVisible: Bool) 102 | ``` 103 | 104 | ### AttachmentManagerDataSource 105 | 106 | ```swift 107 | /// The AttachmentCell for the attachment that is to be inserted into the AttachmentView 108 | func attachmentManager(_ manager: AttachmentManager, cellFor attachment: AttachmentManager.Attachment, at index: Int) -> AttachmentCell 109 | ``` 110 | 111 | 112 | ## InputBarButtonItem 113 | 114 | It is recommended that you use the `InputBarButtonItem` for the `InputStackView `'s. This is because all `InputStackView`'s size their arranged subviews based on intrinsicContentSize: 115 | 116 | This will layout the arrangedViews based on their intrinsicContentSize and if there is extra space the views will be expanded based on their content hugging `UILayoutPriority`. 117 | 118 | ### Size 119 | 120 | Each `InputBarButtonItem`'s `intrinsicContentSize` can be overridden by setting the `size` property. It is optional so when set to `nil` the `super.intrinsicContentSize` will be used. 121 | 122 | ### Spacing 123 | 124 | Spacing can be set using the `spacing` property. This will change the content hugging `UILayoutPriority` and add extra space to the `intrinsicContentSize` when set to `.fixed(CGFloat)`. 125 | 126 | ## InputBarAccessoryViewDelegate 127 | 128 | ```swift 129 | func inputBar(_ inputBar: InputBarAccessoryView, didPressSendButtonWith text: String) 130 | 131 | // Useful for updating a UICollectionView or UITableView bottom inset 132 | func inputBar(_ inputBar: InputBarAccessoryView, didChangeIntrinsicContentTo size: CGSize) 133 | 134 | func inputBar(_ inputBar: InputBarAccessoryView, textViewTextDidChangeTo text: String) 135 | 136 | func inputBar(_ inputBar: InputBarAccessoryView, didSwipeTextViewWith gesture: UISwipeGestureRecognizer) 137 | ``` -------------------------------------------------------------------------------- /GitHubActions/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | function trap_handler { 5 | echo -e "\n\nOh no! You walked directly into the slavering fangs of a lurking grue!" 6 | echo "**** You have died ****" 7 | exit 255 8 | } 9 | trap trap_handler INT TERM EXIT 10 | 11 | MODE="$1" 12 | 13 | if [ "$MODE" = "framework" -o "$MODE" = "all" ]; then 14 | echo "Building InputBarAccessoryView Framework." 15 | set -o pipefail && xcodebuild build -scheme InputBarAccessoryView -sdk iphonesimulator -destination "platform=iOS Simulator,name=iPhone 16" | xcpretty -c 16 | success="1" 17 | fi 18 | 19 | if [ "$MODE" = "example" -o "$MODE" = "all" ]; then 20 | echo "Building InputBarAccessoryView Example app." 21 | set -o pipefail && xcodebuild build -project Example/Example.xcodeproj -scheme Example -sdk iphonesimulator -destination "platform=iOS Simulator,name=iPhone 16 Pro" | xcpretty -c 22 | success="1" 23 | fi 24 | 25 | if [ "$success" = "1" ]; then 26 | trap - EXIT 27 | exit 0 28 | fi 29 | 30 | echo "Unrecognised mode '$MODE'." 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | The MIT License (MIT) 3 | 4 | Copyright (c) Nathan Tannar 2017-2019 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:6.0 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: "InputBarAccessoryView", 8 | platforms: [.iOS(.v14)], 9 | products: [ 10 | .library(name: "InputBarAccessoryView", targets: ["InputBarAccessoryView"]), 11 | ], 12 | targets: [ 13 | .target( 14 | name: "InputBarAccessoryView", 15 | path: "Sources", 16 | exclude: ["Supporting/Info.plist"] 17 | ) 18 | ], 19 | swiftLanguageModes: [.v6] 20 | ) 21 | -------------------------------------------------------------------------------- /Screenshots/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nathantannar4/InputBarAccessoryView/dfdc822b0d4a0d1b5777100347c399069e9bfc62/Screenshots/.DS_Store -------------------------------------------------------------------------------- /Screenshots/Layout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nathantannar4/InputBarAccessoryView/dfdc822b0d4a0d1b5777100347c399069e9bfc62/Screenshots/Layout.png -------------------------------------------------------------------------------- /Screenshots/Preview.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nathantannar4/InputBarAccessoryView/dfdc822b0d4a0d1b5777100347c399069e9bfc62/Screenshots/Preview.gif -------------------------------------------------------------------------------- /Screenshots/ScreenshotA.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nathantannar4/InputBarAccessoryView/dfdc822b0d4a0d1b5777100347c399069e9bfc62/Screenshots/ScreenshotA.png -------------------------------------------------------------------------------- /Screenshots/ScreenshotB.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nathantannar4/InputBarAccessoryView/dfdc822b0d4a0d1b5777100347c399069e9bfc62/Screenshots/ScreenshotB.png -------------------------------------------------------------------------------- /Screenshots/ScreenshotC.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nathantannar4/InputBarAccessoryView/dfdc822b0d4a0d1b5777100347c399069e9bfc62/Screenshots/ScreenshotC.png -------------------------------------------------------------------------------- /Screenshots/ScreenshotD.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nathantannar4/InputBarAccessoryView/dfdc822b0d4a0d1b5777100347c399069e9bfc62/Screenshots/ScreenshotD.png -------------------------------------------------------------------------------- /Screenshots/ScreenshotE.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nathantannar4/InputBarAccessoryView/dfdc822b0d4a0d1b5777100347c399069e9bfc62/Screenshots/ScreenshotE.png -------------------------------------------------------------------------------- /Screenshots/ScreenshotF.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nathantannar4/InputBarAccessoryView/dfdc822b0d4a0d1b5777100347c399069e9bfc62/Screenshots/ScreenshotF.png -------------------------------------------------------------------------------- /Sources/Controls/InputBarSendButton.swift: -------------------------------------------------------------------------------- 1 | // 2 | // InputBarSendButton.swift 3 | // InputBarAccessoryView 4 | // 5 | // Copyright © 2017-2020 Nathan Tannar. 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // 25 | // Created by Nathan Tannar on 8/18/17. 26 | // 27 | 28 | import UIKit 29 | 30 | open class InputBarSendButton: InputBarButtonItem { 31 | 32 | /// A flag indicating the animation state of the `InputBarSendButton` 33 | open private(set) var isAnimating: Bool = false 34 | 35 | /// Accessor to modify the color of the activity view 36 | open var activityViewColor: UIColor! { 37 | get { 38 | return activityView.color 39 | } 40 | set { 41 | activityView.color = newValue 42 | } 43 | } 44 | 45 | private let activityView: UIActivityIndicatorView = { 46 | let view: UIActivityIndicatorView 47 | 48 | if #available(iOS 13.0, *) { 49 | view = UIActivityIndicatorView(style: .medium) 50 | } else { 51 | view = UIActivityIndicatorView(style: .gray) 52 | } 53 | 54 | view.isUserInteractionEnabled = false 55 | view.isHidden = true 56 | return view 57 | }() 58 | 59 | public convenience init() { 60 | self.init(frame: .zero) 61 | } 62 | 63 | public override init(frame: CGRect) { 64 | super.init(frame: frame) 65 | setupSendButton() 66 | } 67 | 68 | required public init?(coder aDecoder: NSCoder) { 69 | super.init(coder: aDecoder) 70 | setupSendButton() 71 | } 72 | 73 | private func setupSendButton() { 74 | addSubview(activityView) 75 | } 76 | 77 | open override func layoutSubviews() { 78 | super.layoutSubviews() 79 | activityView.frame = bounds 80 | } 81 | 82 | /// Starts the animation of the activity view, hiding other elements 83 | open func startAnimating() { 84 | guard !isAnimating else { return } 85 | defer { isAnimating = true } 86 | activityView.startAnimating() 87 | activityView.isHidden = false 88 | // Setting isHidden doesn't hide the elements 89 | titleLabel?.alpha = 0 90 | imageView?.layer.transform = CATransform3DMakeScale(0.0, 0.0, 0.0) 91 | } 92 | 93 | /// Stops the animation of the activity view, shows other elements 94 | open func stopAnimating() { 95 | guard isAnimating else { return } 96 | defer { isAnimating = false } 97 | activityView.stopAnimating() 98 | activityView.isHidden = true 99 | titleLabel?.alpha = 1 100 | imageView?.layer.transform = CATransform3DIdentity 101 | } 102 | 103 | } 104 | -------------------------------------------------------------------------------- /Sources/Extensions/NSMutableAttributedString+Extensions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NSMutableAttributedString+Extensions.swift 3 | // InputBarAccessoryView 4 | // 5 | // Copyright © 2017-2020 Nathan Tannar. 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // 25 | // Created by Nathan Tannar on 8/25/17. 26 | // 27 | 28 | import UIKit 29 | 30 | internal extension NSMutableAttributedString { 31 | 32 | @discardableResult 33 | func bold(_ text: String, fontSize: CGFloat = UIFont.preferredFont(forTextStyle: .body).pointSize, textColor: UIColor) -> NSMutableAttributedString { 34 | let attrs: [NSAttributedString.Key:AnyObject] = [ 35 | NSAttributedString.Key.font : UIFont.boldSystemFont(ofSize: fontSize), 36 | NSAttributedString.Key.foregroundColor : textColor 37 | ] 38 | let boldString = NSMutableAttributedString(string: text, attributes: attrs) 39 | self.append(boldString) 40 | return self 41 | } 42 | 43 | @discardableResult 44 | func medium(_ text: String, fontSize: CGFloat = UIFont.preferredFont(forTextStyle: .body).pointSize, textColor: UIColor) -> NSMutableAttributedString { 45 | let attrs: [NSAttributedString.Key:AnyObject] = [ 46 | NSAttributedString.Key.font : UIFont.systemFont(ofSize: fontSize, weight: UIFont.Weight.medium), 47 | NSAttributedString.Key.foregroundColor : textColor 48 | ] 49 | let mediumString = NSMutableAttributedString(string: text, attributes: attrs) 50 | self.append(mediumString) 51 | return self 52 | } 53 | 54 | @discardableResult 55 | func italic(_ text: String, fontSize: CGFloat = UIFont.preferredFont(forTextStyle: .body).pointSize, textColor: UIColor) -> NSMutableAttributedString { 56 | let attrs: [NSAttributedString.Key:AnyObject] = [ 57 | NSAttributedString.Key.font : UIFont.italicSystemFont(ofSize: fontSize), 58 | NSAttributedString.Key.foregroundColor : textColor 59 | ] 60 | let italicString = NSMutableAttributedString(string: text, attributes: attrs) 61 | self.append(italicString) 62 | return self 63 | } 64 | 65 | @discardableResult 66 | func normal(_ text: String, fontSize: CGFloat = UIFont.preferredFont(forTextStyle: .body).pointSize, textColor: UIColor) -> NSMutableAttributedString { 67 | let attrs:[NSAttributedString.Key:AnyObject] = [ 68 | NSAttributedString.Key.font : UIFont.systemFont(ofSize: fontSize), 69 | NSAttributedString.Key.foregroundColor : textColor 70 | ] 71 | let normal = NSMutableAttributedString(string: text, attributes: attrs) 72 | self.append(normal) 73 | return self 74 | } 75 | 76 | @discardableResult 77 | func bold(_ text: String, fontSize: CGFloat = UIFont.preferredFont(forTextStyle: .body).pointSize) -> NSMutableAttributedString { 78 | if #available(iOS 13, *) { 79 | return bold(text, fontSize: fontSize, textColor: .label) 80 | } else { 81 | return bold(text, fontSize: fontSize, textColor: .black) 82 | } 83 | } 84 | 85 | @discardableResult 86 | func medium(_ text: String, fontSize: CGFloat = UIFont.preferredFont(forTextStyle: .body).pointSize) -> NSMutableAttributedString { 87 | if #available(iOS 13, *) { 88 | return medium(text, fontSize: fontSize, textColor: .label) 89 | } else { 90 | return medium(text, fontSize: fontSize, textColor: .black) 91 | } 92 | } 93 | 94 | @discardableResult 95 | func italic(_ text: String, fontSize: CGFloat = UIFont.preferredFont(forTextStyle: .body).pointSize) -> NSMutableAttributedString { 96 | if #available(iOS 13, *) { 97 | return italic(text, fontSize: fontSize, textColor: .label) 98 | } else { 99 | return italic(text, fontSize: fontSize, textColor: .black) 100 | 101 | } 102 | } 103 | 104 | @discardableResult 105 | func normal(_ text: String, fontSize: CGFloat = UIFont.preferredFont(forTextStyle: .body).pointSize) -> NSMutableAttributedString { 106 | if #available(iOS 13, *) { 107 | return normal(text, fontSize: fontSize, textColor: .label) 108 | } else { 109 | return normal(text, fontSize: fontSize, textColor: .black) 110 | } 111 | } 112 | } 113 | 114 | internal extension NSAttributedString { 115 | 116 | func replacingCharacters(in range: NSRange, with attributedString: NSAttributedString) -> NSMutableAttributedString { 117 | let ns = NSMutableAttributedString(attributedString: self) 118 | ns.replaceCharacters(in: range, with: attributedString) 119 | return ns 120 | } 121 | 122 | static func += (lhs: inout NSAttributedString, rhs: NSAttributedString) { 123 | let ns = NSMutableAttributedString(attributedString: lhs) 124 | ns.append(rhs) 125 | lhs = ns 126 | } 127 | 128 | static func + (lhs: NSAttributedString, rhs: NSAttributedString) -> NSAttributedString { 129 | let ns = NSMutableAttributedString(attributedString: lhs) 130 | ns.append(rhs) 131 | return NSAttributedString(attributedString: ns) 132 | } 133 | 134 | } 135 | -------------------------------------------------------------------------------- /Sources/Extensions/NSNotification+Extensions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NSNotification+Extensions.swift 3 | // InputBarAccessoryView 4 | // 5 | // Copyright © 2017-2020 Nathan Tannar. 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // 25 | // Created by Nathan Tannar on 8/25/17. 26 | // 27 | 28 | import UIKit 29 | 30 | internal extension NSNotification { 31 | 32 | var event: KeyboardEvent { 33 | switch self.name { 34 | case UIResponder.keyboardWillShowNotification: 35 | return .willShow 36 | case UIResponder.keyboardDidShowNotification: 37 | return .didShow 38 | case UIResponder.keyboardWillHideNotification: 39 | return .willHide 40 | case UIResponder.keyboardDidHideNotification: 41 | return .didHide 42 | case UIResponder.keyboardWillChangeFrameNotification: 43 | return .willChangeFrame 44 | case UIResponder.keyboardDidChangeFrameNotification: 45 | return .didChangeFrame 46 | default: 47 | return .unknown 48 | } 49 | } 50 | 51 | var timeInterval: TimeInterval? { 52 | guard let value = userInfo?[UIResponder.keyboardAnimationDurationUserInfoKey] as? NSNumber else { return nil } 53 | return TimeInterval(truncating: value) 54 | } 55 | 56 | var animationCurve: UIView.AnimationCurve? { 57 | guard let index = (userInfo?[UIResponder.keyboardAnimationCurveUserInfoKey] as? NSNumber)?.intValue else { return nil } 58 | guard index >= 0 && index <= 3 else { return .linear } 59 | return UIView.AnimationCurve.init(rawValue: index) ?? .linear 60 | } 61 | 62 | var animationOptions: UIView.AnimationOptions { 63 | guard let curve = animationCurve else { return [] } 64 | switch curve { 65 | case .easeIn: 66 | return .curveEaseIn 67 | case .easeOut: 68 | return .curveEaseOut 69 | case .easeInOut: 70 | return .curveEaseInOut 71 | case .linear: 72 | return .curveLinear 73 | @unknown default: 74 | return .curveLinear 75 | } 76 | } 77 | 78 | var startFrame: CGRect? { 79 | return (userInfo?[UIResponder.keyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue 80 | } 81 | 82 | var endFrame: CGRect? { 83 | return (userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue 84 | } 85 | 86 | var isForCurrentApp: Bool? { 87 | return (userInfo?[UIResponder.keyboardIsLocalUserInfoKey] as? NSNumber)?.boolValue 88 | } 89 | 90 | } 91 | 92 | -------------------------------------------------------------------------------- /Sources/Extensions/String+Extensions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // String+Extensions.swift 3 | // InputBarAccessoryView 4 | // 5 | // Created by Ryan Nystrom on 12/22/17. 6 | // Modified by Nathan Tannar on 09/18/18 7 | // Copyright © 2017 Ryan Nystrom. All rights reserved. 8 | // 9 | 10 | import Foundation 11 | 12 | internal extension Character { 13 | 14 | static var space: Character { 15 | return " " 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Sources/Extensions/UITextView+Extensions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UITextView+Extensions.swift 3 | // InputBarAccessoryView 4 | // 5 | // Created by Nathan Tannar on 09/18/18. 6 | // Copyright © 2018 Nathan Tannar. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | public extension UITextView { 12 | 13 | typealias Match = (prefix: String, word: String, range: NSRange) 14 | 15 | func find(prefixes: Set, with delimiterSet: CharacterSet) -> Match? { 16 | guard prefixes.count > 0 else { return nil } 17 | 18 | let matches = prefixes.compactMap { find(prefix: $0, with: delimiterSet) } 19 | let sorted = matches.sorted { a, b in 20 | return a.range.lowerBound > b.range.lowerBound 21 | } 22 | return sorted.first 23 | } 24 | 25 | func find(prefix: String, with delimiterSet: CharacterSet) -> Match? { 26 | guard !prefix.isEmpty else { return nil } 27 | guard let caretRange = self.caretRange else { return nil } 28 | guard let cursorRange = Range(caretRange, in: text) else { return nil } 29 | 30 | let leadingText = text[.. [NSLayoutConstraint] { 45 | 46 | if self.superview == nil { 47 | return [] 48 | } 49 | translatesAutoresizingMaskIntoConstraints = false 50 | 51 | var constraints = [NSLayoutConstraint]() 52 | 53 | if let top = top { 54 | let constraint = topAnchor.constraint(equalTo: top, constant: topConstant) 55 | constraint.identifier = "top" 56 | constraints.append(constraint) 57 | } 58 | 59 | if let left = left { 60 | let constraint = leftAnchor.constraint(equalTo: left, constant: leftConstant) 61 | constraint.identifier = "left" 62 | constraints.append(constraint) 63 | } 64 | 65 | if let bottom = bottom { 66 | let constraint = bottomAnchor.constraint(equalTo: bottom, constant: -bottomConstant) 67 | constraint.identifier = "bottom" 68 | constraints.append(constraint) 69 | } 70 | 71 | if let right = right { 72 | let constraint = rightAnchor.constraint(equalTo: right, constant: -rightConstant) 73 | constraint.identifier = "right" 74 | constraints.append(constraint) 75 | } 76 | 77 | if widthConstant > 0 { 78 | let constraint = widthAnchor.constraint(equalToConstant: widthConstant) 79 | constraint.identifier = "width" 80 | constraints.append(constraint) 81 | } 82 | 83 | if heightConstant > 0 { 84 | let constraint = heightAnchor.constraint(equalToConstant: heightConstant) 85 | constraint.identifier = "height" 86 | constraints.append(constraint) 87 | } 88 | 89 | constraints.forEach { $0.isActive = true } 90 | return constraints 91 | } 92 | 93 | func removeAllConstraints() { 94 | constraints.forEach { removeConstraint($0) } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /Sources/KeyboardManager/KeyboardEvent.swift: -------------------------------------------------------------------------------- 1 | // 2 | // KeyboardEvent.swift 3 | // InputBarAccessoryView 4 | // 5 | // Copyright © 2017-2020 Nathan Tannar. 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // 25 | // Created by Nathan Tannar on 8/18/17. 26 | // 27 | import Foundation 28 | 29 | /// Keyboard events that can happen. Translates directly to `UIKeyboard` notifications from UIKit. 30 | public enum KeyboardEvent { 31 | 32 | /// Event raised by UIKit's `.UIKeyboardWillShow`. 33 | case willShow 34 | 35 | /// Event raised by UIKit's `.UIKeyboardDidShow`. 36 | case didShow 37 | 38 | /// Event raised by UIKit's `.UIKeyboardWillShow`. 39 | case willHide 40 | 41 | /// Event raised by UIKit's `.UIKeyboardDidHide`. 42 | case didHide 43 | 44 | /// Event raised by UIKit's `.UIKeyboardWillChangeFrame`. 45 | case willChangeFrame 46 | 47 | /// Event raised by UIKit's `.UIKeyboardDidChangeFrame`. 48 | case didChangeFrame 49 | 50 | /// Non-keyboard based event raised by UIKit 51 | case unknown 52 | 53 | } 54 | -------------------------------------------------------------------------------- /Sources/KeyboardManager/KeyboardNotification.swift: -------------------------------------------------------------------------------- 1 | // 2 | // KeyboardNotification.swift 3 | // InputBarAccessoryView 4 | // 5 | // Copyright © 2017-2020 Nathan Tannar. 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // 25 | // Created by Nathan Tannar on 8/18/17. 26 | // 27 | 28 | import UIKit 29 | 30 | /// An object containing the key animation properties from NSNotification 31 | public struct KeyboardNotification { 32 | 33 | // MARK: - Properties 34 | 35 | /// The event that triggered the transition 36 | public let event: KeyboardEvent 37 | 38 | /// The animation length the keyboards transition 39 | public let timeInterval: TimeInterval 40 | 41 | /// The animation properties of the keyboards transition 42 | public let animationOptions: UIView.AnimationOptions 43 | 44 | /// iPad supports split-screen apps, this indicates if the notification was for the current app 45 | public let isForCurrentApp: Bool 46 | 47 | /// The keyboards frame at the start of its transition 48 | public var startFrame: CGRect 49 | 50 | /// The keyboards frame at the beginning of its transition 51 | public var endFrame: CGRect 52 | 53 | /// Requires that the `NSNotification` is based on a `UIKeyboard...` event 54 | /// 55 | /// - Parameter notification: `KeyboardNotification` 56 | public init?(from notification: NSNotification) { 57 | guard notification.event != .unknown else { return nil } 58 | self.event = notification.event 59 | self.timeInterval = notification.timeInterval ?? 0.25 60 | self.animationOptions = notification.animationOptions 61 | self.isForCurrentApp = notification.isForCurrentApp ?? true 62 | self.startFrame = notification.startFrame ?? .zero 63 | self.endFrame = notification.endFrame ?? .zero 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /Sources/Models/HorizontalEdgePadding.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HorizontalEdgePadding.swift 3 | // InputBarAccessoryView 4 | // 5 | // Created by Nathan Tannar on 2018-11-07. 6 | // Copyright © 2018 Nathan Tannar. All rights reserved. 7 | // 8 | 9 | import CoreGraphics 10 | 11 | public struct HorizontalEdgePadding: Sendable { 12 | public let left: CGFloat 13 | public let right: CGFloat 14 | 15 | public static let zero = HorizontalEdgePadding(left: 0, right: 0) 16 | 17 | public init(left: CGFloat, right: CGFloat) { 18 | self.left = left 19 | self.right = right 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Sources/Models/NSConstraintLayoutSet.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NSConstraintLayoutSet.swift 3 | // InputBarAccessoryView 4 | // 5 | // Copyright © 2017-2020 Nathan Tannar. 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // 25 | // Created by Nathan Tannar on 8/25/17. 26 | // 27 | 28 | import Foundation 29 | import UIKit 30 | 31 | @MainActor 32 | class NSLayoutConstraintSet { 33 | 34 | var top: NSLayoutConstraint? 35 | var bottom: NSLayoutConstraint? 36 | var left: NSLayoutConstraint? 37 | var right: NSLayoutConstraint? 38 | var centerX: NSLayoutConstraint? 39 | var centerY: NSLayoutConstraint? 40 | var width: NSLayoutConstraint? 41 | var height: NSLayoutConstraint? 42 | 43 | public init(top: NSLayoutConstraint? = nil, 44 | bottom: NSLayoutConstraint? = nil, 45 | left: NSLayoutConstraint? = nil, 46 | right: NSLayoutConstraint? = nil, 47 | centerX: NSLayoutConstraint? = nil, 48 | centerY: NSLayoutConstraint? = nil, 49 | width: NSLayoutConstraint? = nil, 50 | height: NSLayoutConstraint? = nil) { 51 | self.top = top 52 | self.bottom = bottom 53 | self.left = left 54 | self.right = right 55 | self.centerX = centerX 56 | self.centerY = centerY 57 | self.width = width 58 | self.height = height 59 | } 60 | 61 | /// All of the currently configured constraints 62 | private var availableConstraints: [NSLayoutConstraint] { 63 | #if swift(>=4.1) 64 | return [top, bottom, left, right, centerX, centerY, width, height].compactMap {$0} 65 | #else 66 | return [top, bottom, left, right, centerX, centerY, width, height].flatMap {$0} 67 | #endif 68 | } 69 | 70 | /// Activates all of the non-nil constraints 71 | /// 72 | /// - Returns: Self 73 | @discardableResult 74 | func activate() -> Self { 75 | NSLayoutConstraint.activate(availableConstraints) 76 | return self 77 | } 78 | 79 | /// Deactivates all of the non-nil constraints 80 | /// 81 | /// - Returns: Self 82 | @discardableResult 83 | func deactivate() -> Self { 84 | NSLayoutConstraint.deactivate(availableConstraints) 85 | return self 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /Sources/Plugins/AttachmentManager/Protocols/AttachmentManagerDataSource.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AttachmentManagerDataSource.swift 3 | // InputBarAccessoryView 4 | // 5 | // Copyright © 2017-2020 Nathan Tannar. 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // 25 | // Created by Nathan Tannar on 10/6/17. 26 | // 27 | 28 | import Foundation 29 | import UIKit 30 | 31 | /// AttachmentManagerDataSource is a protocol to passes data to the AttachmentManager 32 | public protocol AttachmentManagerDataSource: AnyObject { 33 | 34 | /// The AttachmentCell for the attachment that is to be inserted into the AttachmentView 35 | /// 36 | /// - Parameters: 37 | /// - manager: The AttachmentManager 38 | /// - attachment: The object 39 | /// - index: The index in the AttachmentView 40 | /// - Returns: An AttachmentCell 41 | func attachmentManager(_ manager: AttachmentManager, cellFor attachment: AttachmentManager.Attachment, at index: Int) -> AttachmentCell 42 | 43 | /// The CGSize of the AttachmentCell for the attachment that is to be inserted into the AttachmentView 44 | /// 45 | /// - Parameters: 46 | /// - manager: The AttachmentManager 47 | /// - attachment: The object 48 | /// - index: The index in the AttachmentView 49 | /// - Returns: The size of the given attachment 50 | func attachmentManager(_ manager: AttachmentManager, sizeFor attachment: AttachmentManager.Attachment, at index: Int) -> CGSize? 51 | } 52 | 53 | public extension AttachmentManagerDataSource{ 54 | 55 | // Default implementation, if data source method is not given, use autocalculated default. 56 | func attachmentManager(_ manager: AttachmentManager, sizeFor attachment: AttachmentManager.Attachment, at index: Int) -> CGSize? { 57 | return nil 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /Sources/Plugins/AttachmentManager/Protocols/AttachmentManagerDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AttachmentManagerDelegate.swift 3 | // InputBarAccessoryView 4 | // 5 | // Copyright © 2017-2020 Nathan Tannar. 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // 25 | // Created by Nathan Tannar on 10/6/17. 26 | // 27 | 28 | import UIKit 29 | 30 | /// AttachmentManagerDelegate is a protocol that can recieve notifications from the AttachmentManager 31 | public protocol AttachmentManagerDelegate: AnyObject { 32 | 33 | /// Can be used to determine if the AttachmentManager should be inserted into an InputStackView 34 | /// 35 | /// - Parameters: 36 | /// - manager: The AttachmentManager 37 | /// - shouldBecomeVisible: If the AttachmentManager should be presented or dismissed 38 | func attachmentManager(_ manager: AttachmentManager, shouldBecomeVisible: Bool) 39 | 40 | 41 | /// Notifys when an attachment has been inserted into the AttachmentManager 42 | /// 43 | /// - Parameters: 44 | /// - manager: The AttachmentManager 45 | /// - attachment: The attachment that was inserted 46 | /// - index: The index of the attachment in the AttachmentManager's attachments array 47 | func attachmentManager(_ manager: AttachmentManager, didInsert attachment: AttachmentManager.Attachment, at index: Int) 48 | 49 | /// Notifys when an attachment has been removed from the AttachmentManager 50 | /// 51 | /// - Parameters: 52 | /// - manager: The AttachmentManager 53 | /// - attachment: The attachment that was removed 54 | /// - index: The index of the attachment in the AttachmentManager's attachments array 55 | func attachmentManager(_ manager: AttachmentManager, didRemove attachment: AttachmentManager.Attachment, at index: Int) 56 | 57 | /// Notifys when the AttachmentManager was reloaded 58 | /// 59 | /// - Parameters: 60 | /// - manager: The AttachmentManager 61 | /// - attachments: The AttachmentManager's attachments array 62 | func attachmentManager(_ manager: AttachmentManager, didReloadTo attachments: [AttachmentManager.Attachment]) 63 | 64 | /// Notifys when the AddAttachmentCell was selected 65 | /// 66 | /// - Parameters: 67 | /// - manager: The AttachmentManager 68 | /// - attachments: The index of the AddAttachmentCell 69 | func attachmentManager(_ manager: AttachmentManager, didSelectAddAttachmentAt index: Int) 70 | } 71 | 72 | public extension AttachmentManagerDelegate { 73 | 74 | func attachmentManager(_ manager: AttachmentManager, didInsert attachment: AttachmentManager.Attachment, at index: Int) {} 75 | 76 | func attachmentManager(_ manager: AttachmentManager, didRemove attachment: AttachmentManager.Attachment, at index: Int) {} 77 | 78 | func attachmentManager(_ manager: AttachmentManager, didReloadTo attachments: [AttachmentManager.Attachment]) {} 79 | 80 | func attachmentManager(_ manager: AttachmentManager, didSelectAddAttachmentAt index: Int) {} 81 | } 82 | -------------------------------------------------------------------------------- /Sources/Plugins/AttachmentManager/Views/AttachmentCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AttachmentCell.swift 3 | // InputBarAccessoryView 4 | // 5 | // Copyright © 2017-2020 Nathan Tannar. 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // 25 | // Created by Nathan Tannar on 10/4/17. 26 | // 27 | 28 | import UIKit 29 | 30 | open class AttachmentCell: UICollectionViewCell { 31 | 32 | // MARK: - Properties 33 | 34 | open class var reuseIdentifier: String { 35 | return "AttachmentCell" 36 | } 37 | 38 | public let containerView: UIView = { 39 | let view = UIView() 40 | view.translatesAutoresizingMaskIntoConstraints = false 41 | if #available(iOS 13, *) { 42 | view.backgroundColor = .systemGray6 43 | } else { 44 | view.backgroundColor = .groupTableViewBackground 45 | } 46 | view.layer.cornerRadius = 8 47 | view.clipsToBounds = true 48 | return view 49 | }() 50 | 51 | open var padding: UIEdgeInsets = UIEdgeInsets(top: 5, left: 5, bottom: 5, right: 5) { 52 | didSet { 53 | updateContainerPadding() 54 | } 55 | } 56 | 57 | open lazy var deleteButton: UIButton = { [weak self] in 58 | let button = UIButton() 59 | let textColor: UIColor 60 | if #available(iOS 13, *) { 61 | textColor = .systemBackground 62 | } else { 63 | textColor = .white 64 | } 65 | button.setAttributedTitle(NSMutableAttributedString().bold("X", fontSize: 15, textColor: textColor), for: .normal) 66 | button.setAttributedTitle(NSMutableAttributedString().bold("X", fontSize: 15, textColor: textColor.withAlphaComponent(0.5)), for: .highlighted) 67 | button.layer.cornerRadius = 10 68 | button.clipsToBounds = true 69 | button.backgroundColor = .systemBlue 70 | button.addTarget(self, action: #selector(self?.deleteAttachment), for: .touchUpInside) 71 | return button 72 | }() 73 | 74 | open var attachment: AttachmentManager.Attachment? 75 | 76 | open var indexPath: IndexPath? 77 | 78 | open weak var manager: AttachmentManager? 79 | 80 | private var containerViewLayoutSet: NSLayoutConstraintSet? 81 | 82 | // MARK: - Initialization 83 | 84 | public override init(frame: CGRect) { 85 | super.init(frame: frame) 86 | setup() 87 | } 88 | 89 | required public init?(coder aDecoder: NSCoder) { 90 | super.init(coder: aDecoder) 91 | setup() 92 | } 93 | 94 | open override func prepareForReuse() { 95 | super.prepareForReuse() 96 | indexPath = nil 97 | manager = nil 98 | attachment = nil 99 | } 100 | 101 | // MARK: - Setup 102 | 103 | private func setup() { 104 | 105 | setupSubviews() 106 | setupConstraints() 107 | } 108 | 109 | private func setupSubviews() { 110 | 111 | contentView.addSubview(containerView) 112 | contentView.addSubview(deleteButton) 113 | } 114 | 115 | private func setupConstraints() { 116 | 117 | containerViewLayoutSet = NSLayoutConstraintSet( 118 | top: containerView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: padding.top), 119 | bottom: containerView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -padding.bottom), 120 | left: containerView.leftAnchor.constraint(equalTo: contentView.leftAnchor, constant: padding.left), 121 | right: containerView.rightAnchor.constraint(equalTo: contentView.rightAnchor, constant: -padding.right) 122 | ).activate() 123 | deleteButton.addConstraints(contentView.topAnchor, right: contentView.rightAnchor, widthConstant: 20, heightConstant: 20) 124 | } 125 | 126 | private func updateContainerPadding() { 127 | 128 | containerViewLayoutSet?.top?.constant = padding.top 129 | containerViewLayoutSet?.bottom?.constant = -padding.bottom 130 | containerViewLayoutSet?.left?.constant = padding.left 131 | containerViewLayoutSet?.right?.constant = -padding.right 132 | } 133 | 134 | // MARK: - User Actions 135 | 136 | @objc 137 | func deleteAttachment() { 138 | 139 | guard let index = indexPath?.row else { return } 140 | manager?.removeAttachment(at: index) 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /Sources/Plugins/AttachmentManager/Views/AttachmentsView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AttachmentCollectionView.swift 3 | // InputBarAccessoryView 4 | // 5 | // Copyright © 2017-2020 Nathan Tannar. 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // 25 | // Created by Nathan Tannar on 10/4/17. 26 | // 27 | 28 | import UIKit 29 | 30 | @available(*, deprecated, message: "AttachmentsView has been renamed to AttachmentCollectionView") 31 | public typealias AttachmentsView = AttachmentCollectionView 32 | 33 | open class AttachmentCollectionView: UICollectionView { 34 | 35 | // MARK: - Properties 36 | 37 | open var intrinsicContentHeight: CGFloat = 100 { 38 | didSet { 39 | invalidateIntrinsicContentSize() 40 | } 41 | } 42 | 43 | open override var intrinsicContentSize: CGSize { 44 | return CGSize(width: 0, height: intrinsicContentHeight) 45 | } 46 | 47 | // MARK: - Initialization 48 | 49 | public init() { 50 | let layout = UICollectionViewFlowLayout() 51 | layout.scrollDirection = .horizontal 52 | layout.minimumLineSpacing = 0 53 | layout.sectionInset.top = 5 54 | layout.sectionInset.bottom = 5 55 | layout.headerReferenceSize = CGSize(width: 12, height: 0) 56 | layout.footerReferenceSize = CGSize(width: 12, height: 0) 57 | super.init(frame: .zero, collectionViewLayout: layout) 58 | setup() 59 | } 60 | 61 | public override init(frame: CGRect, collectionViewLayout layout: UICollectionViewLayout) { 62 | super.init(frame: frame, collectionViewLayout: layout) 63 | setup() 64 | } 65 | 66 | required public init?(coder aDecoder: NSCoder) { 67 | super.init(coder: aDecoder) 68 | setup() 69 | } 70 | 71 | // MARK: - Setup 72 | 73 | private func setup() { 74 | 75 | if #available(iOS 13, *) { 76 | backgroundColor = .systemBackground 77 | } else { 78 | backgroundColor = .white 79 | } 80 | alwaysBounceHorizontal = true 81 | showsHorizontalScrollIndicator = true 82 | setContentHuggingPriority(UILayoutPriority.defaultHigh, for: .vertical) 83 | register(AttachmentCell.self, forCellWithReuseIdentifier: AttachmentCell.reuseIdentifier) 84 | register(ImageAttachmentCell.self, forCellWithReuseIdentifier: ImageAttachmentCell.reuseIdentifier) 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /Sources/Plugins/AttachmentManager/Views/ImageAttachmentCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImageAttachmentCell.swift 3 | // InputBarAccessoryView 4 | // 5 | // Copyright © 2017-2020 Nathan Tannar. 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // 25 | // Created by Nathan Tannar on 10/6/17. 26 | // 27 | 28 | import UIKit 29 | 30 | open class ImageAttachmentCell: AttachmentCell { 31 | 32 | // MARK: - Properties 33 | 34 | override open class var reuseIdentifier: String { 35 | return "ImageAttachmentCell" 36 | } 37 | 38 | public let imageView: UIImageView = { 39 | let imageView = UIImageView() 40 | imageView.contentMode = .scaleAspectFill 41 | return imageView 42 | }() 43 | 44 | // MARK: - Initialization 45 | 46 | public override init(frame: CGRect) { 47 | super.init(frame: frame) 48 | setup() 49 | } 50 | 51 | required public init?(coder aDecoder: NSCoder) { 52 | super.init(coder: aDecoder) 53 | setup() 54 | } 55 | 56 | open override func prepareForReuse() { 57 | super.prepareForReuse() 58 | imageView.image = nil 59 | } 60 | 61 | // MARK: - Setup 62 | 63 | private func setup() { 64 | containerView.addSubview(imageView) 65 | imageView.fillSuperview() 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /Sources/Plugins/AutocompleteManager/Models/AutocompleteCompletion.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AutocompleteCompletion.swift 3 | // InputBarAccessoryView 4 | // 5 | // Copyright © 2017-2020 Nathan Tannar. 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // 25 | // Created by Nathan Tannar on 10/4/17. 26 | // 27 | 28 | import Foundation 29 | 30 | public struct AutocompleteCompletion { 31 | 32 | // The String to insert/replace upon autocompletion 33 | public let text: String 34 | 35 | // The context of the completion that you may need later when completed 36 | public let context: [String: Any]? 37 | 38 | public init(text: String, context: [String: Any]? = nil) { 39 | self.text = text 40 | self.context = context 41 | } 42 | 43 | @available(*, deprecated, message: "`displayText` should no longer be used, use `context: [String: Any]` instead") 44 | public init(_ text: String, displayText: String) { 45 | self.text = text 46 | self.context = nil 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Sources/Plugins/AutocompleteManager/Models/AutocompleteSession.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AutocompleteSession.swift 3 | // InputBarAccessoryView 4 | // 5 | // Copyright © 2017-2020 Nathan Tannar. 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // 25 | // Created by Nathan Tannar on 10/4/17. 26 | // 27 | 28 | import Foundation 29 | 30 | /// A class containing data on the `AutocompleteManager`'s session 31 | public class AutocompleteSession { 32 | 33 | public let prefix: String 34 | public let range: NSRange 35 | public var filter: String 36 | public var completion: AutocompleteCompletion? 37 | internal var spaceCounter: Int = 0 38 | 39 | public init?(prefix: String?, range: NSRange?, filter: String?) { 40 | guard let pfx = prefix, let rng = range, let flt = filter else { return nil } 41 | self.prefix = pfx 42 | self.range = rng 43 | self.filter = flt 44 | } 45 | } 46 | 47 | extension AutocompleteSession: Equatable { 48 | 49 | public static func == (lhs: AutocompleteSession, rhs: AutocompleteSession) -> Bool { 50 | return lhs.prefix == rhs.prefix && lhs.range == rhs.range 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Sources/Plugins/AutocompleteManager/Protocols/AutocompleteManagerDataSource.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AutocompleteManagerDataSource.swift 3 | // InputBarAccessoryView 4 | // 5 | // Copyright © 2017-2020 Nathan Tannar. 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // 25 | // Created by Nathan Tannar on 10/1/17. 26 | // 27 | 28 | import UIKit 29 | 30 | /// AutocompleteManagerDataSource is a protocol that passes data to the AutocompleteManager 31 | @MainActor 32 | public protocol AutocompleteManagerDataSource: AnyObject { 33 | 34 | /// The autocomplete options for the registered prefix. 35 | /// 36 | /// - Parameters: 37 | /// - manager: The AutocompleteManager 38 | /// - prefix: The registered prefix 39 | /// - Returns: An array of `AutocompleteCompletion` options for the given prefix 40 | func autocompleteManager(_ manager: AutocompleteManager, autocompleteSourceFor prefix: String) -> [AutocompleteCompletion] 41 | 42 | /// The cell to populate the `AutocompleteTableView` with 43 | /// 44 | /// - Parameters: 45 | /// - manager: The `AttachmentManager` that sources the UITableViewDataSource 46 | /// - tableView: The `AttachmentManager`'s `AutocompleteTableView` 47 | /// - indexPath: The `IndexPath` of the cell 48 | /// - session: The current `Session` of the `AutocompleteManager` 49 | /// - Returns: A UITableViewCell to populate the `AutocompleteTableView` 50 | func autocompleteManager(_ manager: AutocompleteManager, tableView: UITableView, cellForRowAt indexPath: IndexPath, for session: AutocompleteSession) -> UITableViewCell 51 | } 52 | 53 | public extension AutocompleteManagerDataSource { 54 | 55 | func autocompleteManager(_ manager: AutocompleteManager, tableView: UITableView, cellForRowAt indexPath: IndexPath, for session: AutocompleteSession) -> UITableViewCell { 56 | 57 | guard let cell = tableView.dequeueReusableCell(withIdentifier: AutocompleteCell.reuseIdentifier, for: indexPath) as? AutocompleteCell else { 58 | fatalError("AutocompleteCell is not registered") 59 | } 60 | 61 | cell.textLabel?.attributedText = manager.attributedText(matching: session, fontSize: 13) 62 | if #available(iOS 13, *) { 63 | cell.backgroundColor = .systemBackground 64 | } else { 65 | cell.backgroundColor = .white 66 | } 67 | cell.separatorLine.isHidden = tableView.numberOfRows(inSection: indexPath.section) - 1 == indexPath.row 68 | return cell 69 | 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /Sources/Plugins/AutocompleteManager/Protocols/AutocompleteManagerDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AutocompleteManagerDelegate.swift 3 | // InputBarAccessoryView 4 | // 5 | // Copyright © 2017-2020 Nathan Tannar. 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // 25 | // Created by Nathan Tannar on 10/4/17. 26 | // 27 | 28 | import UIKit 29 | 30 | /// AutocompleteManagerDelegate is a protocol that more precisely define AutocompleteManager logic 31 | public protocol AutocompleteManagerDelegate: AnyObject { 32 | 33 | /// Can be used to determine if the AutocompleteManager should be inserted into an InputStackView 34 | /// 35 | /// - Parameters: 36 | /// - manager: The AutocompleteManager 37 | /// - shouldBecomeVisible: If the AutocompleteManager should be presented or dismissed 38 | func autocompleteManager(_ manager: AutocompleteManager, shouldBecomeVisible: Bool) 39 | 40 | /// Determines if a prefix character should be registered to initialize the auto-complete selection table 41 | /// 42 | /// - Parameters: 43 | /// - manager: The AutocompleteManager 44 | /// - prefix: The prefix `Character` could be registered 45 | /// - range: The `NSRange` of the prefix in the UITextView managed by the AutocompleteManager 46 | /// - Returns: If the prefix should be registered. Default is TRUE 47 | func autocompleteManager(_ manager: AutocompleteManager, shouldRegister prefix: String, at range: NSRange) -> Bool 48 | 49 | /// Determines if a prefix character should be unregistered to de-initialize the auto-complete selection table 50 | /// 51 | /// - Parameters: 52 | /// - manager: The AutocompleteManager 53 | /// - prefix: The prefix character could be unregistered 54 | /// - range: The range of the prefix in the UITextView managed by the AutocompleteManager 55 | /// - Returns: If the prefix should be unregistered. Default is TRUE 56 | func autocompleteManager(_ manager: AutocompleteManager, shouldUnregister prefix: String) -> Bool 57 | 58 | /// Determines if a prefix character can should be autocompleted 59 | /// 60 | /// - Parameters: 61 | /// - manager: The AutocompleteManager 62 | /// - prefix: The prefix character that is currently registered 63 | /// - text: The text to autocomplete with 64 | /// - Returns: If the prefix can be autocompleted. Default is TRUE 65 | func autocompleteManager(_ manager: AutocompleteManager, shouldComplete prefix: String, with text: String) -> Bool 66 | } 67 | 68 | public extension AutocompleteManagerDelegate { 69 | 70 | func autocompleteManager(_ manager: AutocompleteManager, shouldRegister prefix: String, at range: NSRange) -> Bool { 71 | return true 72 | } 73 | 74 | func autocompleteManager(_ manager: AutocompleteManager, shouldUnregister prefix: String) -> Bool { 75 | return true 76 | } 77 | 78 | func autocompleteManager(_ manager: AutocompleteManager, shouldComplete prefix: String, with text: String) -> Bool { 79 | return true 80 | } 81 | } 82 | 83 | -------------------------------------------------------------------------------- /Sources/Plugins/AutocompleteManager/Views/AutocompleteCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AutocompleteCell.swift 3 | // InputBarAccessoryView 4 | // 5 | // Copyright © 2017-2020 Nathan Tannar. 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // 25 | // Created by Nathan Tannar on 10/4/17. 26 | // 27 | 28 | import UIKit 29 | 30 | open class AutocompleteCell: UITableViewCell { 31 | 32 | // MARK: - Properties 33 | 34 | open class var reuseIdentifier: String { 35 | return "AutocompleteCell" 36 | } 37 | 38 | /// A boarder line anchored to the top of the view 39 | public let separatorLine = SeparatorLine() 40 | 41 | open var imageViewEdgeInsets: UIEdgeInsets = .zero { didSet { setNeedsLayout() } } 42 | 43 | // MARK: - Initialization 44 | 45 | override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { 46 | super.init(style: .subtitle, reuseIdentifier: reuseIdentifier) 47 | setup() 48 | } 49 | 50 | required public init?(coder aDecoder: NSCoder) { 51 | super.init(coder: aDecoder) 52 | setup() 53 | } 54 | 55 | open override func prepareForReuse() { 56 | super.prepareForReuse() 57 | textLabel?.text = nil 58 | detailTextLabel?.text = nil 59 | imageView?.image = nil 60 | imageViewEdgeInsets = .zero 61 | if #available(iOS 13, *) { 62 | separatorLine.backgroundColor = .systemGray2 63 | } else { 64 | separatorLine.backgroundColor = .lightGray 65 | } 66 | separatorLine.isHidden = false 67 | } 68 | 69 | // MARK: - Setup 70 | 71 | private func setup() { 72 | 73 | setupSubviews() 74 | setupConstraints() 75 | } 76 | 77 | open func setupSubviews() { 78 | 79 | addSubview(separatorLine) 80 | } 81 | 82 | open func setupConstraints() { 83 | 84 | separatorLine.addConstraints(left: leftAnchor, bottom: bottomAnchor, right: rightAnchor, heightConstant: 0.5) 85 | } 86 | 87 | open override func layoutSubviews() { 88 | super.layoutSubviews() 89 | guard let imageViewFrame = imageView?.frame else { return } 90 | let imageViewOrigin = CGPoint(x: imageViewFrame.origin.x + imageViewEdgeInsets.left, y: imageViewFrame.origin.y + imageViewEdgeInsets.top) 91 | let imageViewSize = CGSize(width: imageViewFrame.size.width - imageViewEdgeInsets.left - imageViewEdgeInsets.right, height: imageViewFrame.size.height - imageViewEdgeInsets.top - imageViewEdgeInsets.bottom) 92 | imageView?.frame = CGRect(origin: imageViewOrigin, size: imageViewSize) 93 | } 94 | 95 | // MARK: - API [Public] 96 | 97 | @available(*, deprecated, message: "This function has been moved to the `AutocompleteManager`") 98 | open func attributedText(matching session: AutocompleteSession) -> NSMutableAttributedString { 99 | fatalError("Please use `func attributedText(matching:, fontSize:)` implemented in the `AutocompleteManager`") 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /Sources/Plugins/AutocompleteManager/Views/AutocompleteTableView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AutocompleteTableView.swift 3 | // InputBarAccessoryView 4 | // 5 | // Copyright © 2017-2020 Nathan Tannar. 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // 25 | // Created by Nathan Tannar on 10/4/17. 26 | // 27 | 28 | import UIKit 29 | 30 | open class AutocompleteTableView: UITableView { 31 | 32 | /// The max visible rows visible in the autocomplete table before the user has to scroll throught them 33 | open var maxVisibleRows = 3 { didSet { invalidateIntrinsicContentSize() } } 34 | 35 | open override var intrinsicContentSize: CGSize { 36 | 37 | let rows = numberOfRows(inSection: 0) < maxVisibleRows ? numberOfRows(inSection: 0) : maxVisibleRows 38 | return CGSize(width: super.intrinsicContentSize.width, height: (CGFloat(rows) * rowHeight)) 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /Sources/Protocols/InputBarAccessoryViewDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // InputBarAccessoryViewDelegate.swift 3 | // InputBarAccessoryView 4 | // 5 | // Copyright © 2017-2020 Nathan Tannar. 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // 25 | // Created by Nathan Tannar on 8/18/17. 26 | // 27 | 28 | import Foundation 29 | import UIKit 30 | 31 | /// InputBarAccessoryViewDelegate is a protocol that can recieve notifications from the InputBarAccessoryView 32 | @MainActor 33 | public protocol InputBarAccessoryViewDelegate: AnyObject { 34 | 35 | /// Called when the default send button has been selected 36 | /// 37 | /// - Parameters: 38 | /// - inputBar: The InputBarAccessoryView 39 | /// - text: The current text in the InputBarAccessoryView's InputTextView 40 | func inputBar(_ inputBar: InputBarAccessoryView, didPressSendButtonWith text: String) 41 | 42 | /// Called when the instrinsicContentSize of the InputBarAccessoryView has changed. Can be used for adjusting content insets 43 | /// on other views to make sure the InputBarAccessoryView does not cover up any other view 44 | /// 45 | /// - Parameters: 46 | /// - inputBar: The InputBarAccessoryView 47 | /// - size: The new instrinsicContentSize 48 | func inputBar(_ inputBar: InputBarAccessoryView, didChangeIntrinsicContentTo size: CGSize) 49 | 50 | /// Called when the InputBarAccessoryView's InputTextView's text has changed. Useful for adding your own logic without the 51 | /// need of assigning a delegate or notification 52 | /// 53 | /// - Parameters: 54 | /// - inputBar: The InputBarAccessoryView 55 | /// - text: The current text in the InputBarAccessoryView's InputTextView 56 | func inputBar(_ inputBar: InputBarAccessoryView, textViewTextDidChangeTo text: String) 57 | 58 | /// Called when a swipe gesture was recognized on the InputBarAccessoryView's InputTextView 59 | /// 60 | /// - Parameters: 61 | /// - inputBar: The InputBarAccessoryView 62 | /// - gesture: The gesture that was recognized 63 | func inputBar(_ inputBar: InputBarAccessoryView, didSwipeTextViewWith gesture: UISwipeGestureRecognizer) 64 | } 65 | 66 | public extension InputBarAccessoryViewDelegate { 67 | 68 | func inputBar(_ inputBar: InputBarAccessoryView, didPressSendButtonWith text: String) {} 69 | 70 | func inputBar(_ inputBar: InputBarAccessoryView, didChangeIntrinsicContentTo size: CGSize) {} 71 | 72 | func inputBar(_ inputBar: InputBarAccessoryView, textViewTextDidChangeTo text: String) {} 73 | 74 | func inputBar(_ inputBar: InputBarAccessoryView, didSwipeTextViewWith gesture: UISwipeGestureRecognizer) {} 75 | } 76 | -------------------------------------------------------------------------------- /Sources/Protocols/InputItem.swift: -------------------------------------------------------------------------------- 1 | // 2 | // InputItem.swift 3 | // InputBarAccessoryView 4 | // 5 | // Copyright © 2017-2020 Nathan Tannar. 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // 25 | // Copyright © 2017-2020 Nathan Tannar. All rights reserved. 26 | // 27 | 28 | import UIKit 29 | 30 | /// InputItem is a protocol that links elements to the InputBarAccessoryView to make them reactive 31 | @MainActor 32 | public protocol InputItem: AnyObject { 33 | 34 | /// A weak reference to the InputBarAccessoryView. Set when inserted into an InputStackView 35 | var inputBarAccessoryView: InputBarAccessoryView? { get set } 36 | 37 | /// A reference to the InputStackView that the InputItem is contained in. Set when inserted into an InputStackView 38 | var parentStackViewPosition: InputStackView.Position? { get set } 39 | 40 | /// A hook that is called when the InputTextView's text is changed 41 | func textViewDidChangeAction(with textView: InputTextView) 42 | 43 | /// A hook that is called when the InputBarAccessoryView's InputTextView receieves a swipe gesture 44 | func keyboardSwipeGestureAction(with gesture: UISwipeGestureRecognizer) 45 | 46 | /// A hook that is called when the InputTextView is resigned as the first responder 47 | func keyboardEditingEndsAction() 48 | 49 | /// A hook that is called when the InputTextView is made the first responder 50 | func keyboardEditingBeginsAction() 51 | } 52 | -------------------------------------------------------------------------------- /Sources/Protocols/InputPlugin.swift: -------------------------------------------------------------------------------- 1 | // 2 | // InputPlugin.swift 3 | // InputBarAccessoryView 4 | // 5 | // Copyright © 2017-2020 Nathan Tannar. 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // 25 | // Copyright © 2017-2020 Nathan Tannar. All rights reserved. 26 | // 27 | 28 | import UIKit 29 | 30 | /// `InputPlugin` is a protocol that makes integrating plugins to the `InputBarAccessoryView` easy. 31 | @MainActor 32 | public protocol InputPlugin: AnyObject { 33 | 34 | /// Should reload the state if the `InputPlugin` 35 | func reloadData() 36 | 37 | /// Should remove any content that the `InputPlugin` is managing 38 | func invalidate() 39 | 40 | /// Should handle the input of data types that an `InputPlugin` manages 41 | /// 42 | /// - Parameter object: The object to input 43 | func handleInput(of object: AnyObject) -> Bool 44 | } 45 | -------------------------------------------------------------------------------- /Sources/Supporting/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | $(MARKETING_VERSION) 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSPrincipalClass 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /Sources/Supporting/InputBarAccessoryView+Availability.swift: -------------------------------------------------------------------------------- 1 | // 2 | // InputBarAccessoryView+Availability.swift 3 | // InputBarAccessoryView 4 | // 5 | // Copyright © 2017-2020 Nathan Tannar. 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // 25 | // Created by Nathan Tannar on 2/12/17. 26 | // 27 | 28 | import UIKit 29 | 30 | @available(*, deprecated, message: "InputManager has been renamed to InputPlugin") 31 | public typealias InputManager = InputPlugin 32 | 33 | extension InputPlugin { 34 | 35 | @available(*, deprecated, message: "`handleInput(object:)` should return a `Bool` if handle was successful or now") 36 | func handleInput(of object: AnyObject) { 37 | _ = self.handleInput(of: object) 38 | } 39 | } 40 | 41 | extension AutocompleteCompletion { 42 | 43 | // An optional string to display instead of `text`, for example emojis 44 | @available(*, deprecated, message: "`displayText` should no longer be used, use `context: [String: Any]` instead") 45 | public var displayText: String? { 46 | return text 47 | } 48 | } 49 | 50 | extension AutocompleteManager { 51 | /// If the autocomplete matches should be made by casting the strings to lowercase. 52 | /// Default value is `FALSE` 53 | /// DEPRICATED; will always return `FALSE` 54 | @available(*, deprecated, message: "`isCaseSensitive` was replaced in favour of a more customizable `filterBlock: (String) -> (Bool)`") 55 | public var isCaseSensitive: Bool { 56 | get { return false } 57 | set { 58 | if newValue { 59 | filterBlock = { session, completion in 60 | completion.text.contains(session.filter) 61 | } 62 | } else { 63 | filterBlock = { session, completion in 64 | completion.text.lowercased().contains(session.filter.lowercased()) 65 | } 66 | } 67 | } 68 | } 69 | } 70 | 71 | extension InputBarAccessoryView { 72 | 73 | /** 74 | The anchor constants used by the InputStackView 75 | 76 | ```` 77 | V:|...-(padding.top)-(textViewPadding.top)-[InputTextView]-(textViewPadding.bottom)-[InputStackView.bottom]-...| 78 | 79 | H:|...-[InputStackView.left]-(textViewPadding.left)-[InputTextView]-(textViewPadding.right)-[InputStackView.right]-...| 80 | ```` 81 | 82 | */ 83 | @available(*, deprecated, message: "The `InputTextView` now resides in the `middleContentView` and thus this property has been renamed to `middleContentViewPadding`") 84 | public var textViewPadding: UIEdgeInsets { 85 | get { 86 | return middleContentViewPadding 87 | } 88 | set { 89 | middleContentViewPadding = newValue 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /Sources/Supporting/InputBarAccessoryView-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | // 2 | // InputBarAccessoryView.h 3 | // InputBarAccessoryView 4 | // 5 | // Created by Nathan Tannar on 8/18/17. 6 | // Copyright © 2017-2020 Nathan Tannar. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for InputBarAccessoryView. 12 | FOUNDATION_EXPORT double InputBarAccessoryViewVersionNumber; 13 | 14 | //! Project version string for InputBarAccessoryView. 15 | FOUNDATION_EXPORT const unsigned char InputBarAccessoryViewVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | 20 | -------------------------------------------------------------------------------- /Sources/ViewControllers/InputBarViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // InputBarViewController.swift 3 | // InputBarAccessoryView 4 | // 5 | // Copyright © 2017-2020 Nathan Tannar. 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // 25 | // Created by Nathan Tannar on 9/13/18. 26 | // 27 | 28 | import UIKit 29 | 30 | /// An simple `UIViewController` subclass that is ready to work 31 | /// with an `inputAccessoryView` 32 | open class InputBarViewController: UIViewController, InputBarAccessoryViewDelegate { 33 | 34 | /// A powerful InputAccessoryView ideal for messaging applications 35 | public let inputBar = InputBarAccessoryView() 36 | 37 | /// A boolean value that when changed will update the `inputAccessoryView` 38 | /// of the `InputBarViewController`. When set to `TRUE`, the 39 | /// `inputAccessoryView` is set to `nil` and the `inputBar` slides off 40 | /// the screen. 41 | /// 42 | /// The default value is FALSE 43 | open var isInputBarHidden: Bool = false { 44 | didSet { 45 | isInputBarHiddenDidChange() 46 | } 47 | } 48 | 49 | open override var inputAccessoryView: UIView? { 50 | return isInputBarHidden ? nil : inputBar 51 | } 52 | 53 | open override var canBecomeFirstResponder: Bool { 54 | return !isInputBarHidden 55 | } 56 | 57 | open override func viewDidLoad() { 58 | super.viewDidLoad() 59 | inputBar.delegate = self 60 | } 61 | 62 | /// Invoked when `isInputBarHidden` changes to become or 63 | /// resign first responder 64 | open func isInputBarHiddenDidChange() { 65 | if isInputBarHidden, isFirstResponder { 66 | resignFirstResponder() 67 | } else if !isFirstResponder { 68 | becomeFirstResponder() 69 | } 70 | } 71 | 72 | @discardableResult 73 | open override func resignFirstResponder() -> Bool { 74 | inputBar.inputTextView.resignFirstResponder() 75 | return super.resignFirstResponder() 76 | } 77 | 78 | // MARK: - InputBarAccessoryViewDelegate 79 | 80 | open func inputBar(_ inputBar: InputBarAccessoryView, didPressSendButtonWith text: String) { } 81 | 82 | open func inputBar(_ inputBar: InputBarAccessoryView, textViewTextDidChangeTo text: String) { } 83 | 84 | open func inputBar(_ inputBar: InputBarAccessoryView, didChangeIntrinsicContentTo size: CGSize) { } 85 | 86 | open func inputBar(_ inputBar: InputBarAccessoryView, didSwipeTextViewWith gesture: UISwipeGestureRecognizer) { } 87 | } 88 | 89 | -------------------------------------------------------------------------------- /Sources/Views/InputStackView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // InputStackView.swift 3 | // InputBarAccessoryView 4 | // 5 | // Copyright © 2017-2020 Nathan Tannar. 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // 25 | // Created by Nathan Tannar on 10/6/17. 26 | // 27 | 28 | import UIKit 29 | 30 | /** 31 | A UIStackView that's intended for holding `InputItem`s 32 | 33 | ## Important Notes ## 34 | 1. Default alignment is .fill 35 | 2. Default distribution is .fill 36 | 3. The distribution property needs to be based on its arranged subviews intrinsicContentSize so it is not recommended to change it 37 | */ 38 | open class InputStackView: UIStackView { 39 | 40 | /// The stack view position in the InputBarAccessoryView 41 | /// 42 | /// - left: Left Stack View 43 | /// - right: Bottom Stack View 44 | /// - bottom: Left Stack View 45 | /// - top: Top Stack View 46 | public enum Position { 47 | case left, right, bottom, top 48 | } 49 | 50 | // MARK: Initialization 51 | 52 | convenience init(axis: NSLayoutConstraint.Axis, spacing: CGFloat) { 53 | self.init(frame: .zero) 54 | self.axis = axis 55 | self.spacing = spacing 56 | } 57 | 58 | public override init(frame: CGRect) { 59 | super.init(frame: frame) 60 | setup() 61 | } 62 | 63 | required public init(coder: NSCoder) { 64 | super.init(coder: coder) 65 | } 66 | 67 | // MARK: - Setup 68 | 69 | /// Sets up the default properties 70 | open func setup() { 71 | translatesAutoresizingMaskIntoConstraints = false 72 | distribution = .fill 73 | alignment = .bottom 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /Sources/Views/SeparatorLine.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SeparatorLine.swift 3 | // InputBarAccessoryView 4 | // 5 | // Copyright © 2017-2020 Nathan Tannar. 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // 25 | // Created by Nathan Tannar on 10/4/17. 26 | // 27 | 28 | import UIKit 29 | 30 | /** 31 | A UIView thats intrinsicContentSize is overrided so an exact height can be specified 32 | 33 | ## Important Notes ## 34 | 1. Default height is 1 pixel 35 | 2. Default backgroundColor is UIColor.lightGray 36 | 3. Intended to be used in an `InputStackView` 37 | */ 38 | open class SeparatorLine: UIView { 39 | 40 | // MARK: - Properties 41 | 42 | /// The height of the line 43 | open var height: CGFloat = 1.0 / UIScreen.main.scale { 44 | didSet { 45 | constraints.filter { $0.identifier == "height" }.forEach { $0.constant = height } // Assumes constraint was given an identifier 46 | invalidateIntrinsicContentSize() 47 | } 48 | } 49 | 50 | open override var intrinsicContentSize: CGSize { 51 | return CGSize(width: super.intrinsicContentSize.width, height: height) 52 | } 53 | 54 | // MARK: - Initialization 55 | 56 | public override init(frame: CGRect) { 57 | super.init(frame: frame) 58 | setup() 59 | } 60 | 61 | required public init?(coder aDecoder: NSCoder) { 62 | super.init(coder: aDecoder) 63 | setup() 64 | } 65 | 66 | /// Sets up the default properties 67 | open func setup() { 68 | if #available(iOS 13, *) { 69 | backgroundColor = .systemGray2 70 | } else { 71 | backgroundColor = .lightGray 72 | } 73 | translatesAutoresizingMaskIntoConstraints = false 74 | setContentHuggingPriority(.defaultHigh, for: .vertical) 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /docs/_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-minimal -------------------------------------------------------------------------------- /docs/badge.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | documentation 17 | 18 | 19 | documentation 20 | 21 | 22 | 100% 23 | 24 | 25 | 100% 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /docs/css/highlight.css: -------------------------------------------------------------------------------- 1 | /*! Jazzy - https://github.com/realm/jazzy 2 | * Copyright Realm Inc. 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | /* Credit to https://gist.github.com/wataru420/2048287 */ 6 | .highlight .c { 7 | color: #999988; 8 | font-style: italic; } 9 | 10 | .highlight .err { 11 | color: #a61717; 12 | background-color: #e3d2d2; } 13 | 14 | .highlight .k { 15 | color: #000000; 16 | font-weight: bold; } 17 | 18 | .highlight .o { 19 | color: #000000; 20 | font-weight: bold; } 21 | 22 | .highlight .cm { 23 | color: #999988; 24 | font-style: italic; } 25 | 26 | .highlight .cp { 27 | color: #999999; 28 | font-weight: bold; } 29 | 30 | .highlight .c1 { 31 | color: #999988; 32 | font-style: italic; } 33 | 34 | .highlight .cs { 35 | color: #999999; 36 | font-weight: bold; 37 | font-style: italic; } 38 | 39 | .highlight .gd { 40 | color: #000000; 41 | background-color: #ffdddd; } 42 | 43 | .highlight .gd .x { 44 | color: #000000; 45 | background-color: #ffaaaa; } 46 | 47 | .highlight .ge { 48 | color: #000000; 49 | font-style: italic; } 50 | 51 | .highlight .gr { 52 | color: #aa0000; } 53 | 54 | .highlight .gh { 55 | color: #999999; } 56 | 57 | .highlight .gi { 58 | color: #000000; 59 | background-color: #ddffdd; } 60 | 61 | .highlight .gi .x { 62 | color: #000000; 63 | background-color: #aaffaa; } 64 | 65 | .highlight .go { 66 | color: #888888; } 67 | 68 | .highlight .gp { 69 | color: #555555; } 70 | 71 | .highlight .gs { 72 | font-weight: bold; } 73 | 74 | .highlight .gu { 75 | color: #aaaaaa; } 76 | 77 | .highlight .gt { 78 | color: #aa0000; } 79 | 80 | .highlight .kc { 81 | color: #000000; 82 | font-weight: bold; } 83 | 84 | .highlight .kd { 85 | color: #000000; 86 | font-weight: bold; } 87 | 88 | .highlight .kp { 89 | color: #000000; 90 | font-weight: bold; } 91 | 92 | .highlight .kr { 93 | color: #000000; 94 | font-weight: bold; } 95 | 96 | .highlight .kt { 97 | color: #445588; } 98 | 99 | .highlight .m { 100 | color: #009999; } 101 | 102 | .highlight .s { 103 | color: #d14; } 104 | 105 | .highlight .na { 106 | color: #008080; } 107 | 108 | .highlight .nb { 109 | color: #0086B3; } 110 | 111 | .highlight .nc { 112 | color: #445588; 113 | font-weight: bold; } 114 | 115 | .highlight .no { 116 | color: #008080; } 117 | 118 | .highlight .ni { 119 | color: #800080; } 120 | 121 | .highlight .ne { 122 | color: #990000; 123 | font-weight: bold; } 124 | 125 | .highlight .nf { 126 | color: #990000; } 127 | 128 | .highlight .nn { 129 | color: #555555; } 130 | 131 | .highlight .nt { 132 | color: #000080; } 133 | 134 | .highlight .nv { 135 | color: #008080; } 136 | 137 | .highlight .ow { 138 | color: #000000; 139 | font-weight: bold; } 140 | 141 | .highlight .w { 142 | color: #bbbbbb; } 143 | 144 | .highlight .mf { 145 | color: #009999; } 146 | 147 | .highlight .mh { 148 | color: #009999; } 149 | 150 | .highlight .mi { 151 | color: #009999; } 152 | 153 | .highlight .mo { 154 | color: #009999; } 155 | 156 | .highlight .sb { 157 | color: #d14; } 158 | 159 | .highlight .sc { 160 | color: #d14; } 161 | 162 | .highlight .sd { 163 | color: #d14; } 164 | 165 | .highlight .s2 { 166 | color: #d14; } 167 | 168 | .highlight .se { 169 | color: #d14; } 170 | 171 | .highlight .sh { 172 | color: #d14; } 173 | 174 | .highlight .si { 175 | color: #d14; } 176 | 177 | .highlight .sx { 178 | color: #d14; } 179 | 180 | .highlight .sr { 181 | color: #009926; } 182 | 183 | .highlight .s1 { 184 | color: #d14; } 185 | 186 | .highlight .ss { 187 | color: #990073; } 188 | 189 | .highlight .bp { 190 | color: #999999; } 191 | 192 | .highlight .vc { 193 | color: #008080; } 194 | 195 | .highlight .vg { 196 | color: #008080; } 197 | 198 | .highlight .vi { 199 | color: #008080; } 200 | 201 | .highlight .il { 202 | color: #009999; } 203 | -------------------------------------------------------------------------------- /docs/docsets/.docset/Contents/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleIdentifier 6 | com.jazzy. 7 | CFBundleName 8 | 9 | DocSetPlatformFamily 10 | 11 | isDashDocset 12 | 13 | dashIndexFilePath 14 | index.html 15 | isJavaScriptEnabled 16 | 17 | DashDocSetFamily 18 | dashtoc 19 | 20 | 21 | -------------------------------------------------------------------------------- /docs/docsets/.docset/Contents/Resources/Documents/_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-minimal -------------------------------------------------------------------------------- /docs/docsets/.docset/Contents/Resources/Documents/badge.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | documentation 17 | 18 | 19 | documentation 20 | 21 | 22 | 65% 23 | 24 | 25 | 65% 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /docs/docsets/.docset/Contents/Resources/Documents/css/highlight.css: -------------------------------------------------------------------------------- 1 | /*! Jazzy - https://github.com/realm/jazzy 2 | * Copyright Realm Inc. 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | /* Credit to https://gist.github.com/wataru420/2048287 */ 6 | .highlight .c { 7 | color: #999988; 8 | font-style: italic; } 9 | 10 | .highlight .err { 11 | color: #a61717; 12 | background-color: #e3d2d2; } 13 | 14 | .highlight .k { 15 | color: #000000; 16 | font-weight: bold; } 17 | 18 | .highlight .o { 19 | color: #000000; 20 | font-weight: bold; } 21 | 22 | .highlight .cm { 23 | color: #999988; 24 | font-style: italic; } 25 | 26 | .highlight .cp { 27 | color: #999999; 28 | font-weight: bold; } 29 | 30 | .highlight .c1 { 31 | color: #999988; 32 | font-style: italic; } 33 | 34 | .highlight .cs { 35 | color: #999999; 36 | font-weight: bold; 37 | font-style: italic; } 38 | 39 | .highlight .gd { 40 | color: #000000; 41 | background-color: #ffdddd; } 42 | 43 | .highlight .gd .x { 44 | color: #000000; 45 | background-color: #ffaaaa; } 46 | 47 | .highlight .ge { 48 | color: #000000; 49 | font-style: italic; } 50 | 51 | .highlight .gr { 52 | color: #aa0000; } 53 | 54 | .highlight .gh { 55 | color: #999999; } 56 | 57 | .highlight .gi { 58 | color: #000000; 59 | background-color: #ddffdd; } 60 | 61 | .highlight .gi .x { 62 | color: #000000; 63 | background-color: #aaffaa; } 64 | 65 | .highlight .go { 66 | color: #888888; } 67 | 68 | .highlight .gp { 69 | color: #555555; } 70 | 71 | .highlight .gs { 72 | font-weight: bold; } 73 | 74 | .highlight .gu { 75 | color: #aaaaaa; } 76 | 77 | .highlight .gt { 78 | color: #aa0000; } 79 | 80 | .highlight .kc { 81 | color: #000000; 82 | font-weight: bold; } 83 | 84 | .highlight .kd { 85 | color: #000000; 86 | font-weight: bold; } 87 | 88 | .highlight .kp { 89 | color: #000000; 90 | font-weight: bold; } 91 | 92 | .highlight .kr { 93 | color: #000000; 94 | font-weight: bold; } 95 | 96 | .highlight .kt { 97 | color: #445588; } 98 | 99 | .highlight .m { 100 | color: #009999; } 101 | 102 | .highlight .s { 103 | color: #d14; } 104 | 105 | .highlight .na { 106 | color: #008080; } 107 | 108 | .highlight .nb { 109 | color: #0086B3; } 110 | 111 | .highlight .nc { 112 | color: #445588; 113 | font-weight: bold; } 114 | 115 | .highlight .no { 116 | color: #008080; } 117 | 118 | .highlight .ni { 119 | color: #800080; } 120 | 121 | .highlight .ne { 122 | color: #990000; 123 | font-weight: bold; } 124 | 125 | .highlight .nf { 126 | color: #990000; } 127 | 128 | .highlight .nn { 129 | color: #555555; } 130 | 131 | .highlight .nt { 132 | color: #000080; } 133 | 134 | .highlight .nv { 135 | color: #008080; } 136 | 137 | .highlight .ow { 138 | color: #000000; 139 | font-weight: bold; } 140 | 141 | .highlight .w { 142 | color: #bbbbbb; } 143 | 144 | .highlight .mf { 145 | color: #009999; } 146 | 147 | .highlight .mh { 148 | color: #009999; } 149 | 150 | .highlight .mi { 151 | color: #009999; } 152 | 153 | .highlight .mo { 154 | color: #009999; } 155 | 156 | .highlight .sb { 157 | color: #d14; } 158 | 159 | .highlight .sc { 160 | color: #d14; } 161 | 162 | .highlight .sd { 163 | color: #d14; } 164 | 165 | .highlight .s2 { 166 | color: #d14; } 167 | 168 | .highlight .se { 169 | color: #d14; } 170 | 171 | .highlight .sh { 172 | color: #d14; } 173 | 174 | .highlight .si { 175 | color: #d14; } 176 | 177 | .highlight .sx { 178 | color: #d14; } 179 | 180 | .highlight .sr { 181 | color: #009926; } 182 | 183 | .highlight .s1 { 184 | color: #d14; } 185 | 186 | .highlight .ss { 187 | color: #990073; } 188 | 189 | .highlight .bp { 190 | color: #999999; } 191 | 192 | .highlight .vc { 193 | color: #008080; } 194 | 195 | .highlight .vg { 196 | color: #008080; } 197 | 198 | .highlight .vi { 199 | color: #008080; } 200 | 201 | .highlight .il { 202 | color: #009999; } 203 | -------------------------------------------------------------------------------- /docs/docsets/.docset/Contents/Resources/Documents/img/carat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nathantannar4/InputBarAccessoryView/dfdc822b0d4a0d1b5777100347c399069e9bfc62/docs/docsets/.docset/Contents/Resources/Documents/img/carat.png -------------------------------------------------------------------------------- /docs/docsets/.docset/Contents/Resources/Documents/img/dash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nathantannar4/InputBarAccessoryView/dfdc822b0d4a0d1b5777100347c399069e9bfc62/docs/docsets/.docset/Contents/Resources/Documents/img/dash.png -------------------------------------------------------------------------------- /docs/docsets/.docset/Contents/Resources/Documents/img/gh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nathantannar4/InputBarAccessoryView/dfdc822b0d4a0d1b5777100347c399069e9bfc62/docs/docsets/.docset/Contents/Resources/Documents/img/gh.png -------------------------------------------------------------------------------- /docs/docsets/.docset/Contents/Resources/Documents/img/spinner.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nathantannar4/InputBarAccessoryView/dfdc822b0d4a0d1b5777100347c399069e9bfc62/docs/docsets/.docset/Contents/Resources/Documents/img/spinner.gif -------------------------------------------------------------------------------- /docs/docsets/.docset/Contents/Resources/Documents/js/jazzy.js: -------------------------------------------------------------------------------- 1 | // Jazzy - https://github.com/realm/jazzy 2 | // Copyright Realm Inc. 3 | // SPDX-License-Identifier: MIT 4 | 5 | window.jazzy = {'docset': false} 6 | if (typeof window.dash != 'undefined') { 7 | document.documentElement.className += ' dash' 8 | window.jazzy.docset = true 9 | } 10 | if (navigator.userAgent.match(/xcode/i)) { 11 | document.documentElement.className += ' xcode' 12 | window.jazzy.docset = true 13 | } 14 | 15 | function toggleItem($link, $content) { 16 | var animationDuration = 300; 17 | $link.toggleClass('token-open'); 18 | $content.slideToggle(animationDuration); 19 | } 20 | 21 | function itemLinkToContent($link) { 22 | return $link.parent().parent().next(); 23 | } 24 | 25 | // On doc load + hash-change, open any targetted item 26 | function openCurrentItemIfClosed() { 27 | if (window.jazzy.docset) { 28 | return; 29 | } 30 | var $link = $(`a[name="${location.hash.substring(1)}"]`).nextAll('.token'); 31 | $content = itemLinkToContent($link); 32 | if ($content.is(':hidden')) { 33 | toggleItem($link, $content); 34 | } 35 | } 36 | 37 | $(openCurrentItemIfClosed); 38 | $(window).on('hashchange', openCurrentItemIfClosed); 39 | 40 | // On item link ('token') click, toggle its discussion 41 | $('.token').on('click', function(event) { 42 | if (window.jazzy.docset) { 43 | return; 44 | } 45 | var $link = $(this); 46 | toggleItem($link, itemLinkToContent($link)); 47 | 48 | // Keeps the document from jumping to the hash. 49 | var href = $link.attr('href'); 50 | if (history.pushState) { 51 | history.pushState({}, '', href); 52 | } else { 53 | location.hash = href; 54 | } 55 | event.preventDefault(); 56 | }); 57 | 58 | // Clicks on links to the current, closed, item need to open the item 59 | $("a:not('.token')").on('click', function() { 60 | if (location == this.href) { 61 | openCurrentItemIfClosed(); 62 | } 63 | }); 64 | 65 | // KaTeX rendering 66 | if ("katex" in window) { 67 | $($('.math').each( (_, element) => { 68 | katex.render(element.textContent, element, { 69 | displayMode: $(element).hasClass('m-block'), 70 | throwOnError: false, 71 | trust: true 72 | }); 73 | })) 74 | } 75 | -------------------------------------------------------------------------------- /docs/docsets/.docset/Contents/Resources/Documents/js/jazzy.search.js: -------------------------------------------------------------------------------- 1 | // Jazzy - https://github.com/realm/jazzy 2 | // Copyright Realm Inc. 3 | // SPDX-License-Identifier: MIT 4 | 5 | $(function(){ 6 | var $typeahead = $('[data-typeahead]'); 7 | var $form = $typeahead.parents('form'); 8 | var searchURL = $form.attr('action'); 9 | 10 | function displayTemplate(result) { 11 | return result.name; 12 | } 13 | 14 | function suggestionTemplate(result) { 15 | var t = '
'; 16 | t += '' + result.name + ''; 17 | if (result.parent_name) { 18 | t += '' + result.parent_name + ''; 19 | } 20 | t += '
'; 21 | return t; 22 | } 23 | 24 | $typeahead.one('focus', function() { 25 | $form.addClass('loading'); 26 | 27 | $.getJSON(searchURL).then(function(searchData) { 28 | const searchIndex = lunr(function() { 29 | this.ref('url'); 30 | this.field('name'); 31 | this.field('abstract'); 32 | for (const [url, doc] of Object.entries(searchData)) { 33 | this.add({url: url, name: doc.name, abstract: doc.abstract}); 34 | } 35 | }); 36 | 37 | $typeahead.typeahead( 38 | { 39 | highlight: true, 40 | minLength: 3, 41 | autoselect: true 42 | }, 43 | { 44 | limit: 10, 45 | display: displayTemplate, 46 | templates: { suggestion: suggestionTemplate }, 47 | source: function(query, sync) { 48 | const lcSearch = query.toLowerCase(); 49 | const results = searchIndex.query(function(q) { 50 | q.term(lcSearch, { boost: 100 }); 51 | q.term(lcSearch, { 52 | boost: 10, 53 | wildcard: lunr.Query.wildcard.TRAILING 54 | }); 55 | }).map(function(result) { 56 | var doc = searchData[result.ref]; 57 | doc.url = result.ref; 58 | return doc; 59 | }); 60 | sync(results); 61 | } 62 | } 63 | ); 64 | $form.removeClass('loading'); 65 | $typeahead.trigger('focus'); 66 | }); 67 | }); 68 | 69 | var baseURL = searchURL.slice(0, -"search.json".length); 70 | 71 | $typeahead.on('typeahead:select', function(e, result) { 72 | window.location = baseURL + result.url; 73 | }); 74 | }); 75 | -------------------------------------------------------------------------------- /docs/docsets/.docset/Contents/Resources/docSet.dsidx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nathantannar4/InputBarAccessoryView/dfdc822b0d4a0d1b5777100347c399069e9bfc62/docs/docsets/.docset/Contents/Resources/docSet.dsidx -------------------------------------------------------------------------------- /docs/docsets/.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nathantannar4/InputBarAccessoryView/dfdc822b0d4a0d1b5777100347c399069e9bfc62/docs/docsets/.tgz -------------------------------------------------------------------------------- /docs/docsets/InputBarAccessoryView.docset/Contents/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleIdentifier 6 | com.jazzy.inputbaraccessoryview 7 | CFBundleName 8 | InputBarAccessoryView 9 | DocSetPlatformFamily 10 | inputbaraccessoryview 11 | isDashDocset 12 | 13 | dashIndexFilePath 14 | index.html 15 | isJavaScriptEnabled 16 | 17 | DashDocSetFamily 18 | dashtoc 19 | 20 | 21 | -------------------------------------------------------------------------------- /docs/docsets/InputBarAccessoryView.docset/Contents/Resources/Documents/_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-minimal -------------------------------------------------------------------------------- /docs/docsets/InputBarAccessoryView.docset/Contents/Resources/Documents/badge.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | documentation 17 | 18 | 19 | documentation 20 | 21 | 22 | 63% 23 | 24 | 25 | 63% 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /docs/docsets/InputBarAccessoryView.docset/Contents/Resources/Documents/css/highlight.css: -------------------------------------------------------------------------------- 1 | /* Credit to https://gist.github.com/wataru420/2048287 */ 2 | .highlight { 3 | /* Comment */ 4 | /* Error */ 5 | /* Keyword */ 6 | /* Operator */ 7 | /* Comment.Multiline */ 8 | /* Comment.Preproc */ 9 | /* Comment.Single */ 10 | /* Comment.Special */ 11 | /* Generic.Deleted */ 12 | /* Generic.Deleted.Specific */ 13 | /* Generic.Emph */ 14 | /* Generic.Error */ 15 | /* Generic.Heading */ 16 | /* Generic.Inserted */ 17 | /* Generic.Inserted.Specific */ 18 | /* Generic.Output */ 19 | /* Generic.Prompt */ 20 | /* Generic.Strong */ 21 | /* Generic.Subheading */ 22 | /* Generic.Traceback */ 23 | /* Keyword.Constant */ 24 | /* Keyword.Declaration */ 25 | /* Keyword.Pseudo */ 26 | /* Keyword.Reserved */ 27 | /* Keyword.Type */ 28 | /* Literal.Number */ 29 | /* Literal.String */ 30 | /* Name.Attribute */ 31 | /* Name.Builtin */ 32 | /* Name.Class */ 33 | /* Name.Constant */ 34 | /* Name.Entity */ 35 | /* Name.Exception */ 36 | /* Name.Function */ 37 | /* Name.Namespace */ 38 | /* Name.Tag */ 39 | /* Name.Variable */ 40 | /* Operator.Word */ 41 | /* Text.Whitespace */ 42 | /* Literal.Number.Float */ 43 | /* Literal.Number.Hex */ 44 | /* Literal.Number.Integer */ 45 | /* Literal.Number.Oct */ 46 | /* Literal.String.Backtick */ 47 | /* Literal.String.Char */ 48 | /* Literal.String.Doc */ 49 | /* Literal.String.Double */ 50 | /* Literal.String.Escape */ 51 | /* Literal.String.Heredoc */ 52 | /* Literal.String.Interpol */ 53 | /* Literal.String.Other */ 54 | /* Literal.String.Regex */ 55 | /* Literal.String.Single */ 56 | /* Literal.String.Symbol */ 57 | /* Name.Builtin.Pseudo */ 58 | /* Name.Variable.Class */ 59 | /* Name.Variable.Global */ 60 | /* Name.Variable.Instance */ 61 | /* Literal.Number.Integer.Long */ } 62 | .highlight .c { 63 | color: #999988; 64 | font-style: italic; } 65 | .highlight .err { 66 | color: #a61717; 67 | background-color: #e3d2d2; } 68 | .highlight .k { 69 | color: #000000; 70 | font-weight: bold; } 71 | .highlight .o { 72 | color: #000000; 73 | font-weight: bold; } 74 | .highlight .cm { 75 | color: #999988; 76 | font-style: italic; } 77 | .highlight .cp { 78 | color: #999999; 79 | font-weight: bold; } 80 | .highlight .c1 { 81 | color: #999988; 82 | font-style: italic; } 83 | .highlight .cs { 84 | color: #999999; 85 | font-weight: bold; 86 | font-style: italic; } 87 | .highlight .gd { 88 | color: #000000; 89 | background-color: #ffdddd; } 90 | .highlight .gd .x { 91 | color: #000000; 92 | background-color: #ffaaaa; } 93 | .highlight .ge { 94 | color: #000000; 95 | font-style: italic; } 96 | .highlight .gr { 97 | color: #aa0000; } 98 | .highlight .gh { 99 | color: #999999; } 100 | .highlight .gi { 101 | color: #000000; 102 | background-color: #ddffdd; } 103 | .highlight .gi .x { 104 | color: #000000; 105 | background-color: #aaffaa; } 106 | .highlight .go { 107 | color: #888888; } 108 | .highlight .gp { 109 | color: #555555; } 110 | .highlight .gs { 111 | font-weight: bold; } 112 | .highlight .gu { 113 | color: #aaaaaa; } 114 | .highlight .gt { 115 | color: #aa0000; } 116 | .highlight .kc { 117 | color: #000000; 118 | font-weight: bold; } 119 | .highlight .kd { 120 | color: #000000; 121 | font-weight: bold; } 122 | .highlight .kp { 123 | color: #000000; 124 | font-weight: bold; } 125 | .highlight .kr { 126 | color: #000000; 127 | font-weight: bold; } 128 | .highlight .kt { 129 | color: #445588; } 130 | .highlight .m { 131 | color: #009999; } 132 | .highlight .s { 133 | color: #d14; } 134 | .highlight .na { 135 | color: #008080; } 136 | .highlight .nb { 137 | color: #0086B3; } 138 | .highlight .nc { 139 | color: #445588; 140 | font-weight: bold; } 141 | .highlight .no { 142 | color: #008080; } 143 | .highlight .ni { 144 | color: #800080; } 145 | .highlight .ne { 146 | color: #990000; 147 | font-weight: bold; } 148 | .highlight .nf { 149 | color: #990000; } 150 | .highlight .nn { 151 | color: #555555; } 152 | .highlight .nt { 153 | color: #000080; } 154 | .highlight .nv { 155 | color: #008080; } 156 | .highlight .ow { 157 | color: #000000; 158 | font-weight: bold; } 159 | .highlight .w { 160 | color: #bbbbbb; } 161 | .highlight .mf { 162 | color: #009999; } 163 | .highlight .mh { 164 | color: #009999; } 165 | .highlight .mi { 166 | color: #009999; } 167 | .highlight .mo { 168 | color: #009999; } 169 | .highlight .sb { 170 | color: #d14; } 171 | .highlight .sc { 172 | color: #d14; } 173 | .highlight .sd { 174 | color: #d14; } 175 | .highlight .s2 { 176 | color: #d14; } 177 | .highlight .se { 178 | color: #d14; } 179 | .highlight .sh { 180 | color: #d14; } 181 | .highlight .si { 182 | color: #d14; } 183 | .highlight .sx { 184 | color: #d14; } 185 | .highlight .sr { 186 | color: #009926; } 187 | .highlight .s1 { 188 | color: #d14; } 189 | .highlight .ss { 190 | color: #990073; } 191 | .highlight .bp { 192 | color: #999999; } 193 | .highlight .vc { 194 | color: #008080; } 195 | .highlight .vg { 196 | color: #008080; } 197 | .highlight .vi { 198 | color: #008080; } 199 | .highlight .il { 200 | color: #009999; } 201 | -------------------------------------------------------------------------------- /docs/docsets/InputBarAccessoryView.docset/Contents/Resources/Documents/img/carat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nathantannar4/InputBarAccessoryView/dfdc822b0d4a0d1b5777100347c399069e9bfc62/docs/docsets/InputBarAccessoryView.docset/Contents/Resources/Documents/img/carat.png -------------------------------------------------------------------------------- /docs/docsets/InputBarAccessoryView.docset/Contents/Resources/Documents/img/dash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nathantannar4/InputBarAccessoryView/dfdc822b0d4a0d1b5777100347c399069e9bfc62/docs/docsets/InputBarAccessoryView.docset/Contents/Resources/Documents/img/dash.png -------------------------------------------------------------------------------- /docs/docsets/InputBarAccessoryView.docset/Contents/Resources/Documents/img/gh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nathantannar4/InputBarAccessoryView/dfdc822b0d4a0d1b5777100347c399069e9bfc62/docs/docsets/InputBarAccessoryView.docset/Contents/Resources/Documents/img/gh.png -------------------------------------------------------------------------------- /docs/docsets/InputBarAccessoryView.docset/Contents/Resources/Documents/js/jazzy.js: -------------------------------------------------------------------------------- 1 | window.jazzy = {'docset': false} 2 | if (typeof window.dash != 'undefined') { 3 | document.documentElement.className += ' dash' 4 | window.jazzy.docset = true 5 | } 6 | if (navigator.userAgent.match(/xcode/i)) { 7 | document.documentElement.className += ' xcode' 8 | window.jazzy.docset = true 9 | } 10 | 11 | // On doc load, toggle the URL hash discussion if present 12 | $(document).ready(function() { 13 | if (!window.jazzy.docset) { 14 | var linkToHash = $('a[href="' + window.location.hash +'"]'); 15 | linkToHash.trigger("click"); 16 | } 17 | }); 18 | 19 | // On token click, toggle its discussion and animate token.marginLeft 20 | $(".token").click(function(event) { 21 | if (window.jazzy.docset) { 22 | return; 23 | } 24 | var link = $(this); 25 | var animationDuration = 300; 26 | var tokenOffset = "15px"; 27 | var original = link.css('marginLeft') == tokenOffset; 28 | link.animate({'margin-left':original ? "0px" : tokenOffset}, animationDuration); 29 | $content = link.parent().parent().next(); 30 | $content.slideToggle(animationDuration); 31 | 32 | // Keeps the document from jumping to the hash. 33 | var href = $(this).attr('href'); 34 | if (history.pushState) { 35 | history.pushState({}, '', href); 36 | } else { 37 | location.hash = href; 38 | } 39 | event.preventDefault(); 40 | }); 41 | 42 | // Dumb down quotes within code blocks that delimit strings instead of quotations 43 | // https://github.com/realm/jazzy/issues/714 44 | $("code q").replaceWith(function () { 45 | return ["\"", $(this).contents(), "\""]; 46 | }); 47 | -------------------------------------------------------------------------------- /docs/docsets/InputBarAccessoryView.docset/Contents/Resources/docSet.dsidx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nathantannar4/InputBarAccessoryView/dfdc822b0d4a0d1b5777100347c399069e9bfc62/docs/docsets/InputBarAccessoryView.docset/Contents/Resources/docSet.dsidx -------------------------------------------------------------------------------- /docs/docsets/InputBarAccessoryView.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nathantannar4/InputBarAccessoryView/dfdc822b0d4a0d1b5777100347c399069e9bfc62/docs/docsets/InputBarAccessoryView.tgz -------------------------------------------------------------------------------- /docs/img/carat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nathantannar4/InputBarAccessoryView/dfdc822b0d4a0d1b5777100347c399069e9bfc62/docs/img/carat.png -------------------------------------------------------------------------------- /docs/img/dash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nathantannar4/InputBarAccessoryView/dfdc822b0d4a0d1b5777100347c399069e9bfc62/docs/img/dash.png -------------------------------------------------------------------------------- /docs/img/gh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nathantannar4/InputBarAccessoryView/dfdc822b0d4a0d1b5777100347c399069e9bfc62/docs/img/gh.png -------------------------------------------------------------------------------- /docs/img/spinner.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nathantannar4/InputBarAccessoryView/dfdc822b0d4a0d1b5777100347c399069e9bfc62/docs/img/spinner.gif -------------------------------------------------------------------------------- /docs/js/jazzy.js: -------------------------------------------------------------------------------- 1 | // Jazzy - https://github.com/realm/jazzy 2 | // Copyright Realm Inc. 3 | // SPDX-License-Identifier: MIT 4 | 5 | window.jazzy = {'docset': false} 6 | if (typeof window.dash != 'undefined') { 7 | document.documentElement.className += ' dash' 8 | window.jazzy.docset = true 9 | } 10 | if (navigator.userAgent.match(/xcode/i)) { 11 | document.documentElement.className += ' xcode' 12 | window.jazzy.docset = true 13 | } 14 | 15 | function toggleItem($link, $content) { 16 | var animationDuration = 300; 17 | $link.toggleClass('token-open'); 18 | $content.slideToggle(animationDuration); 19 | } 20 | 21 | function itemLinkToContent($link) { 22 | return $link.parent().parent().next(); 23 | } 24 | 25 | // On doc load + hash-change, open any targetted item 26 | function openCurrentItemIfClosed() { 27 | if (window.jazzy.docset) { 28 | return; 29 | } 30 | var $link = $(`a[name="${location.hash.substring(1)}"]`).nextAll('.token'); 31 | $content = itemLinkToContent($link); 32 | if ($content.is(':hidden')) { 33 | toggleItem($link, $content); 34 | } 35 | } 36 | 37 | $(openCurrentItemIfClosed); 38 | $(window).on('hashchange', openCurrentItemIfClosed); 39 | 40 | // On item link ('token') click, toggle its discussion 41 | $('.token').on('click', function(event) { 42 | if (window.jazzy.docset) { 43 | return; 44 | } 45 | var $link = $(this); 46 | toggleItem($link, itemLinkToContent($link)); 47 | 48 | // Keeps the document from jumping to the hash. 49 | var href = $link.attr('href'); 50 | if (history.pushState) { 51 | history.pushState({}, '', href); 52 | } else { 53 | location.hash = href; 54 | } 55 | event.preventDefault(); 56 | }); 57 | 58 | // Clicks on links to the current, closed, item need to open the item 59 | $("a:not('.token')").on('click', function() { 60 | if (location == this.href) { 61 | openCurrentItemIfClosed(); 62 | } 63 | }); 64 | 65 | // KaTeX rendering 66 | if ("katex" in window) { 67 | $($('.math').each( (_, element) => { 68 | katex.render(element.textContent, element, { 69 | displayMode: $(element).hasClass('m-block'), 70 | throwOnError: false, 71 | trust: true 72 | }); 73 | })) 74 | } 75 | -------------------------------------------------------------------------------- /docs/js/jazzy.search.js: -------------------------------------------------------------------------------- 1 | // Jazzy - https://github.com/realm/jazzy 2 | // Copyright Realm Inc. 3 | // SPDX-License-Identifier: MIT 4 | 5 | $(function(){ 6 | var $typeahead = $('[data-typeahead]'); 7 | var $form = $typeahead.parents('form'); 8 | var searchURL = $form.attr('action'); 9 | 10 | function displayTemplate(result) { 11 | return result.name; 12 | } 13 | 14 | function suggestionTemplate(result) { 15 | var t = '
'; 16 | t += '' + result.name + ''; 17 | if (result.parent_name) { 18 | t += '' + result.parent_name + ''; 19 | } 20 | t += '
'; 21 | return t; 22 | } 23 | 24 | $typeahead.one('focus', function() { 25 | $form.addClass('loading'); 26 | 27 | $.getJSON(searchURL).then(function(searchData) { 28 | const searchIndex = lunr(function() { 29 | this.ref('url'); 30 | this.field('name'); 31 | this.field('abstract'); 32 | for (const [url, doc] of Object.entries(searchData)) { 33 | this.add({url: url, name: doc.name, abstract: doc.abstract}); 34 | } 35 | }); 36 | 37 | $typeahead.typeahead( 38 | { 39 | highlight: true, 40 | minLength: 3, 41 | autoselect: true 42 | }, 43 | { 44 | limit: 10, 45 | display: displayTemplate, 46 | templates: { suggestion: suggestionTemplate }, 47 | source: function(query, sync) { 48 | const lcSearch = query.toLowerCase(); 49 | const results = searchIndex.query(function(q) { 50 | q.term(lcSearch, { boost: 100 }); 51 | q.term(lcSearch, { 52 | boost: 10, 53 | wildcard: lunr.Query.wildcard.TRAILING 54 | }); 55 | }).map(function(result) { 56 | var doc = searchData[result.ref]; 57 | doc.url = result.ref; 58 | return doc; 59 | }); 60 | sync(results); 61 | } 62 | } 63 | ); 64 | $form.removeClass('loading'); 65 | $typeahead.trigger('focus'); 66 | }); 67 | }); 68 | 69 | var baseURL = searchURL.slice(0, -"search.json".length); 70 | 71 | $typeahead.on('typeahead:select', function(e, result) { 72 | window.location = baseURL + result.url; 73 | }); 74 | }); 75 | -------------------------------------------------------------------------------- /docs/undocumented.json: -------------------------------------------------------------------------------- 1 | { 2 | "warnings": [ 3 | 4 | ], 5 | "source_directory": "/Users/kaspik/Documents/Vyvoj/Github/InputBarAccessoryView" 6 | } --------------------------------------------------------------------------------