├── .hound.yml ├── docs ├── img │ ├── gh.png │ ├── carat.png │ ├── dash.png │ └── spinner.gif ├── badge.svg ├── js │ ├── jazzy.js │ └── jazzy.search.js ├── css │ ├── highlight.css │ └── jazzy.css ├── Structs.html ├── Protocols.html ├── Protocols │ └── OptionDescriptive.html ├── Classes │ ├── KeyboardDismissTextField.html │ └── KeyboardDismissAccessoryView.html ├── search.json └── Classes.html ├── screenshots ├── ICTokenField.gif ├── ICTokenField.png └── ICKeyboardDismissTextField.png ├── Example ├── Assets.xcassets │ ├── Contents.json │ ├── icook-iphone-input-search.imageset │ │ ├── icook-iphone-input-search.pdf │ │ └── Contents.json │ └── AppIcon.appiconset │ │ └── Contents.json ├── Base.lproj │ ├── InfoPlist.strings │ └── LaunchScreen.storyboard ├── Info.plist ├── Language.swift ├── AppDelegate.swift ├── StoryboardViewController.swift ├── ExampleCell.swift ├── CustomizedTokenField.swift ├── CustomizedTokenViewController.swift ├── ExampleViewController.swift └── Main.storyboard ├── Gemfile ├── Source ├── KeyboardDismissTextField │ ├── Images.xcassets │ │ ├── Contents.json │ │ └── icook-iphone-button-hide-keyboard.imageset │ │ │ ├── icook-iphone-button-hide-keyboard.pdf │ │ │ └── Contents.json │ ├── KeyboardDismissTextField.swift │ └── KeyboardDismissAccessoryView.swift ├── TokenField │ ├── BackspaceTextField.swift │ ├── InsetLabel.swift │ └── Token.swift └── OptionPickerControl │ ├── Option.swift │ └── OptionPickerControl.swift ├── fastlane └── Scanfile ├── .swiftlint.yml ├── ICInputAccessory.xcodeproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── xcshareddata │ └── xcschemes │ ├── ICInputAccessory-iOS.xcscheme │ └── Example.xcscheme ├── Podfile ├── ICInputAccessory.xcworkspace ├── contents.xcworkspacedata └── xcshareddata │ └── IDEWorkspaceChecks.plist ├── .jazzy.yml ├── Podfile.lock ├── .gitignore ├── ICInputAccessoryUITests ├── Info.plist ├── KeyboardDismissTextFieldUITests.swift ├── OptionPickerControlUITests.swift └── TokenFieldUITests.swift ├── ICInputAccessory ├── Info.plist └── ICInputAccessory.h ├── .github └── workflows │ ├── push.yml │ └── pull_request.yml ├── Makefile ├── Dangerfile ├── LICENSE ├── CHANGELOG.md ├── ICInputAccessory.podspec ├── README.md └── Gemfile.lock /.hound.yml: -------------------------------------------------------------------------------- 1 | swift: 2 | enabled: true 3 | config_file: .swiftlint.yml 4 | -------------------------------------------------------------------------------- /docs/img/gh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polydice/ICInputAccessory/HEAD/docs/img/gh.png -------------------------------------------------------------------------------- /docs/img/carat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polydice/ICInputAccessory/HEAD/docs/img/carat.png -------------------------------------------------------------------------------- /docs/img/dash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polydice/ICInputAccessory/HEAD/docs/img/dash.png -------------------------------------------------------------------------------- /docs/img/spinner.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polydice/ICInputAccessory/HEAD/docs/img/spinner.gif -------------------------------------------------------------------------------- /screenshots/ICTokenField.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polydice/ICInputAccessory/HEAD/screenshots/ICTokenField.gif -------------------------------------------------------------------------------- /screenshots/ICTokenField.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polydice/ICInputAccessory/HEAD/screenshots/ICTokenField.png -------------------------------------------------------------------------------- /Example/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "http://rubygems.org" 2 | 3 | gem "cocoapods" 4 | gem "danger" 5 | gem "fastlane" 6 | gem "jazzy", ">= 0.8.0" 7 | -------------------------------------------------------------------------------- /Source/KeyboardDismissTextField/Images.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /fastlane/Scanfile: -------------------------------------------------------------------------------- 1 | workspace "ICInputAccessory.xcworkspace" 2 | scheme "Example" 3 | clean true 4 | skip_build true 5 | code_coverage true 6 | -------------------------------------------------------------------------------- /screenshots/ICKeyboardDismissTextField.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polydice/ICInputAccessory/HEAD/screenshots/ICKeyboardDismissTextField.png -------------------------------------------------------------------------------- /.swiftlint.yml: -------------------------------------------------------------------------------- 1 | disabled_rules: 2 | - file_length 3 | - identifier_name 4 | - line_length 5 | - type_body_length 6 | - vertical_whitespace 7 | excluded: 8 | - Carthage 9 | - Pods 10 | - vendor 11 | -------------------------------------------------------------------------------- /Example/Assets.xcassets/icook-iphone-input-search.imageset/icook-iphone-input-search.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polydice/ICInputAccessory/HEAD/Example/Assets.xcassets/icook-iphone-input-search.imageset/icook-iphone-input-search.pdf -------------------------------------------------------------------------------- /Example/Base.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* 2 | InfoPlist.strings 3 | Example 4 | 5 | Created by Ben on 07/03/2016. 6 | Copyright © 2016 Polydice, Inc. All rights reserved. 7 | */ 8 | 9 | "CFBundleDisplayName" = "ICInputAccessory"; 10 | -------------------------------------------------------------------------------- /ICInputAccessory.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Example/Assets.xcassets/icook-iphone-input-search.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "icook-iphone-input-search.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | platform :ios, "8.0" 2 | use_frameworks! 3 | 4 | workspace "ICInputAccessory" 5 | project "ICInputAccessory" 6 | 7 | target "Example" do 8 | pod "SwiftLint", podspec: "https://raw.githubusercontent.com/CocoaPods/Specs/master/Specs/4/0/1/SwiftLint/0.25.1/SwiftLint.podspec.json" 9 | end 10 | -------------------------------------------------------------------------------- /Source/KeyboardDismissTextField/Images.xcassets/icook-iphone-button-hide-keyboard.imageset/icook-iphone-button-hide-keyboard.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polydice/ICInputAccessory/HEAD/Source/KeyboardDismissTextField/Images.xcassets/icook-iphone-button-hide-keyboard.imageset/icook-iphone-button-hide-keyboard.pdf -------------------------------------------------------------------------------- /ICInputAccessory.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /ICInputAccessory.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Source/KeyboardDismissTextField/Images.xcassets/icook-iphone-button-hide-keyboard.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "icook-iphone-button-hide-keyboard.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /.jazzy.yml: -------------------------------------------------------------------------------- 1 | clean: true 2 | author: polydice 3 | author_url: https://github.com/polydice 4 | github_url: https://github.com/polydice/ICInputAccessory 5 | github_file_prefix: https://github.com/polydice/ICInputAccessory/blob/develop 6 | xcodebuild_arguments: [-project, ICInputAccessory.xcodeproj, -scheme, ICInputAccessory-iOS] 7 | module: ICInputAccessory 8 | module_version: 2.0.1 9 | output: docs 10 | theme: fullwidth 11 | skip_undocumented: true 12 | -------------------------------------------------------------------------------- /Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - SwiftLint (0.25.1) 3 | 4 | DEPENDENCIES: 5 | - SwiftLint (from `https://raw.githubusercontent.com/CocoaPods/Specs/master/Specs/4/0/1/SwiftLint/0.25.1/SwiftLint.podspec.json`) 6 | 7 | EXTERNAL SOURCES: 8 | SwiftLint: 9 | :podspec: https://raw.githubusercontent.com/CocoaPods/Specs/master/Specs/4/0/1/SwiftLint/0.25.1/SwiftLint.podspec.json 10 | 11 | SPEC CHECKSUMS: 12 | SwiftLint: ce933681be10c3266e82576dad676fa815a602e9 13 | 14 | PODFILE CHECKSUM: 74bc2bf59f17e7cc006ae2aa1d4d73e50cc2b4ff 15 | 16 | COCOAPODS: 1.9.3 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # https://github.com/github/gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData 8 | test_output 9 | 10 | ## Various settings 11 | *.pbxuser 12 | !default.pbxuser 13 | *.mode1v3 14 | !default.mode1v3 15 | *.mode2v3 16 | !default.mode2v3 17 | *.perspectivev3 18 | !default.perspectivev3 19 | xcuserdata 20 | 21 | ## Other 22 | *.xccheckout 23 | *.moved-aside 24 | *.xcuserstate 25 | *.xcscmblueprint 26 | 27 | ## Obj-C/Swift specific 28 | *.hmap 29 | *.ipa 30 | 31 | ## Docs 32 | undocumented.json 33 | *.docset 34 | *.tgz 35 | 36 | ## CocoaPods 37 | Pods/ 38 | 39 | ## Carthage 40 | Carthage/ 41 | -------------------------------------------------------------------------------- /ICInputAccessoryUITests/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 | BNDL 17 | CFBundleShortVersionString 18 | 2.0.1 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /ICInputAccessory/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 | 2.0.1 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /.github/workflows/push.yml: -------------------------------------------------------------------------------- 1 | name: iOS build 2 | 3 | on: push 4 | 5 | jobs: 6 | build: 7 | runs-on: macos-latest 8 | steps: 9 | - uses: actions/checkout@v1 10 | - name: Apply Ruby 11 | uses: actions/setup-ruby@v1 12 | with: 13 | ruby-version: '2.x' 14 | - uses: actions/cache@v1 15 | with: 16 | path: vendor/bundle 17 | key: ${{ runner.os }}-gems-${{ hashFiles('**/Gemfile.lock') }} 18 | restore-keys: | 19 | ${{ runner.os }}-gems- 20 | - uses: actions/cache@v1 21 | with: 22 | path: Pods 23 | key: ${{ runner.os }}-pods-${{ hashFiles('**/Podfile.lock') }} 24 | restore-keys: | 25 | ${{ runner.os }}-pods- 26 | - name: Install and setup 27 | run: make install 28 | - name: Run build 29 | run: | 30 | bundle exec fastlane scan 31 | make -B carthage 32 | make -B docs 33 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | bootstrap: 2 | gem install bundler 3 | bundle install 4 | bundle exec pod install 5 | 6 | install: bundle-install pod-install 7 | 8 | bundle-install: 9 | bundle install --without development --deployment --jobs=3 --retry=3 10 | 11 | pod-install: 12 | bundle exec pod install 13 | 14 | bump: 15 | ifeq (,$(strip $(version))) 16 | # Usage: make bump version= 17 | else 18 | ruby -pi -e "gsub(/\d+\.\d+\.\d+/i, \""$(version)"\")" ICInputAccessory.podspec 19 | ruby -pi -e "gsub(/:\s\d+\.\d+\.\d+/i, \": "$(version)"\")" .jazzy.yml 20 | xcrun agvtool new-marketing-version $(version) 21 | make -B docs 22 | endif 23 | 24 | carthage: 25 | set -o pipefail && carthage build --no-skip-current --verbose | bundle exec xcpretty 26 | 27 | docs: 28 | bundle exec jazzy --config .jazzy.yml 29 | for file in "html" "css" "js" "json"; do \ 30 | echo "Trimming whitespace in *."$$file ; \ 31 | find docs -name "*."$$file -exec sed -E -i "" -e "s/[[:blank:]]*$$//" {} \; ; \ 32 | done 33 | -------------------------------------------------------------------------------- /Dangerfile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | # Sometimes it's a README fix, or something like that - which isn't relevant for 4 | # including in a project's CHANGELOG for example 5 | declared_trivial = (github.pr_title + github.pr_body).include? "#trivial" 6 | 7 | # Make it more obvious that a PR is a work in progress and shouldn't be merged yet 8 | warn "PR is classed as Work in Progress" if github.pr_title.include? "[WIP]" 9 | 10 | # Warn when there is a big PR 11 | warn "Big PR" if git.lines_of_code > 500 12 | 13 | # Ensure there is a summary for a PR 14 | fail "Please provide a summary in the Pull Request description" if github.pr_body.length < 5 15 | 16 | # Add a CHANGELOG entry for app changes 17 | if git.lines_of_code > 50 && !git.modified_files.include?("CHANGELOG.md") && !declared_trivial 18 | fail "Please update [CHANGELOG.md](https://github.com/polydice/ICInputAccessory/blob/develop/CHANGELOG.md).", sticky: true 19 | end 20 | 21 | # Ensure a clean commits history 22 | if git.commits.any? { |c| c.message =~ /^Merge branch/ } 23 | fail "Please rebase to get rid of the merge commits in this PR", sticky: true 24 | end 25 | -------------------------------------------------------------------------------- /Example/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" : "ios-marketing", 45 | "size" : "1024x1024", 46 | "scale" : "1x" 47 | } 48 | ], 49 | "info" : { 50 | "version" : 1, 51 | "author" : "xcode" 52 | } 53 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Polydice, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /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 | 73% 23 | 24 | 25 | 73% 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /.github/workflows/pull_request.yml: -------------------------------------------------------------------------------- 1 | name: iOS review 2 | 3 | on: pull_request 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v2 10 | with: 11 | fetch-depth: 0 12 | - name: Apply Ruby 13 | uses: actions/setup-ruby@v1 14 | with: 15 | ruby-version: '2.x' 16 | - uses: actions/cache@v1 17 | with: 18 | path: vendor/bundle 19 | key: ${{ runner.os }}-gems-${{ hashFiles('**/Gemfile.lock') }} 20 | restore-keys: | 21 | ${{ runner.os }}-gems- 22 | - name: Bundle install 23 | run: | 24 | bundle config path vendor/bundle 25 | bundle install --jobs 4 --retry 3 26 | - name: Run Danger 27 | run: bundle exec danger 28 | env: 29 | DANGER_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }} 30 | - name: Fetch base ref 31 | run: git fetch --no-tags --prune --depth=1 origin ${{ github.base_ref }} 32 | - name: Run SwiftLint 33 | uses: norio-nomura/action-swiftlint@3.1.0 34 | with: 35 | args: --force-exclude 36 | env: 37 | DIFF_BASE: origin/${{ github.base_ref }} 38 | -------------------------------------------------------------------------------- /docs/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 | $content = link.parent().parent().next(); 27 | $content.slideToggle(animationDuration); 28 | 29 | // Keeps the document from jumping to the hash. 30 | var href = $(this).attr('href'); 31 | if (history.pushState) { 32 | history.pushState({}, '', href); 33 | } else { 34 | location.hash = href; 35 | } 36 | event.preventDefault(); 37 | }); 38 | 39 | // Dumb down quotes within code blocks that delimit strings instead of quotations 40 | // https://github.com/realm/jazzy/issues/714 41 | $("code q").replaceWith(function () { 42 | return ["\"", $(this).contents(), "\""]; 43 | }); 44 | -------------------------------------------------------------------------------- /Example/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ICInputAccessoryExample 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 2.0.1 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 101 25 | LSRequiresIPhoneOS 26 | 27 | UILaunchStoryboardName 28 | LaunchScreen 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UIRequiresFullScreen 34 | 35 | UISupportedInterfaceOrientations 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationLandscapeLeft 39 | UIInterfaceOrientationLandscapeRight 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /ICInputAccessory/ICInputAccessory.h: -------------------------------------------------------------------------------- 1 | // 2 | // ICInputAccessory.h 3 | // ICInputAccessory 4 | // 5 | // Created by Ben on 04/03/2016. 6 | // Copyright © 2016 Polydice, Inc. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | // SOFTWARE. 25 | // 26 | 27 | #import 28 | 29 | //! Project version number for ICInputAccessory. 30 | FOUNDATION_EXPORT double ICInputAccessoryVersionNumber; 31 | 32 | //! Project version string for ICInputAccessory. 33 | FOUNDATION_EXPORT const unsigned char ICInputAccessoryVersionString[]; 34 | 35 | // In this header, you should import all the public headers of your framework using statements like #import 36 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## Unreleased 2 | 3 | * Update CI infrastructure to GitHub Actions 4 | * Swift 5.0 5 | 6 | ## v2.0.1 7 | 8 | * Fix the embedded standard library issue [#32](https://github.com/polydice/ICInputAccessory/pull/32) 9 | 10 | ## v2.0.0 11 | 12 | * Drop Class Name Prefixes 13 | * Add an easy to use `OptionPickerControl` that displays a `UIPickerView` with given options 14 | 15 | ## v1.5.0 16 | 17 | * Swift 4.0 18 | 19 | ## v1.4.1 20 | 21 | * Fix `tokenField(_:didChangeInputText:)` with the delete key [#20](https://github.com/polydice/ICInputAccessory/pull/20) 22 | 23 | #### Project Updates 24 | 25 | * Reenable UI tests 26 | * Combine the framework and example projects 27 | * Specify SwiftLint version via CocoaPods 28 | 29 | ## v1.4.0 30 | 31 | * Added delegate methods: 32 | 33 | ```swift 34 | @objc optional func tokenField(_ tokenField: ICTokenField, didChangeInputText text: String) 35 | @objc optional func tokenField(_ tokenField: ICTokenField, shouldCompleteText text: String) -> Bool 36 | @objc optional func tokenField(_ tokenField: ICTokenField, subsequentDelimiterForCompletedText text: String) -> String 37 | ``` 38 | 39 | * Renamed delegate method: 40 | 41 | ```swift 42 | @objc optional func tokenField(_ tokenField: ICTokenField, didCompleteText text: String) 43 | ``` 44 | 45 | ## v1.3.0 46 | 47 | * Swift 3.0 48 | * Support `pod try ICInputAccessory` 49 | 50 | ## v1.2.1 51 | 52 | * Update to Xcode 8.2 53 | 54 | ## v1.2.0 55 | 56 | * Swift 2.3 57 | 58 | ## v1.1.0 59 | 60 | * Swift 2.2 61 | * Support storyboard 62 | * Support subspecs 63 | 64 | ## v1.0.0 65 | 66 | Initial release written in Swift 2.1. 67 | 68 | * `ICKeyboardDismissTextField` with an accessory view to dismiss keyboard. 69 | * `ICTokenField`, a text field that groups input texts as tokens. 70 | -------------------------------------------------------------------------------- /docs/js/jazzy.search.js: -------------------------------------------------------------------------------- 1 | $(function(){ 2 | var searchIndex = lunr(function() { 3 | this.ref('url'); 4 | this.field('name'); 5 | }); 6 | 7 | var $typeahead = $('[data-typeahead]'); 8 | var $form = $typeahead.parents('form'); 9 | var searchURL = $form.attr('action'); 10 | 11 | function displayTemplate(result) { 12 | return result.name; 13 | } 14 | 15 | function suggestionTemplate(result) { 16 | var t = '
'; 17 | t += '' + result.name + ''; 18 | if (result.parent_name) { 19 | t += '' + result.parent_name + ''; 20 | } 21 | t += '
'; 22 | return t; 23 | } 24 | 25 | $typeahead.one('focus', function() { 26 | $form.addClass('loading'); 27 | 28 | $.getJSON(searchURL).then(function(searchData) { 29 | $.each(searchData, function (url, doc) { 30 | searchIndex.add({url: url, name: doc.name}); 31 | }); 32 | 33 | $typeahead.typeahead( 34 | { 35 | highlight: true, 36 | minLength: 3 37 | }, 38 | { 39 | limit: 10, 40 | display: displayTemplate, 41 | templates: { suggestion: suggestionTemplate }, 42 | source: function(query, sync) { 43 | var results = searchIndex.search(query).map(function(result) { 44 | var doc = searchData[result.ref]; 45 | doc.url = result.ref; 46 | return doc; 47 | }); 48 | sync(results); 49 | } 50 | } 51 | ); 52 | $form.removeClass('loading'); 53 | $typeahead.trigger('focus'); 54 | }); 55 | }); 56 | 57 | var baseURL = searchURL.slice(0, -"search.json".length); 58 | 59 | $typeahead.on('typeahead:select', function(e, result) { 60 | window.location = baseURL + result.url; 61 | }); 62 | }); 63 | -------------------------------------------------------------------------------- /Example/Language.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Language.swift 3 | // Example 4 | // 5 | // Created by Ben on 20/01/2018. 6 | // Copyright © 2018 bcylin. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | // SOFTWARE. 25 | // 26 | 27 | import Foundation 28 | import ICInputAccessory 29 | 30 | enum Language: String, OptionDescriptive { 31 | 32 | case english 33 | case french 34 | case german 35 | case japanese 36 | case mandarin 37 | case spanish 38 | 39 | static var availableLanguages: [Language] = [ 40 | .english, 41 | .french, 42 | .german, 43 | .japanese, 44 | .mandarin, 45 | .spanish 46 | ] 47 | 48 | // MARK: - OptionDescriptive 49 | 50 | var title: String { 51 | return rawValue.capitalized 52 | } 53 | 54 | static var titleForOptionalValue: String { 55 | return "(Optional)" 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /Example/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // Example 4 | // 5 | // Created by Ben on 07/03/2016. 6 | // Copyright © 2016 Polydice, Inc. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | // SOFTWARE. 25 | // 26 | 27 | import UIKit 28 | 29 | @UIApplicationMain 30 | class AppDelegate: UIResponder, UIApplicationDelegate { 31 | 32 | var window: UIWindow? 33 | 34 | func application(_ application: UIApplication, 35 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) 36 | -> Bool { 37 | window = UIWindow(frame: UIScreen.main.bounds) 38 | window?.backgroundColor = UIColor.white 39 | window?.rootViewController = UINavigationController(rootViewController: ExampleViewController()) 40 | window?.makeKeyAndVisible() 41 | return true 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /ICInputAccessory.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "ICInputAccessory" 3 | s.version = "2.0.1" 4 | s.summary = "A customized token text field used in the iCook app." 5 | s.description = <<-DESC 6 | KeyboardDismissTextField: 7 | * An input accessory view with a button to dismiss keyboard. 8 | 9 | TokenField: 10 | * A horizontal scrolling UI that groups input texts. 11 | * Easy to add, select and delete tokens. 12 | * Customizable icon and colors. 13 | * Supports storyboard. 14 | 15 | OptionPickerControl: 16 | * An easy to use UIControl that displays a UIPickerView with given options. 17 | DESC 18 | 19 | s.screenshots = "https://raw.githubusercontent.com/polydice/ICInputAccessory/master/screenshots/ICTokenField.png", 20 | "https://raw.githubusercontent.com/polydice/ICInputAccessory/master/screenshots/ICKeyboardDismissTextField.png" 21 | s.homepage = "https://github.com/polydice/ICInputAccessory" 22 | s.license = { type: "MIT", file: "LICENSE" } 23 | s.authors = "bcylin", "trisix" 24 | s.platform = :ios, "8.0" 25 | s.swift_version = "4.0" 26 | s.source = { git: "https://github.com/polydice/ICInputAccessory.git", tag: "v#{s.version}" } 27 | s.requires_arc = true 28 | 29 | s.default_subspecs = "KeyboardDismissTextField", "OptionPickerControl", "TokenField" 30 | 31 | s.subspec "KeyboardDismissTextField" do |sp| 32 | sp.source_files = "Source/KeyboardDismissTextField/*.swift" 33 | sp.resources = "Source/KeyboardDismissTextField/*.xcassets" 34 | end 35 | 36 | s.subspec "OptionPickerControl" do |sp| 37 | sp.source_files = "Source/OptionPickerControl/*.swift" 38 | end 39 | 40 | s.subspec "TokenField" do |sp| 41 | sp.source_files = "Source/TokenField/*.swift" 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /Example/StoryboardViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StoryboardViewController.swift 3 | // Example 4 | // 5 | // Created by Ben on 16/03/2016. 6 | // Copyright © 2016 Polydice, Inc. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | // SOFTWARE. 25 | // 26 | 27 | import UIKit 28 | import ICInputAccessory 29 | 30 | class StoryboardViewController: UITableViewController { 31 | 32 | @IBOutlet weak var tokenField: TokenField! { 33 | didSet { 34 | tokenField.normalTokenAttributes = [ 35 | .foregroundColor: UIColor.white, 36 | .backgroundColor: UIColor.white.withAlphaComponent(0.25) 37 | ] 38 | 39 | tokenField.highlightedTokenAttributes = [ 40 | .foregroundColor: UIColor.darkGray, 41 | .backgroundColor: UIColor.white 42 | ] 43 | } 44 | } 45 | 46 | @IBAction func dismiss(_ sender: UIButton) { 47 | presentingViewController?.dismiss(animated: true, completion: nil) 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /ICInputAccessoryUITests/KeyboardDismissTextFieldUITests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // KeyboardDismissTextFieldUITests.swift 3 | // ICInputAccessoryUITests 4 | // 5 | // Created by Ben on 20/03/2016. 6 | // Copyright © 2016 Polydice, Inc. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | // SOFTWARE. 25 | // 26 | 27 | import XCTest 28 | 29 | extension XCUIElement { 30 | func waitAndForceTap(timeout: TimeInterval = 5000) { 31 | XCTAssert(waitForExistence(timeout: timeout)) 32 | coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.5)).tap() 33 | } 34 | } 35 | 36 | class KeyboardDismissTextFieldUITests: XCTestCase { 37 | 38 | private lazy var app = XCUIApplication() 39 | 40 | override func setUp() { 41 | super.setUp() 42 | continueAfterFailure = false 43 | XCUIApplication().launch() 44 | } 45 | 46 | func testKeyboardDismissing() { 47 | app.tables.textFields["KeyboardDismissTextField"].tap() 48 | app.buttons["Dismiss Keyboard"].waitAndForceTap() 49 | } 50 | 51 | func testStoryboard() { 52 | app.tables.buttons["Storyboard"].tap() 53 | app.tables.textFields["Storyboard KeyboardDismissTextField"].tap() 54 | app.buttons["Dismiss Keyboard"].waitAndForceTap() 55 | app.tables.buttons["Back to Code"].tap() 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /Source/TokenField/BackspaceTextField.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BackspaceTextField.swift 3 | // iCook 4 | // 5 | // Created by Ben on 02/03/2016. 6 | // Copyright © 2016 Polydice, Inc. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | // SOFTWARE. 25 | // 26 | 27 | import UIKit 28 | 29 | internal protocol BackspaceTextFieldDelegate: class { 30 | func textFieldShouldDelete(_ textField: BackspaceTextField) -> Bool 31 | } 32 | 33 | //////////////////////////////////////////////////////////////////////////////// 34 | 35 | 36 | internal class BackspaceTextField: UITextField { 37 | 38 | weak var backspaceDelegate: BackspaceTextFieldDelegate? 39 | 40 | var showsCursor = true { 41 | didSet { 42 | // Trigger the lazy instantiation of cursorColor 43 | let color = cursorColor 44 | tintColor = showsCursor ? color : UIColor.clear 45 | } 46 | } 47 | 48 | lazy var cursorColor: UIColor! = { self.tintColor }() 49 | 50 | // MARK: - UIView 51 | 52 | override func point(inside point: CGPoint, with event: UIEvent?) -> Bool { 53 | // Forward touches to the superview when the cursor is hidden. 54 | return showsCursor && super.point(inside: point, with: event) 55 | } 56 | 57 | // MARK: - UITextField 58 | 59 | @objc func keyboardInputShouldDelete(_ textField: UITextField) -> Bool { 60 | return backspaceDelegate?.textFieldShouldDelete(self) ?? true 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /ICInputAccessoryUITests/OptionPickerControlUITests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OptionPickerControlUITests.swift 3 | // ICInputAccessoryUITests 4 | // 5 | // Created by Ben on 21/01/2018. 6 | // Copyright © 2018 bcylin. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | // SOFTWARE. 25 | // 26 | 27 | import XCTest 28 | 29 | final class OptionPickerControlUITests: XCTestCase { 30 | 31 | private lazy var app = XCUIApplication() 32 | 33 | override func setUp() { 34 | super.setUp() 35 | continueAfterFailure = false 36 | XCUIApplication().launch() 37 | } 38 | 39 | func testOptionSelection() { 40 | app.tables.staticTexts["(Optional)"].tap() 41 | let picker = app.pickerWheels.element 42 | 43 | picker.adjust(toPickerWheelValue: "English") 44 | XCTAssert(app.tables.staticTexts["English"].exists) 45 | 46 | picker.adjust(toPickerWheelValue: "French") 47 | XCTAssert(app.tables.staticTexts["French"].exists) 48 | 49 | picker.adjust(toPickerWheelValue: "German") 50 | XCTAssert(app.tables.staticTexts["German"].exists) 51 | 52 | picker.adjust(toPickerWheelValue: "Japanese") 53 | XCTAssert(app.tables.staticTexts["Japanese"].exists) 54 | 55 | picker.adjust(toPickerWheelValue: "Mandarin") 56 | XCTAssert(app.tables.staticTexts["Mandarin"].exists) 57 | 58 | picker.adjust(toPickerWheelValue: "Spanish") 59 | XCTAssert(app.tables.staticTexts["Spanish"].exists) 60 | 61 | app.toolbars.buttons["Done"].tap() 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /Source/TokenField/InsetLabel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // InsetLabel.swift 3 | // iCook 4 | // 5 | // Created by Ben on 10/07/2015. 6 | // Copyright (c) 2015 Polydice, Inc. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | // SOFTWARE. 25 | // 26 | 27 | import UIKit 28 | 29 | internal class InsetLabel: UILabel { 30 | 31 | enum CornerRadius { 32 | case dynamic 33 | case constant(CGFloat) 34 | } 35 | 36 | var contentEdgeInsets = UIEdgeInsets.zero 37 | var cornerRadius = CornerRadius.constant(0) 38 | 39 | convenience init(contentEdgeInsets: UIEdgeInsets, cornerRadius: CornerRadius = .constant(0)) { 40 | self.init(frame: CGRect.zero) 41 | self.contentEdgeInsets = contentEdgeInsets 42 | self.cornerRadius = cornerRadius 43 | 44 | switch cornerRadius { 45 | case let .constant(radius) where radius > 0: 46 | layer.cornerRadius = radius 47 | fallthrough // swiftlint:disable:this fallthrough 48 | case .dynamic: 49 | layer.masksToBounds = true 50 | layer.shouldRasterize = true 51 | layer.rasterizationScale = UIScreen.main.scale 52 | default: 53 | break 54 | } 55 | } 56 | 57 | // MARK: - UIView 58 | 59 | override var intrinsicContentSize: CGSize { 60 | let size = super.intrinsicContentSize 61 | return CGSize( 62 | width: contentEdgeInsets.left + size.width + contentEdgeInsets.right, 63 | height: contentEdgeInsets.top + size.height + contentEdgeInsets.bottom 64 | ) 65 | } 66 | 67 | override func layoutSubviews() { 68 | super.layoutSubviews() 69 | if case .dynamic = cornerRadius { 70 | layer.cornerRadius = frame.height / 2 71 | } 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /Example/ExampleCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ExampleCell.swift 3 | // Example 4 | // 5 | // Created by Ben on 07/03/2016. 6 | // Copyright © 2016 Polydice, Inc. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | // SOFTWARE. 25 | // 26 | 27 | import UIKit 28 | 29 | class ExampleCell: UITableViewCell { 30 | 31 | var showcase: UIView? { 32 | didSet { 33 | oldValue?.removeFromSuperview() 34 | if let displayingView = showcase { 35 | contentView.addSubview(displayingView) 36 | displayingView.translatesAutoresizingMaskIntoConstraints = false 37 | 38 | if #available(iOS 9.0, *) { 39 | displayingView.topAnchor.constraint(equalTo: contentView.topAnchor).isActive = true 40 | displayingView.leftAnchor.constraint(equalTo: contentView.leftAnchor).isActive = true 41 | displayingView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor).isActive = true 42 | displayingView.rightAnchor.constraint(equalTo: contentView.rightAnchor).isActive = true 43 | } else { 44 | let views = ["view": displayingView] 45 | contentView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[view]|", options: [], metrics: nil, views: views)) 46 | contentView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|[view]|", options: [], metrics: nil, views: views)) 47 | } 48 | } 49 | } 50 | } 51 | 52 | // MARK: - UITableViewCell 53 | 54 | override func prepareForReuse() { 55 | super.prepareForReuse() 56 | showcase?.removeFromSuperview() 57 | showcase = nil 58 | textLabel?.text = nil 59 | accessoryType = .none 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /ICInputAccessoryUITests/TokenFieldUITests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TokenFieldUITests.swift 3 | // ICInputAccessoryUITests 4 | // 5 | // Created by Ben on 08/03/2016. 6 | // Copyright © 2016 Polydice, Inc. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | // SOFTWARE. 25 | // 26 | 27 | import XCTest 28 | 29 | class TokenFieldUITests: XCTestCase { 30 | 31 | private lazy var app = XCUIApplication() 32 | 33 | override func setUp() { 34 | super.setUp() 35 | continueAfterFailure = false 36 | app.launch() 37 | } 38 | 39 | private func typeTexts(in textField: XCUIElement) { 40 | textField.tap() 41 | textField.typeText("Try") 42 | textField.typeText(" ") 43 | textField.typeText("iCook") 44 | textField.typeText(",") 45 | textField.typeText("beta") 46 | textField.typeText(" ") 47 | 48 | let deleteKey = app.keys["delete"] 49 | deleteKey.tap() 50 | deleteKey.tap() 51 | 52 | textField.typeText("TestFlight") 53 | textField.typeText(",") 54 | 55 | app.buttons["Search"].tap() 56 | } 57 | 58 | func testTokenField() { 59 | let textField = app.tables.cells.textFields["TokenField"] 60 | typeTexts(in: textField) 61 | } 62 | 63 | func testCustomizedTokenField() { 64 | app.tables.staticTexts["CustomizedTokenField"].tap() 65 | let tokenField = app.navigationBars["Example.CustomizedTokenView"].scrollViews.children(matching: .textField).element 66 | typeTexts(in: tokenField) 67 | } 68 | 69 | func testStoryboard() { 70 | app.tables.buttons["Storyboard"].tap() 71 | let tokenField = app.tables.cells.textFields["Storyboard TokenField"] 72 | typeTexts(in: tokenField) 73 | app.tables.buttons["Back to Code"].tap() 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /Source/OptionPickerControl/Option.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Option.swift 3 | // ICInputAccessory 4 | // 5 | // Created by Ben on 22/11/2017. 6 | // Copyright © 2017 bcylin. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | // SOFTWARE. 25 | // 26 | 27 | import Foundation 28 | 29 | /// The protocol defines the required variables to be displayed in a `UIPickerView` via `OptionPickerControl`. 30 | public protocol OptionDescriptive: Equatable { 31 | /// The text for the row in the `UIPickerView`. 32 | var title: String { get } 33 | /// The text for a placeholder row when the picker selection is optional. 34 | static var titleForOptionalValue: String { get } 35 | } 36 | 37 | 38 | /// An option struct that carries the `OptionDescriptive` 39 | public struct Option: Equatable { 40 | 41 | // MARK: - Initialization 42 | 43 | /// Returns an option that displays the optional value of an `OptionDescriptive` type. 44 | public static var optional: Option { 45 | return Option() 46 | } 47 | 48 | /// Returns an initialized option with an instance of an `OptionDescriptive` type. 49 | public init(_ value: T) { 50 | self.value = value 51 | } 52 | 53 | // MARK: - Properties 54 | 55 | /// Conformance to `OptionDescriptive`. 56 | public var title: String { 57 | return value?.title ?? T.titleForOptionalValue 58 | } 59 | 60 | /// The `OptionDescriptive` value of the option. Returns `nil` when it's the `optional` placeholder. 61 | public let value: T? 62 | 63 | // MARK: - Private 64 | 65 | private init() { 66 | self.value = nil 67 | } 68 | 69 | // MARK: - Equatable 70 | 71 | /// Returns true when two options' values are equal. 72 | public static func == (lhs: Option, rhs: Option) -> Bool { 73 | return lhs.value == rhs.value 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /Example/CustomizedTokenField.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CustomizedTokenField.swift 3 | // Example 4 | // 5 | // Created by Ben on 09/03/2016. 6 | // Copyright © 2016 Polydice, Inc. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | // SOFTWARE. 25 | // 26 | 27 | import UIKit 28 | import ICInputAccessory 29 | 30 | class CustomizedTokenField: TokenField { 31 | 32 | override init(frame: CGRect) { 33 | super.init(frame: frame) 34 | applyCustomizedStyle() 35 | } 36 | 37 | required init?(coder aDecoder: NSCoder) { 38 | super.init(coder: aDecoder) 39 | applyCustomizedStyle() 40 | } 41 | 42 | override var intrinsicContentSize: CGSize { 43 | return UIView.layoutFittingExpandedSize 44 | } 45 | 46 | } 47 | 48 | 49 | //////////////////////////////////////////////////////////////////////////////// 50 | 51 | 52 | extension TokenField { 53 | 54 | func applyCustomizedStyle() { 55 | icon = UIImage(named: "icook-iphone-input-search") 56 | 57 | layer.cornerRadius = 5 58 | layer.shouldRasterize = true 59 | layer.rasterizationScale = UIScreen.main.scale 60 | backgroundColor = UIColor(red: 0.8, green: 0.32, blue: 0.24, alpha: 1) 61 | 62 | textField.textColor = UIColor.white 63 | textField.tintColor = UIColor.white 64 | textField.font = UIFont.boldSystemFont(ofSize: 14) 65 | 66 | attributedPlaceholder = NSAttributedString( 67 | string: String(describing: type(of: self)), 68 | attributes: [ 69 | .foregroundColor: UIColor.white.withAlphaComponent(0.5), 70 | .font: UIFont.boldSystemFont(ofSize: 14) 71 | ] 72 | ) 73 | 74 | normalTokenAttributes = [ 75 | .foregroundColor: UIColor.white, 76 | .backgroundColor: UIColor.white.withAlphaComponent(0.25), 77 | .font: UIFont.boldSystemFont(ofSize: 14) 78 | ] 79 | 80 | highlightedTokenAttributes = [ 81 | .foregroundColor: UIColor(red: 0.8, green: 0.32, blue: 0.24, alpha: 1), 82 | .backgroundColor: UIColor.white, 83 | .font: UIFont.boldSystemFont(ofSize: 14) 84 | ] 85 | } 86 | 87 | } 88 | -------------------------------------------------------------------------------- /ICInputAccessory.xcodeproj/xcshareddata/xcschemes/ICInputAccessory-iOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 44 | 50 | 51 | 52 | 53 | 59 | 60 | 66 | 67 | 68 | 69 | 71 | 72 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /Example/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /Source/KeyboardDismissTextField/KeyboardDismissTextField.swift: -------------------------------------------------------------------------------- 1 | // 2 | // KeyboardDismissTextField.swift 3 | // ICInputAccessory 4 | // 5 | // Created by Ben on 07/03/2016. 6 | // Copyright © 2016 Polydice, Inc. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | // SOFTWARE. 25 | // 26 | 27 | import UIKit 28 | 29 | /// A text field that has a button to dismiss keyboard on the input accessory view. 30 | @IBDesignable 31 | open class KeyboardDismissTextField: UITextField { 32 | 33 | /// The custom input accessory view with a button to dismiss keyboard. 34 | @IBOutlet public var keyboardAccessoryView: KeyboardDismissAccessoryView! { 35 | didSet { 36 | if UI_USER_INTERFACE_IDIOM() != .phone { return } 37 | keyboardAccessoryView.dismissButton.addTarget(self, action: .dismiss, for: .touchUpInside) 38 | inputAccessoryView = keyboardAccessoryView 39 | } 40 | } 41 | 42 | // MARK: - Initialization 43 | 44 | /// Initializes and returns a newly allocated view object with the specified frame rectangle. 45 | public override init(frame: CGRect) { 46 | super.init(frame: frame) 47 | setUpAccessoryView() 48 | } 49 | 50 | /// Returns an object initialized from data in a given unarchiver. 51 | public required init?(coder aDecoder: NSCoder) { 52 | super.init(coder: aDecoder) 53 | setUpAccessoryView() 54 | } 55 | 56 | // MARK: - UIResponder 57 | 58 | open override func becomeFirstResponder() -> Bool { 59 | if UI_USER_INTERFACE_IDIOM() == .phone { 60 | keyboardAccessoryView.alpha = 1 61 | } 62 | return super.becomeFirstResponder() 63 | } 64 | 65 | @objc fileprivate func dismiss(_ sender: UIButton) { 66 | resignFirstResponder() 67 | UIView.animate(withDuration: 0.3) { 68 | self.keyboardAccessoryView.alpha = 0 69 | } 70 | } 71 | 72 | // MARK: - Private Methods 73 | 74 | private func setUpAccessoryView() { 75 | if keyboardAccessoryView == nil { 76 | // Set an initial frame for the button to appear during UI testing. 77 | let frame = CGRect(x: 0, y: 0, width: 320, height: 60) 78 | keyboardAccessoryView = KeyboardDismissAccessoryView(frame: frame) 79 | } 80 | } 81 | 82 | } 83 | 84 | 85 | //////////////////////////////////////////////////////////////////////////////// 86 | 87 | 88 | private extension Selector { 89 | static let dismiss = #selector(KeyboardDismissTextField.dismiss(_:)) 90 | } 91 | -------------------------------------------------------------------------------- /ICInputAccessory.xcodeproj/xcshareddata/xcschemes/Example.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 37 | 38 | 39 | 40 | 42 | 48 | 49 | 50 | 51 | 52 | 62 | 64 | 70 | 71 | 72 | 73 | 79 | 81 | 87 | 88 | 89 | 90 | 92 | 93 | 96 | 97 | 98 | -------------------------------------------------------------------------------- /Source/OptionPickerControl/OptionPickerControl.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OptionPickerControl.swift 3 | // ICInputAccessory 4 | // 5 | // Created by Ben on 27/11/2017. 6 | // Copyright © 2017 bcylin. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | // SOFTWARE. 25 | // 26 | 27 | import UIKit 28 | 29 | /// A `UIControl` that displays a `UIPickerView` and notifies changed selection and via `UIControlEvents` `.valueChanged`. 30 | open class OptionPickerControl: UIControl, UIPickerViewDataSource, UIPickerViewDelegate { 31 | 32 | // MARK: - Initialization 33 | 34 | /// Returns an initialized `OptionPickerControl`. 35 | public init() { 36 | super.init(frame: .zero) 37 | addSubview(hiddenTextField) 38 | } 39 | 40 | /// Not supported. `OptionPickerControl` is not compatible with storyboards. 41 | required public init?(coder aDecoder: NSCoder) { 42 | fatalError("init(coder:) is not supported") 43 | } 44 | 45 | // MARK: - Properties 46 | 47 | /// Options that shows in the `UIPickerView`. 48 | public var options: [Option] = [.optional] 49 | 50 | /// The currently selected item in the options. 51 | public var selectedOption: Option = .optional { 52 | didSet { 53 | if hiddenTextField.isFirstResponder { 54 | sendActions(for: .valueChanged) 55 | } else if let index = options.firstIndex(of: selectedOption) { 56 | picker.selectRow(index, inComponent: 0, animated: false) 57 | } 58 | } 59 | } 60 | 61 | /// A reference to the displayed `UIPickerView` for customization. 62 | public private(set) lazy var picker: UIPickerView = { 63 | let picker = UIPickerView() 64 | picker.dataSource = self 65 | picker.delegate = self 66 | return picker 67 | }() 68 | 69 | // MARK: - Lazy Instantiation 70 | 71 | private lazy var doneBarButton: UIBarButtonItem = 72 | UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(dismissPicker(_:))) 73 | 74 | private lazy var pickerToolbar: UIToolbar = { 75 | let toolbar = UIToolbar(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: 44)) 76 | toolbar.items = [ 77 | UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil), 78 | self.doneBarButton 79 | ] 80 | return toolbar 81 | }() 82 | 83 | private lazy var hiddenTextField: UITextField = { 84 | let textField = UITextField() 85 | textField.inputAccessoryView = self.pickerToolbar 86 | textField.inputView = self.picker 87 | return textField 88 | }() 89 | 90 | // MARK: - UIResponder 91 | 92 | @discardableResult 93 | override open func becomeFirstResponder() -> Bool { 94 | return super.becomeFirstResponder() || hiddenTextField.becomeFirstResponder() 95 | } 96 | 97 | @discardableResult 98 | override open func resignFirstResponder() -> Bool { 99 | return hiddenTextField.resignFirstResponder() 100 | } 101 | 102 | // MARK: - UIPickerViewDataSource 103 | 104 | /// Currently `OptionPickerControl` only supports one component. 105 | open func numberOfComponents(in pickerView: UIPickerView) -> Int { 106 | return 1 107 | } 108 | 109 | open func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int { 110 | return options.count 111 | } 112 | 113 | // MARK: - UIPickerViewDelegate 114 | 115 | open func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? { 116 | return options[row].title 117 | } 118 | 119 | open func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) { 120 | selectedOption = options[row] 121 | } 122 | 123 | // MARK: - IBActions 124 | 125 | @objc private func dismissPicker(_ sender: Any) { 126 | resignFirstResponder() 127 | } 128 | 129 | } 130 | -------------------------------------------------------------------------------- /Source/KeyboardDismissTextField/KeyboardDismissAccessoryView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // KeyboardDismissAccessoryView.swift 3 | // iCook 4 | // 5 | // Created by Ben on 27/08/2015. 6 | // Copyright (c) 2015 Polydice, Inc. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | // SOFTWARE. 25 | // 26 | 27 | import UIKit 28 | 29 | /// A customized keyboard accessory view with a dismiss button. 30 | @IBDesignable 31 | open class KeyboardDismissAccessoryView: UIView { 32 | 33 | /// The background color of the button to dismiss keyboard. 34 | @IBInspectable public var buttonColor: UIColor = Constants.ButtonColor { 35 | didSet { 36 | dismissButton.backgroundColor = buttonColor 37 | } 38 | } 39 | 40 | /// The button to dismiss keyboard. 41 | public private(set) lazy var dismissButton: UIButton = { 42 | let _button = UIButton() 43 | let resources = Bundle(for: type(of: self)) 44 | let icon = UIImage(named: "icook-iphone-button-hide-keyboard", in: resources, compatibleWith: nil) 45 | _button.setImage(icon, for: UIControl.State()) 46 | _button.backgroundColor = Constants.ButtonColor 47 | _button.isExclusiveTouch = true 48 | _button.layer.cornerRadius = 4 49 | _button.layer.shouldRasterize = true 50 | _button.layer.rasterizationScale = UIScreen.main.scale 51 | _button.accessibilityLabel = "Dismiss Keyboard" 52 | return _button 53 | }() 54 | 55 | private struct Constants { 56 | static let ButtonColor = UIColor(red: 0.21, green: 0.2, blue: 0.19, alpha: 0.5) 57 | static let EdgePadding = CGFloat(7) 58 | static let InteractiveSize = CGSize(width: 44, height: 44) 59 | } 60 | 61 | // MARK: - Initialization 62 | 63 | /// Initializes and returns a newly allocated view object with the specified frame rectangle. 64 | public override init(frame: CGRect) { 65 | super.init(frame: frame) 66 | setUpSubviews() 67 | } 68 | 69 | /// Returns an object initialized from data in a given unarchiver. 70 | public required init?(coder aDecoder: NSCoder) { 71 | super.init(coder: aDecoder) 72 | setUpSubviews() 73 | } 74 | 75 | // MARK: - UIView 76 | 77 | open override func point(inside point: CGPoint, with event: UIEvent?) -> Bool { 78 | for subview in subviews { 79 | if !subview.isHidden && subview.alpha > 0 && 80 | subview.isUserInteractionEnabled && 81 | subview.point(inside: convert(point, to: subview), with: event) { 82 | return true 83 | } 84 | } 85 | return false 86 | } 87 | 88 | // MARK: - NSKeyValueCoding 89 | 90 | open override func setValue(_ value: Any?, forKey key: String) { 91 | if let color = value as? UIColor, key == "buttonColor" { 92 | buttonColor = color 93 | } 94 | } 95 | 96 | // MARK: - Private Methods 97 | 98 | private func setUpSubviews() { 99 | backgroundColor = UIColor.clear 100 | 101 | addSubview(dismissButton) 102 | dismissButton.translatesAutoresizingMaskIntoConstraints = false 103 | 104 | let views = ["button": dismissButton] 105 | let metrics = [ 106 | "width": Constants.InteractiveSize.width, 107 | "height": Constants.InteractiveSize.height, 108 | "padding": Constants.EdgePadding 109 | ] 110 | 111 | addConstraints(NSLayoutConstraint.constraints( 112 | withVisualFormat: "H:[button(width)]-(padding)-|", 113 | options: [], 114 | metrics: metrics, 115 | views: views 116 | )) 117 | addConstraints(NSLayoutConstraint.constraints( 118 | withVisualFormat: "V:[button(height)]-(padding)-|", 119 | options: [], 120 | metrics: metrics, 121 | views: views 122 | )) 123 | } 124 | 125 | // MARK: - Internal Methods 126 | 127 | class func requiredHeight() -> CGFloat { 128 | return Constants.InteractiveSize.height + Constants.EdgePadding * 2 129 | } 130 | 131 | } 132 | -------------------------------------------------------------------------------- /Source/TokenField/Token.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Token.swift 3 | // iCook 4 | // 5 | // Created by Ben on 03/03/2016. 6 | // Copyright © 2016 Polydice, Inc. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | // SOFTWARE. 25 | // 26 | 27 | import UIKit 28 | 29 | internal class Token: UIView { 30 | 31 | var text = "" { 32 | didSet { 33 | updateTextLabel() 34 | frame = CGRect(origin: CGPoint.zero, size: systemLayoutSizeFitting(UIView.layoutFittingCompressedSize)) 35 | } 36 | } 37 | 38 | var isHighlighted = false { 39 | didSet { 40 | updateTextLabel() 41 | } 42 | } 43 | 44 | var normalTextAttributes: [NSAttributedString.Key: NSObject] = [ 45 | .foregroundColor: UIColor(red: 0.14, green: 0.38, blue: 0.95, alpha: 1), 46 | .backgroundColor: UIColor.clear 47 | ] { 48 | didSet { 49 | if !isHighlighted { updateTextLabel() } 50 | delimiterLabel.textColor = self.normalTextAttributes[.foregroundColor] as? UIColor 51 | } 52 | } 53 | 54 | var highlightedTextAttributes: [NSAttributedString.Key: NSObject] = [ 55 | .foregroundColor: UIColor.white, 56 | .backgroundColor: UIColor(red: 0.14, green: 0.38, blue: 0.95, alpha: 1) 57 | ] { 58 | didSet { 59 | if isHighlighted { updateTextLabel() } 60 | } 61 | } 62 | 63 | // MARK: - Private Properties 64 | 65 | private(set) lazy var delimiterLabel: UILabel = { 66 | let _label = UILabel() 67 | _label.textColor = self.normalTextAttributes[.foregroundColor] as? UIColor 68 | _label.textAlignment = .right 69 | return _label 70 | }() 71 | 72 | private(set) lazy var textLabel: UILabel = { 73 | let _label = InsetLabel(contentEdgeInsets: UIEdgeInsets(top: 3, left: 5, bottom: 3, right: 5), cornerRadius: .constant(3)) 74 | _label.textAlignment = .center 75 | _label.textColor = self.normalTextAttributes[.foregroundColor] as? UIColor 76 | _label.backgroundColor = self.normalTextAttributes[.backgroundColor] as? UIColor 77 | _label.numberOfLines = 1 78 | return _label 79 | }() 80 | 81 | // MARK: - Initialization 82 | 83 | override init(frame: CGRect) { 84 | super.init(frame: frame) 85 | setUpSubviews() 86 | } 87 | 88 | required init?(coder aDecoder: NSCoder) { 89 | super.init(coder: aDecoder) 90 | setUpSubviews() 91 | } 92 | 93 | convenience init( 94 | text: String, 95 | delimiter: String = ",", 96 | normalAttributes: [NSAttributedString.Key: NSObject]? = nil, 97 | highlightedAttributes: [NSAttributedString.Key: NSObject]? = nil 98 | ) { 99 | self.init() 100 | if let attributes = normalAttributes { normalTextAttributes = attributes } 101 | if let attributes = highlightedAttributes { highlightedTextAttributes = attributes } 102 | delimiterLabel.text = delimiter 103 | ({ 104 | // Workaround to trigger didSet inside the initializer 105 | self.text = text 106 | })() 107 | } 108 | 109 | // MARK: - Private Methods 110 | 111 | private func updateTextLabel() { 112 | var attributes = isHighlighted ? highlightedTextAttributes : normalTextAttributes 113 | if let color = attributes[.backgroundColor] as? UIColor { 114 | textLabel.backgroundColor = color 115 | } 116 | // Avoid overlapped translucent background colors 117 | attributes[.backgroundColor] = nil 118 | textLabel.attributedText = NSAttributedString(string: text, attributes: attributes) 119 | 120 | delimiterLabel.textColor = normalTextAttributes[.foregroundColor] as? UIColor 121 | delimiterLabel.font = normalTextAttributes[.font] as? UIFont 122 | } 123 | 124 | private func setUpSubviews() { 125 | addSubview(textLabel) 126 | addSubview(delimiterLabel) 127 | textLabel.translatesAutoresizingMaskIntoConstraints = false 128 | delimiterLabel.translatesAutoresizingMaskIntoConstraints = false 129 | 130 | let views = [ 131 | "text": textLabel, 132 | "delimiter": delimiterLabel 133 | ] 134 | addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[text][delimiter]-5-|", 135 | options: [.alignAllCenterY], 136 | metrics: nil, 137 | views: views 138 | )) 139 | addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-2-[text]-2-|", 140 | options: [], 141 | metrics: nil, 142 | views: views 143 | )) 144 | } 145 | 146 | } 147 | -------------------------------------------------------------------------------- /docs/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 | -------------------------------------------------------------------------------- /Example/CustomizedTokenViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CustomizedTokenViewController.swift 3 | // Example 4 | // 5 | // Created by Ben on 11/03/2016. 6 | // Copyright © 2016 Polydice, Inc. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | // SOFTWARE. 25 | // 26 | 27 | import UIKit 28 | import ICInputAccessory 29 | 30 | class CustomizedTokenViewController: UIViewController, TokenFieldDelegate { 31 | 32 | private let tokenField = CustomizedTokenField() 33 | private let textView = UITextView() 34 | 35 | // MARK: - UIViewController 36 | 37 | override func loadView() { 38 | super.loadView() 39 | view.backgroundColor = UIColor.white 40 | textView.isEditable = false 41 | textView.isSelectable = false 42 | textView.text = "[\n\n]" 43 | textView.font = UIFont.preferredFont(forTextStyle: .subheadline) 44 | textView.frame = view.bounds.insetBy(dx: 10, dy: 10) 45 | textView.autoresizingMask = [.flexibleWidth, .flexibleHeight] 46 | view.addSubview(textView) 47 | } 48 | 49 | override func viewDidLoad() { 50 | super.viewDidLoad() 51 | navigationController?.navigationBar.barTintColor = UIColor(red: 0.96, green: 0.48, blue: 0.4, alpha: 1) 52 | navigationController?.navigationBar.isTranslucent = false 53 | navigationController?.navigationBar.barStyle = .black 54 | 55 | let cancelBarButton = UIBarButtonItem(barButtonSystemItem: .cancel, target: self, action: .dismiss) 56 | cancelBarButton.tintColor = UIColor.white 57 | navigationItem.rightBarButtonItem = cancelBarButton 58 | 59 | navigationItem.titleView = { 60 | if #available(iOS 11, *) { 61 | tokenField.translatesAutoresizingMaskIntoConstraints = false 62 | let views = ["field": tokenField] 63 | let metrics = ["padding": 7] 64 | let containerView = UIView() 65 | containerView.addSubview(tokenField) 66 | containerView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[field]-padding-|", options: [], metrics: metrics, views: views)) 67 | containerView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-padding-[field]-padding-|", options: [], metrics: metrics, views: views)) 68 | return containerView 69 | } else { 70 | return tokenField 71 | } 72 | }() 73 | tokenField.delegate = self 74 | } 75 | 76 | override func viewWillAppear(_ animated: Bool) { 77 | super.viewWillAppear(animated) 78 | _ = tokenField.becomeFirstResponder() 79 | } 80 | 81 | override func viewWillDisappear(_ animated: Bool) { 82 | super.viewWillAppear(animated) 83 | _ = tokenField.resignFirstResponder() 84 | textView.endEditing(true) 85 | } 86 | 87 | // MARK: - TokenFieldDelegate 88 | 89 | func tokenFieldDidBeginEditing(_ tokenField: TokenField) { 90 | print(#function) 91 | } 92 | 93 | func tokenFieldDidEndEditing(_ tokenField: TokenField) { 94 | print(#function) 95 | } 96 | 97 | func tokenFieldWillReturn(_ tokenField: TokenField) { 98 | print(#function) 99 | } 100 | 101 | func tokenField(_ tokenField: TokenField, didChangeInputText text: String) { 102 | print("Typing \"\(text)\"") 103 | } 104 | 105 | func tokenField(_ tokenField: TokenField, shouldCompleteText text: String) -> Bool { 106 | print("Should add \"\(text)\"?") 107 | return text != "42" 108 | } 109 | 110 | func tokenField(_ tokenField: TokenField, didCompleteText text: String) { 111 | print("Added \"\(text)\"") 112 | updateTexts() 113 | } 114 | 115 | func tokenField(_ tokenField: TokenField, didDeleteText text: String, atIndex index: Int) { 116 | print("Deleted \"\(text)\"") 117 | updateTexts() 118 | } 119 | 120 | func tokenField(_ tokenField: TokenField, subsequentDelimiterForCompletedText text: String) -> String { 121 | return " ," 122 | } 123 | 124 | // MARK: - UIResponder Callbacks 125 | 126 | @objc fileprivate func dismiss(_ sender: UIBarButtonItem) { 127 | presentingViewController?.dismiss(animated: true, completion: nil) 128 | } 129 | 130 | // MARK: - Private Methods 131 | 132 | private func updateTexts() { 133 | let tokens = tokenField.texts.map { "\"" + $0 + "\"" } .joined(separator: ",\n ") 134 | textView.text = "[\n " + tokens + "\n]" 135 | } 136 | 137 | } 138 | 139 | 140 | //////////////////////////////////////////////////////////////////////////////// 141 | 142 | 143 | private extension Selector { 144 | static let dismiss = #selector(CustomizedTokenViewController.dismiss(_:)) 145 | } 146 | -------------------------------------------------------------------------------- /docs/Structs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Structures Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 |

23 | 24 | ICInputAccessory Docs 25 | 26 | (73% documented) 27 |

28 | 29 |

30 |

31 | 32 |
33 |

34 | 35 |

36 | 37 | 38 | View on GitHub 39 | 40 |

41 | 42 |
43 | 44 | 49 | 50 |
51 | 91 |
92 | 93 |
94 |
95 |

Structures

96 |

The following structures are available globally.

97 | 98 |
99 |
100 | 101 |
102 |
103 |
104 |
    105 |
  • 106 |
    107 | 108 | 109 | 110 | Option 111 | 112 |
    113 |
    114 |
    115 |
    116 |
    117 |
    118 |

    An option struct that carries the OptionDescriptive

    119 | 120 | See more 121 |
    122 |
    123 |

    Declaration

    124 |
    125 |

    Swift

    126 |
    public struct Option<T> : Equatable where T : OptionDescriptive
    127 | 128 |
    129 |
    130 |
    131 | Show on GitHub 132 |
    133 |
    134 |
    135 |
  • 136 |
137 |
138 |
139 |
140 | 141 |
142 |
143 | 147 | 148 | 149 | 150 | -------------------------------------------------------------------------------- /Example/ExampleViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ExampleViewController.swift 3 | // Example 4 | // 5 | // Created by Ben on 07/03/2016. 6 | // Copyright © 2016 Polydice, Inc. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | // SOFTWARE. 25 | // 26 | 27 | import UIKit 28 | import ICInputAccessory 29 | 30 | class ExampleViewController: UITableViewController { 31 | 32 | private let showcases: [UIView.Type] = [ 33 | KeyboardDismissTextField.self, 34 | TokenField.self, 35 | CustomizedTokenField.self, 36 | OptionPickerControl.self 37 | ] 38 | 39 | private lazy var languagePicker: OptionPickerControl = { 40 | let picker = OptionPickerControl() 41 | picker.options += Language.availableLanguages.map(Option.init(_:)) 42 | picker.addTarget(self, action: .updateLanguage, for: .valueChanged) 43 | return picker 44 | }() 45 | 46 | private lazy var flipButton: UIButton = { 47 | let _button = UIButton(type: .system) 48 | _button.frame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: 88) 49 | _button.setTitle("Storyboard", for: UIControl.State()) 50 | _button.addTarget(self, action: .showStoryboard, for: .touchUpInside) 51 | return _button 52 | }() 53 | 54 | // MARK: - Initialization 55 | 56 | convenience init() { 57 | self.init(style: .grouped) 58 | title = "ICInputAccessory" 59 | } 60 | 61 | // MARK: - UIViewController 62 | 63 | override func viewDidLoad() { 64 | super.viewDidLoad() 65 | tableView.rowHeight = 44 66 | tableView.register(ExampleCell.self, forCellReuseIdentifier: String(describing: ExampleCell.self)) 67 | tableView.tableFooterView = flipButton 68 | tableView.tableFooterView?.isUserInteractionEnabled = true 69 | view.addSubview(languagePicker) 70 | } 71 | 72 | // MARK: - UITableViewDataSource 73 | 74 | override func numberOfSections(in tableView: UITableView) -> Int { 75 | return showcases.count 76 | } 77 | 78 | override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 79 | return 1 80 | } 81 | 82 | override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { 83 | switch showcases[section] { 84 | case is KeyboardDismissTextField.Type: 85 | return "Dismiss Keyboard" 86 | case is TokenField.Type: 87 | return "Text Field with Tokens" 88 | case is CustomizedTokenField.Type: 89 | return "Customize Token Field" 90 | case is OptionPickerControl.Type: 91 | return "Option Picker Control" 92 | default: 93 | return "" 94 | } 95 | } 96 | 97 | override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 98 | let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: ExampleCell.self), for: indexPath) 99 | cell.accessoryType = .none 100 | 101 | switch showcases[indexPath.section] { 102 | case let type as KeyboardDismissTextField.Type: 103 | let textField = type.init() 104 | textField.leftViewMode = .always 105 | textField.leftView = UIView(frame: CGRect(x: 0, y: 0, width: 15, height: 15)) 106 | textField.placeholder = String(describing: type) 107 | (cell as? ExampleCell)?.showcase = textField 108 | 109 | case let type as CustomizedTokenField.Type: 110 | cell.textLabel?.text = String(describing: type) 111 | cell.accessoryType = .disclosureIndicator 112 | 113 | case let type as TokenField.Type: 114 | let container = UIView(frame: cell.bounds) 115 | let tokenField = type.init() 116 | tokenField.placeholder = String(describing: type) 117 | tokenField.frame = container.bounds.insetBy(dx: 5, dy: 0) 118 | tokenField.autoresizingMask = [.flexibleWidth, .flexibleHeight] 119 | container.addSubview(tokenField) 120 | (cell as? ExampleCell)?.showcase = container 121 | 122 | case is OptionPickerControl.Type: 123 | (cell as? ExampleCell)?.showcase = nil 124 | cell.textLabel?.text = languagePicker.selectedOption.title 125 | cell.accessoryType = .disclosureIndicator 126 | 127 | default: 128 | break 129 | } 130 | return cell 131 | } 132 | 133 | // MARK: - UITableViewDelegate 134 | 135 | override func tableView(_ tableView: UITableView, shouldHighlightRowAt indexPath: IndexPath) -> Bool { 136 | switch showcases[indexPath.section] { 137 | case is CustomizedTokenField.Type: 138 | return true 139 | case is OptionPickerControl.Type: 140 | return true 141 | default: 142 | return false 143 | } 144 | } 145 | 146 | override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 147 | switch showcases[indexPath.section] { 148 | case is CustomizedTokenField.Type: 149 | let controller = UINavigationController(rootViewController: CustomizedTokenViewController()) 150 | controller.modalPresentationStyle = .fullScreen 151 | present(controller, animated: true, completion: nil) 152 | case is OptionPickerControl.Type: 153 | tableView.deselectRow(at: indexPath, animated: true) 154 | languagePicker.becomeFirstResponder() 155 | default: 156 | break 157 | } 158 | } 159 | 160 | // MARK: - UIResponder Callbacks 161 | 162 | @objc fileprivate func showStoryboard(_ sender: UIButton) { 163 | if let controller = UIStoryboard(name: "Main", bundle: Bundle.main).instantiateInitialViewController() { 164 | controller.modalPresentationStyle = .fullScreen 165 | controller.modalTransitionStyle = .flipHorizontal 166 | present(controller, animated: true, completion: nil) 167 | } 168 | } 169 | 170 | @objc fileprivate func updateLanguage(_ sender: UIControl) { 171 | tableView.reloadData() 172 | } 173 | 174 | } 175 | 176 | 177 | //////////////////////////////////////////////////////////////////////////////// 178 | 179 | 180 | private extension Selector { 181 | static let showStoryboard = #selector(ExampleViewController.showStoryboard(_:)) 182 | static let updateLanguage = #selector(ExampleViewController.updateLanguage(_:)) 183 | } 184 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ICInputAccessory 2 | 3 | Customized text fields used in the [iCook app](https://itunes.apple.com/app/id554065086). 4 | 5 | ![Build Status](https://github.com/polydice/ICInputAccessory/workflows/iOS%20build/badge.svg) 6 | [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg)](https://github.com/Carthage/Carthage) 7 | [![CocoaPods Compatible](https://img.shields.io/cocoapods/v/ICInputAccessory.svg)](https://cocoapods.org/pods/ICInputAccessory) 8 | ![Platform](https://img.shields.io/cocoapods/p/ICInputAccessory.svg) 9 | ![Swift 5](https://img.shields.io/badge/Swift-5-orange.svg) 10 | 11 | ### KeyboardDismissTextField 12 | 13 | * A text field that has a button to dismiss keyboard on the input accessory view. 14 | 15 | ### TokenField 16 | 17 | * A horizontal scrolling UI that groups input texts. 18 | * Easy to add, select and delete tokens. 19 | * Customizable icon and colors. 20 | * Supports storyboard. 21 | 22 | ![ICTokenField](https://raw.githubusercontent.com/polydice/ICInputAccessory/master/screenshots/ICTokenField.gif) 23 | 24 | ### OptionPickerControl 25 | 26 | * An easy to use `UIControl` that displays a `UIPickerView` with given options. 27 | 28 | ## Requirements 29 | 30 | ICInputAccessory | iOS | Xcode | Swift 31 | ---------------- | :--: | :---: | ----- 32 | `~> 1.0.0` | 8.0+ | 7.2 | ![Swift 2.1.1](https://img.shields.io/badge/Swift-2.1.1-orange.svg) 33 | `~> 1.1.0` | 8.0+ | 7.3 | ![Swift 2.2](https://img.shields.io/badge/Swift-2.2-orange.svg) 34 | `~> 1.2.0` | 8.0+ | 8.0 | ![Swift 2.3](https://img.shields.io/badge/Swift-2.3-orange.svg) 35 | `~> 1.3.0` | 8.0+ | 8.0 | ![Swift 3.0](https://img.shields.io/badge/Swift-3.0-orange.svg) 36 | `~> 1.4.0` | 8.0+ | 8.3 | ![Swift 3.1](https://img.shields.io/badge/Swift-3.1-orange.svg) 37 | `~> 1.5.0` | 8.0+ | 9.3 | ![Swift 4.1](https://img.shields.io/badge/Swift-4.1-orange.svg) 38 | `~> 2.0.0` | 8.0+ | 10.0 | ![Swift 4.1](https://img.shields.io/badge/Swift-4.1-orange.svg) 39 | `develop` | 8.0+ | 11.1 | ![Swift 5.0](https://img.shields.io/badge/Swift-5.0-orange.svg) 40 | 41 | ## Installation 42 | 43 | ### Use [Carthage](https://github.com/Carthage/Carthage) 44 | 45 | Create a `Cartfile` with the following specification and run `carthage update ICInputAccessory`. Follow the [instructions](https://github.com/Carthage/Carthage#if-youre-building-for-ios) to add the framework to an iOS project. 46 | 47 | ``` 48 | github "polydice/ICInputAccessory" 49 | ``` 50 | 51 | ### Use [CocoaPods](http://guides.cocoapods.org/) 52 | 53 | **ICInputAccessory** supports subspecs. Create a `Podfile` with the following specification and run `pod install`. 54 | 55 | ```rb 56 | platform :ios, '8.0' 57 | use_frameworks! 58 | 59 | pod 'ICInputAccessory/TokenField' 60 | pod 'ICInputAccessory/KeyboardDismissTextField' 61 | ``` 62 | 63 | ### Use Git Submodule 64 | 65 | ``` 66 | git submodule add -b master git@github.com:polydice/ICInputAccessory.git Dependencies/ICInputAccessory 67 | ``` 68 | 69 | * Everything you need resides in the `Source` directory. Add those files to your project. 70 | * Alternatively, drag **ICInputAccessory.xcodeproj** to your app project as a subproject. Add **ICInputAccessory-iOS** to the **Target Dependencies** in the application target's **Build Phases** settings. 71 | 72 | ## Usage 73 | 74 | ### KeyboardDismissTextField 75 | 76 | ```swift 77 | let textField = KeyboardDismissTextField(frame: rect) 78 | ``` 79 | 80 | ### TokenField 81 | 82 | ```swift 83 | let tokenField = TokenField(frame: rect) 84 | tokenField.delegate = self as? TokenFieldDelegate 85 | ``` 86 | 87 | The characters that complete a token: 88 | 89 | ```swift 90 | /// Characters that complete a new token, defaults are whitespace and commas. 91 | public var delimiters: [String] 92 | ``` 93 | 94 | Tokens: 95 | 96 | ```swift 97 | /// Texts of each created token. 98 | public var texts: [String] { get } 99 | 100 | /// Creates a token with the current input text. 101 | public func completeCurrentInputText() 102 | 103 | /// Removes the input text and all displayed tokens. 104 | public func resetTokens() 105 | ``` 106 | 107 | UI customization: 108 | 109 | ```swift 110 | /// The image on the left of text field. 111 | public var icon: UIImage? { get set } 112 | 113 | /// The placeholder with the default color and font. 114 | public var placeholder: String? { get set } 115 | 116 | /// The placeholder with customized attributes. 117 | public var attributedPlaceholder: NSAttributedString? { get set } 118 | 119 | /// Customized attributes for tokens in the normal state, e.g. .font and .foregroundColor. 120 | public var normalTokenAttributes: [NSAttributedStringKey : NSObject]? { get set } 121 | 122 | /// Customized attributes for tokens in the highlighted state. 123 | public var highlightedTokenAttributes: [NSAttributedStringKey : NSObject]? { get set } 124 | ``` 125 | 126 | Customizable properties in storyboard: 127 | 128 | ```swift 129 | @IBInspectable var icon: UIImage? 130 | @IBInspectable var placeholder: String? 131 | @IBInspectable var textColor: UIColor? 132 | @IBInspectable var cornerRadius: CGFloat 133 | ``` 134 | 135 | See `Example/CustomizedTokenField.swift` for more details. 136 | 137 | #### TokenFieldDelegate 138 | 139 | `TokenField` currently notifies its delegate the following events: 140 | 141 | ```swift 142 | @objc optional func tokenFieldDidBeginEditing(_ tokenField: TokenField) 143 | @objc optional func tokenFieldDidEndEditing(_ tokenField: TokenField) 144 | @objc optional func tokenFieldWillReturn(_ tokenField: TokenField) 145 | @objc optional func tokenField(_ tokenField: TokenField, didChangeInputText text: String) 146 | @objc optional func tokenField(_ tokenField: TokenField, shouldCompleteText text: String) -> Bool 147 | @objc optional func tokenField(_ tokenField: TokenField, didCompleteText text: String) 148 | @objc optional func tokenField(_ tokenField: TokenField, didDeleteText text: String, atIndex index: Int) 149 | ``` 150 | 151 | The displayed delimiter string can be customized by: 152 | 153 | ```swift 154 | @objc optional func tokenField(_ tokenField: TokenField, subsequentDelimiterForCompletedText text: String) -> String 155 | ``` 156 | 157 | ### OptionPickerControl 158 | 159 | An example type that conforms to `OptionDescriptive`: 160 | 161 | ```swift 162 | extension String: OptionDescriptive { 163 | 164 | var title: String { 165 | return self 166 | } 167 | 168 | static var titleForOptionalValue: String { 169 | return "(optional)" 170 | } 171 | 172 | } 173 | ``` 174 | 175 | To initialize `OptionPickerControl` with `CGRect.zero` and add it to the view hierarchy: 176 | 177 | ```swift 178 | let optionPicker = OptionPickerControl() 179 | optionPicker.options = [.optional, Option("Option 1"), Option("Option 2")] 180 | optionPicker.addTarget(self, action: #selector(didChangeOption(_:)), for: .valueChanged) 181 | view.addSubview(optionPicker) 182 | ``` 183 | 184 | To show the `UIPickerView`: 185 | 186 | ```swift 187 | optionPicker.becomeFirstResponder() 188 | ``` 189 | 190 | ### Documentation 191 | 192 | * [ICInputAccessory Reference](https://polydice.github.io/ICInputAccessory) 193 | * [Example Project](https://github.com/polydice/ICInputAccessory/tree/develop/Example) 194 | 195 | ## Development 196 | 197 | * Set up dependencies by running the following command in the project root: 198 | 199 | ``` 200 | make bootstrap 201 | ``` 202 | 203 | * Open **ICInputAccessory.xcworkspace** and run the demo app with the `Example` scheme. 204 | 205 | * See more tasks for building and testing: 206 | 207 | ``` 208 | rake -T 209 | ``` 210 | 211 | ## Contributing 212 | 213 | Thank you for being interested in contributing to this project. We'd love to hear your ideas! 214 | 215 | Please fork this repository, create a branch named like `feature/some-new-feature` and send us a pull request to make this project better. 216 | 217 | ## Contact 218 | 219 | [![Twitter](https://img.shields.io/badge/twitter-@polydice-blue.svg?style=flat)](https://twitter.com/polydice) 220 | [![Join the chat at https://gitter.im/polydice/ICInputAccessory](https://badges.gitter.im/polydice/ICInputAccessory.svg)](https://gitter.im/polydice/ICInputAccessory) 221 | 222 | ## License 223 | 224 | Copyright (c) 2016 [Polydice, Inc.](https://polydice.com) 225 | 226 | **ICInputAccessory** is released under the MIT license. See [LICENSE](https://github.com/polydice/ICInputAccessory/blob/master/LICENSE) for details. 227 | -------------------------------------------------------------------------------- /docs/Protocols.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Protocols Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 |

23 | 24 | ICInputAccessory Docs 25 | 26 | (73% documented) 27 |

28 | 29 |

30 |

31 | 32 |
33 |

34 | 35 |

36 | 37 | 38 | View on GitHub 39 | 40 |

41 | 42 |
43 | 44 | 49 | 50 |
51 | 91 |
92 | 93 |
94 |
95 |

Protocols

96 |

The following protocols are available globally.

97 | 98 |
99 |
100 | 101 |
102 |
103 |
104 |
    105 |
  • 106 |
    107 | 108 | 109 | 110 | OptionDescriptive 111 | 112 |
    113 |
    114 |
    115 |
    116 |
    117 |
    118 |

    The protocol defines the required variables to be displayed in a UIPickerView via OptionPickerControl.

    119 | 120 | See more 121 |
    122 |
    123 |

    Declaration

    124 |
    125 |

    Swift

    126 |
    public protocol OptionDescriptive : Equatable
    127 | 128 |
    129 |
    130 |
    131 | Show on GitHub 132 |
    133 |
    134 |
    135 |
  • 136 |
137 |
138 |
139 |
    140 |
  • 141 |
    142 | 143 | 144 | 145 | TokenFieldDelegate 146 | 147 |
    148 |
    149 |
    150 |
    151 |
    152 |
    153 |

    The protocol defines the messages sent to a delegate. All the methods are optional.

    154 | 155 | See more 156 |
    157 |
    158 |

    Declaration

    159 |
    160 |

    Swift

    161 |
    @objc
    162 | public protocol TokenFieldDelegate : NSObjectProtocol
    163 | 164 |
    165 |
    166 |
    167 | Show on GitHub 168 |
    169 |
    170 |
    171 |
  • 172 |
173 |
174 |
175 |
176 | 177 |
178 |
179 | 183 | 184 | 185 | 186 | -------------------------------------------------------------------------------- /docs/css/jazzy.css: -------------------------------------------------------------------------------- 1 | *, *:before, *:after { 2 | box-sizing: inherit; } 3 | 4 | body { 5 | margin: 0; 6 | background: #fff; 7 | color: #333; 8 | font: 16px/1.7 "Helvetica Neue", Helvetica, Arial, sans-serif; 9 | letter-spacing: .2px; 10 | -webkit-font-smoothing: antialiased; 11 | box-sizing: border-box; } 12 | 13 | h1 { 14 | font-size: 2rem; 15 | font-weight: 700; 16 | margin: 1.275em 0 0.6em; } 17 | 18 | h2 { 19 | font-size: 1.75rem; 20 | font-weight: 700; 21 | margin: 1.275em 0 0.3em; } 22 | 23 | h3 { 24 | font-size: 1.5rem; 25 | font-weight: 700; 26 | margin: 1em 0 0.3em; } 27 | 28 | h4 { 29 | font-size: 1.25rem; 30 | font-weight: 700; 31 | margin: 1.275em 0 0.85em; } 32 | 33 | h5 { 34 | font-size: 1rem; 35 | font-weight: 700; 36 | margin: 1.275em 0 0.85em; } 37 | 38 | h6 { 39 | font-size: 1rem; 40 | font-weight: 700; 41 | margin: 1.275em 0 0.85em; 42 | color: #777; } 43 | 44 | p { 45 | margin: 0 0 1em; } 46 | 47 | ul, ol { 48 | padding: 0 0 0 2em; 49 | margin: 0 0 0.85em; } 50 | 51 | blockquote { 52 | margin: 0 0 0.85em; 53 | padding: 0 15px; 54 | color: #858585; 55 | border-left: 4px solid #e5e5e5; } 56 | 57 | img { 58 | max-width: 100%; } 59 | 60 | a { 61 | color: #4183c4; 62 | text-decoration: none; } 63 | a:hover, a:focus { 64 | outline: 0; 65 | text-decoration: underline; } 66 | 67 | table { 68 | background: #fff; 69 | width: 100%; 70 | border-collapse: collapse; 71 | border-spacing: 0; 72 | overflow: auto; 73 | margin: 0 0 0.85em; } 74 | 75 | tr:nth-child(2n) { 76 | background-color: #fbfbfb; } 77 | 78 | th, td { 79 | padding: 6px 13px; 80 | border: 1px solid #ddd; } 81 | 82 | pre { 83 | margin: 0 0 1.275em; 84 | padding: .85em 1em; 85 | overflow: auto; 86 | background: #f7f7f7; 87 | font-size: .85em; 88 | font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; } 89 | 90 | code { 91 | font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; } 92 | 93 | p > code, li > code { 94 | background: #f7f7f7; 95 | padding: .2em; } 96 | p > code:before, p > code:after, li > code:before, li > code:after { 97 | letter-spacing: -.2em; 98 | content: "\00a0"; } 99 | 100 | pre code { 101 | padding: 0; 102 | white-space: pre; } 103 | 104 | .content-wrapper { 105 | display: flex; 106 | flex-direction: column; } 107 | @media (min-width: 768px) { 108 | .content-wrapper { 109 | flex-direction: row; } } 110 | 111 | .header { 112 | display: flex; 113 | padding: 8px; 114 | font-size: 0.875em; 115 | background: #444; 116 | color: #999; } 117 | 118 | .header-col { 119 | margin: 0; 120 | padding: 0 8px; } 121 | 122 | .header-col--primary { 123 | flex: 1; } 124 | 125 | .header-link { 126 | color: #fff; } 127 | 128 | .header-icon { 129 | padding-right: 6px; 130 | vertical-align: -4px; 131 | height: 16px; } 132 | 133 | .breadcrumbs { 134 | font-size: 0.875em; 135 | padding: 8px 16px; 136 | margin: 0; 137 | background: #fbfbfb; 138 | border-bottom: 1px solid #ddd; } 139 | 140 | .carat { 141 | height: 10px; 142 | margin: 0 5px; } 143 | 144 | .navigation { 145 | order: 2; } 146 | @media (min-width: 768px) { 147 | .navigation { 148 | order: 1; 149 | width: 25%; 150 | max-width: 300px; 151 | padding-bottom: 64px; 152 | overflow: hidden; 153 | word-wrap: normal; 154 | background: #fbfbfb; 155 | border-right: 1px solid #ddd; } } 156 | 157 | .nav-groups { 158 | list-style-type: none; 159 | padding-left: 0; } 160 | 161 | .nav-group-name { 162 | border-bottom: 1px solid #ddd; 163 | padding: 8px 0 8px 16px; } 164 | 165 | .nav-group-name-link { 166 | color: #333; } 167 | 168 | .nav-group-tasks { 169 | margin: 8px 0; 170 | padding: 0 0 0 8px; } 171 | 172 | .nav-group-task { 173 | font-size: 1em; 174 | list-style-type: none; 175 | white-space: nowrap; } 176 | 177 | .nav-group-task-link { 178 | color: #808080; } 179 | 180 | .main-content { 181 | order: 1; } 182 | @media (min-width: 768px) { 183 | .main-content { 184 | order: 2; 185 | flex: 1; 186 | padding-bottom: 60px; } } 187 | 188 | .section { 189 | padding: 0 32px; 190 | border-bottom: 1px solid #ddd; } 191 | 192 | .section-content { 193 | max-width: 834px; 194 | margin: 0 auto; 195 | padding: 16px 0; } 196 | 197 | .section-name { 198 | color: #666; 199 | display: block; } 200 | 201 | .declaration .highlight { 202 | overflow-x: initial; 203 | padding: 8px 0; 204 | margin: 0; 205 | background-color: transparent; 206 | border: none; } 207 | 208 | .task-group-section { 209 | border-top: 1px solid #ddd; } 210 | 211 | .task-group { 212 | padding-top: 0px; } 213 | 214 | .task-name-container a[name]:before { 215 | content: ""; 216 | display: block; } 217 | 218 | .item-container { 219 | padding: 0; } 220 | 221 | .item { 222 | padding-top: 8px; 223 | width: 100%; 224 | list-style-type: none; } 225 | .item a[name]:before { 226 | content: ""; 227 | display: block; } 228 | .item .token { 229 | padding-left: 3px; 230 | margin-left: 0px; 231 | font-size: 1rem; } 232 | .item .declaration-note { 233 | font-size: .85em; 234 | color: #808080; 235 | font-style: italic; } 236 | 237 | .pointer-container { 238 | border-bottom: 1px solid #ddd; 239 | left: -23px; 240 | padding-bottom: 13px; 241 | position: relative; 242 | width: 110%; } 243 | 244 | .pointer { 245 | left: 21px; 246 | top: 7px; 247 | display: block; 248 | position: absolute; 249 | width: 12px; 250 | height: 12px; 251 | border-left: 1px solid #ddd; 252 | border-top: 1px solid #ddd; 253 | background: #fff; 254 | transform: rotate(45deg); } 255 | 256 | .height-container { 257 | display: none; 258 | position: relative; 259 | width: 100%; 260 | overflow: hidden; } 261 | .height-container .section { 262 | background: #fff; 263 | border: 1px solid #ddd; 264 | border-top-width: 0; 265 | padding-top: 10px; 266 | padding-bottom: 5px; 267 | padding: 8px 16px; } 268 | 269 | .aside, .language { 270 | padding: 6px 12px; 271 | margin: 12px 0; 272 | border-left: 5px solid #dddddd; 273 | overflow-y: hidden; } 274 | .aside .aside-title, .language .aside-title { 275 | font-size: 9px; 276 | letter-spacing: 2px; 277 | text-transform: uppercase; 278 | padding-bottom: 0; 279 | margin: 0; 280 | color: #aaa; 281 | -webkit-user-select: none; } 282 | .aside p:last-child, .language p:last-child { 283 | margin-bottom: 0; } 284 | 285 | .language { 286 | border-left: 5px solid #cde9f4; } 287 | .language .aside-title { 288 | color: #4183c4; } 289 | 290 | .aside-warning { 291 | border-left: 5px solid #ff6666; } 292 | .aside-warning .aside-title { 293 | color: #ff0000; } 294 | 295 | .graybox { 296 | border-collapse: collapse; 297 | width: 100%; } 298 | .graybox p { 299 | margin: 0; 300 | word-break: break-word; 301 | min-width: 50px; } 302 | .graybox td { 303 | border: 1px solid #ddd; 304 | padding: 5px 25px 5px 10px; 305 | vertical-align: middle; } 306 | .graybox tr td:first-of-type { 307 | text-align: right; 308 | padding: 7px; 309 | vertical-align: top; 310 | word-break: normal; 311 | width: 40px; } 312 | 313 | .slightly-smaller { 314 | font-size: 0.9em; } 315 | 316 | .footer { 317 | padding: 8px 16px; 318 | background: #444; 319 | color: #ddd; 320 | font-size: 0.8em; } 321 | .footer p { 322 | margin: 8px 0; } 323 | .footer a { 324 | color: #fff; } 325 | 326 | html.dash .header, html.dash .breadcrumbs, html.dash .navigation { 327 | display: none; } 328 | html.dash .height-container { 329 | display: block; } 330 | 331 | form[role=search] input { 332 | font: 16px/1.7 "Helvetica Neue", Helvetica, Arial, sans-serif; 333 | font-size: 14px; 334 | line-height: 24px; 335 | padding: 0 10px; 336 | margin: 0; 337 | border: none; 338 | border-radius: 1em; } 339 | .loading form[role=search] input { 340 | background: white url(../img/spinner.gif) center right 4px no-repeat; } 341 | form[role=search] .tt-menu { 342 | margin: 0; 343 | min-width: 300px; 344 | background: #fbfbfb; 345 | color: #333; 346 | border: 1px solid #ddd; } 347 | form[role=search] .tt-highlight { 348 | font-weight: bold; } 349 | form[role=search] .tt-suggestion { 350 | font: 16px/1.7 "Helvetica Neue", Helvetica, Arial, sans-serif; 351 | padding: 0 8px; } 352 | form[role=search] .tt-suggestion span { 353 | display: table-cell; 354 | white-space: nowrap; } 355 | form[role=search] .tt-suggestion .doc-parent-name { 356 | width: 100%; 357 | text-align: right; 358 | font-weight: normal; 359 | font-size: 0.9em; 360 | padding-left: 16px; } 361 | form[role=search] .tt-suggestion:hover, 362 | form[role=search] .tt-suggestion.tt-cursor { 363 | cursor: pointer; 364 | background-color: #4183c4; 365 | color: #fff; } 366 | form[role=search] .tt-suggestion:hover .doc-parent-name, 367 | form[role=search] .tt-suggestion.tt-cursor .doc-parent-name { 368 | color: #fff; } 369 | -------------------------------------------------------------------------------- /docs/Protocols/OptionDescriptive.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | OptionDescriptive Protocol Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 |

23 | 24 | ICInputAccessory Docs 25 | 26 | (73% documented) 27 |

28 | 29 |

30 |

31 | 32 |
33 |

34 | 35 |

36 | 37 | 38 | View on GitHub 39 | 40 |

41 | 42 |
43 | 44 | 49 | 50 |
51 | 91 |
92 | 93 |
94 |
95 |

OptionDescriptive

96 |
97 |
98 |
public protocol OptionDescriptive : Equatable
99 | 100 |
101 |
102 |

The protocol defines the required variables to be displayed in a UIPickerView via OptionPickerControl.

103 | 104 |
105 |
106 | 107 |
108 |
109 |
110 |
    111 |
  • 112 |
    113 | 114 | 115 | 116 | title 117 | 118 |
    119 |
    120 |
    121 |
    122 |
    123 |
    124 |

    The text for the row in the UIPickerView.

    125 | 126 |
    127 |
    128 |

    Declaration

    129 |
    130 |

    Swift

    131 |
    var title: String { get }
    132 | 133 |
    134 |
    135 |
    136 | Show on GitHub 137 |
    138 |
    139 |
    140 |
  • 141 |
  • 142 |
    143 | 144 | 145 | 146 | titleForOptionalValue 147 | 148 |
    149 |
    150 |
    151 |
    152 |
    153 |
    154 |

    The text for a placeholder row when the picker selection is optional.

    155 | 156 |
    157 |
    158 |

    Declaration

    159 |
    160 |

    Swift

    161 |
    static var titleForOptionalValue: String { get }
    162 | 163 |
    164 |
    165 |
    166 | Show on GitHub 167 |
    168 |
    169 |
    170 |
  • 171 |
172 |
173 |
174 |
175 | 176 |
177 |
178 | 182 | 183 | 184 | 185 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: http://rubygems.org/ 3 | specs: 4 | CFPropertyList (3.0.2) 5 | activesupport (4.2.11.3) 6 | i18n (~> 0.7) 7 | minitest (~> 5.1) 8 | thread_safe (~> 0.3, >= 0.3.4) 9 | tzinfo (~> 1.1) 10 | addressable (2.7.0) 11 | public_suffix (>= 2.0.2, < 5.0) 12 | algoliasearch (1.27.3) 13 | httpclient (~> 2.8, >= 2.8.3) 14 | json (>= 1.5.1) 15 | atomos (0.1.3) 16 | aws-eventstream (1.1.0) 17 | aws-partitions (1.358.0) 18 | aws-sdk-core (3.104.4) 19 | aws-eventstream (~> 1, >= 1.0.2) 20 | aws-partitions (~> 1, >= 1.239.0) 21 | aws-sigv4 (~> 1.1) 22 | jmespath (~> 1.0) 23 | aws-sdk-kms (1.36.0) 24 | aws-sdk-core (~> 3, >= 3.99.0) 25 | aws-sigv4 (~> 1.1) 26 | aws-sdk-s3 (1.78.0) 27 | aws-sdk-core (~> 3, >= 3.104.3) 28 | aws-sdk-kms (~> 1) 29 | aws-sigv4 (~> 1.1) 30 | aws-sigv4 (1.2.2) 31 | aws-eventstream (~> 1, >= 1.0.2) 32 | babosa (1.0.3) 33 | claide (1.0.3) 34 | claide-plugins (0.9.2) 35 | cork 36 | nap 37 | open4 (~> 1.3) 38 | cocoapods (1.9.3) 39 | activesupport (>= 4.0.2, < 5) 40 | claide (>= 1.0.2, < 2.0) 41 | cocoapods-core (= 1.9.3) 42 | cocoapods-deintegrate (>= 1.0.3, < 2.0) 43 | cocoapods-downloader (>= 1.2.2, < 2.0) 44 | cocoapods-plugins (>= 1.0.0, < 2.0) 45 | cocoapods-search (>= 1.0.0, < 2.0) 46 | cocoapods-stats (>= 1.0.0, < 2.0) 47 | cocoapods-trunk (>= 1.4.0, < 2.0) 48 | cocoapods-try (>= 1.1.0, < 2.0) 49 | colored2 (~> 3.1) 50 | escape (~> 0.0.4) 51 | fourflusher (>= 2.3.0, < 3.0) 52 | gh_inspector (~> 1.0) 53 | molinillo (~> 0.6.6) 54 | nap (~> 1.0) 55 | ruby-macho (~> 1.4) 56 | xcodeproj (>= 1.14.0, < 2.0) 57 | cocoapods-core (1.9.3) 58 | activesupport (>= 4.0.2, < 6) 59 | algoliasearch (~> 1.0) 60 | concurrent-ruby (~> 1.1) 61 | fuzzy_match (~> 2.0.4) 62 | nap (~> 1.0) 63 | netrc (~> 0.11) 64 | typhoeus (~> 1.0) 65 | cocoapods-deintegrate (1.0.4) 66 | cocoapods-downloader (1.4.0) 67 | cocoapods-plugins (1.0.0) 68 | nap 69 | cocoapods-search (1.0.0) 70 | cocoapods-stats (1.1.0) 71 | cocoapods-trunk (1.5.0) 72 | nap (>= 0.8, < 2.0) 73 | netrc (~> 0.11) 74 | cocoapods-try (1.2.0) 75 | colored (1.2) 76 | colored2 (3.1.2) 77 | commander-fastlane (4.4.6) 78 | highline (~> 1.7.2) 79 | concurrent-ruby (1.1.7) 80 | cork (0.3.0) 81 | colored2 (~> 3.1) 82 | danger (8.0.4) 83 | claide (~> 1.0) 84 | claide-plugins (>= 0.9.2) 85 | colored2 (~> 3.1) 86 | cork (~> 0.1) 87 | faraday (>= 0.9.0, < 2.0) 88 | faraday-http-cache (~> 2.0) 89 | git (~> 1.7) 90 | kramdown (~> 2.0) 91 | kramdown-parser-gfm (~> 1.0) 92 | no_proxy_fix 93 | octokit (~> 4.7) 94 | terminal-table (~> 1) 95 | declarative (0.0.20) 96 | declarative-option (0.1.0) 97 | digest-crc (0.6.1) 98 | rake (~> 13.0) 99 | domain_name (0.5.20190701) 100 | unf (>= 0.0.5, < 1.0.0) 101 | dotenv (2.7.6) 102 | emoji_regex (3.0.0) 103 | escape (0.0.4) 104 | ethon (0.12.0) 105 | ffi (>= 1.3.0) 106 | excon (0.76.0) 107 | faraday (1.0.1) 108 | multipart-post (>= 1.2, < 3) 109 | faraday-cookie_jar (0.0.6) 110 | faraday (>= 0.7.4) 111 | http-cookie (~> 1.0.0) 112 | faraday-http-cache (2.2.0) 113 | faraday (>= 0.8) 114 | faraday_middleware (1.0.0) 115 | faraday (~> 1.0) 116 | fastimage (2.2.0) 117 | fastlane (2.156.1) 118 | CFPropertyList (>= 2.3, < 4.0.0) 119 | addressable (>= 2.3, < 3.0.0) 120 | aws-sdk-s3 (~> 1.0) 121 | babosa (>= 1.0.3, < 2.0.0) 122 | bundler (>= 1.12.0, < 3.0.0) 123 | colored 124 | commander-fastlane (>= 4.4.6, < 5.0.0) 125 | dotenv (>= 2.1.1, < 3.0.0) 126 | emoji_regex (>= 0.1, < 4.0) 127 | excon (>= 0.71.0, < 1.0.0) 128 | faraday (~> 1.0) 129 | faraday-cookie_jar (~> 0.0.6) 130 | faraday_middleware (~> 1.0) 131 | fastimage (>= 2.1.0, < 3.0.0) 132 | gh_inspector (>= 1.1.2, < 2.0.0) 133 | google-api-client (>= 0.37.0, < 0.39.0) 134 | google-cloud-storage (>= 1.15.0, < 2.0.0) 135 | highline (>= 1.7.2, < 2.0.0) 136 | json (< 3.0.0) 137 | jwt (>= 2.1.0, < 3) 138 | mini_magick (>= 4.9.4, < 5.0.0) 139 | multipart-post (~> 2.0.0) 140 | plist (>= 3.1.0, < 4.0.0) 141 | rubyzip (>= 2.0.0, < 3.0.0) 142 | security (= 0.1.3) 143 | simctl (~> 1.6.3) 144 | slack-notifier (>= 2.0.0, < 3.0.0) 145 | terminal-notifier (>= 2.0.0, < 3.0.0) 146 | terminal-table (>= 1.4.5, < 2.0.0) 147 | tty-screen (>= 0.6.3, < 1.0.0) 148 | tty-spinner (>= 0.8.0, < 1.0.0) 149 | word_wrap (~> 1.0.0) 150 | xcodeproj (>= 1.13.0, < 2.0.0) 151 | xcpretty (~> 0.3.0) 152 | xcpretty-travis-formatter (>= 0.0.3) 153 | ffi (1.13.1) 154 | fourflusher (2.3.1) 155 | fuzzy_match (2.0.4) 156 | gh_inspector (1.1.3) 157 | git (1.7.0) 158 | rchardet (~> 1.8) 159 | google-api-client (0.38.0) 160 | addressable (~> 2.5, >= 2.5.1) 161 | googleauth (~> 0.9) 162 | httpclient (>= 2.8.1, < 3.0) 163 | mini_mime (~> 1.0) 164 | representable (~> 3.0) 165 | retriable (>= 2.0, < 4.0) 166 | signet (~> 0.12) 167 | google-cloud-core (1.5.0) 168 | google-cloud-env (~> 1.0) 169 | google-cloud-errors (~> 1.0) 170 | google-cloud-env (1.3.3) 171 | faraday (>= 0.17.3, < 2.0) 172 | google-cloud-errors (1.0.1) 173 | google-cloud-storage (1.27.0) 174 | addressable (~> 2.5) 175 | digest-crc (~> 0.4) 176 | google-api-client (~> 0.33) 177 | google-cloud-core (~> 1.2) 178 | googleauth (~> 0.9) 179 | mini_mime (~> 1.0) 180 | googleauth (0.13.1) 181 | faraday (>= 0.17.3, < 2.0) 182 | jwt (>= 1.4, < 3.0) 183 | memoist (~> 0.16) 184 | multi_json (~> 1.11) 185 | os (>= 0.9, < 2.0) 186 | signet (~> 0.14) 187 | highline (1.7.10) 188 | http-cookie (1.0.3) 189 | domain_name (~> 0.5) 190 | httpclient (2.8.3) 191 | i18n (0.9.5) 192 | concurrent-ruby (~> 1.0) 193 | jazzy (0.13.5) 194 | cocoapods (~> 1.5) 195 | mustache (~> 1.1) 196 | open4 197 | redcarpet (~> 3.4) 198 | rouge (>= 2.0.6, < 4.0) 199 | sassc (~> 2.1) 200 | sqlite3 (~> 1.3) 201 | xcinvoke (~> 0.3.0) 202 | jmespath (1.4.0) 203 | json (2.3.1) 204 | jwt (2.2.2) 205 | kramdown (2.3.0) 206 | rexml 207 | kramdown-parser-gfm (1.1.0) 208 | kramdown (~> 2.0) 209 | liferaft (0.0.6) 210 | memoist (0.16.2) 211 | mini_magick (4.10.1) 212 | mini_mime (1.0.2) 213 | minitest (5.14.1) 214 | molinillo (0.6.6) 215 | multi_json (1.15.0) 216 | multipart-post (2.0.0) 217 | mustache (1.1.1) 218 | nanaimo (0.3.0) 219 | nap (1.1.0) 220 | naturally (2.2.0) 221 | netrc (0.11.0) 222 | no_proxy_fix (0.1.2) 223 | octokit (4.18.0) 224 | faraday (>= 0.9) 225 | sawyer (~> 0.8.0, >= 0.5.3) 226 | open4 (1.3.4) 227 | os (1.1.1) 228 | plist (3.5.0) 229 | public_suffix (4.0.5) 230 | rake (13.0.1) 231 | rchardet (1.8.0) 232 | redcarpet (3.5.0) 233 | representable (3.0.4) 234 | declarative (< 0.1.0) 235 | declarative-option (< 0.2.0) 236 | uber (< 0.2.0) 237 | retriable (3.1.2) 238 | rexml (3.2.4) 239 | rouge (2.0.7) 240 | ruby-macho (1.4.0) 241 | rubyzip (2.3.0) 242 | sassc (2.4.0) 243 | ffi (~> 1.9) 244 | sawyer (0.8.2) 245 | addressable (>= 2.3.5) 246 | faraday (> 0.8, < 2.0) 247 | security (0.1.3) 248 | signet (0.14.0) 249 | addressable (~> 2.3) 250 | faraday (>= 0.17.3, < 2.0) 251 | jwt (>= 1.5, < 3.0) 252 | multi_json (~> 1.10) 253 | simctl (1.6.8) 254 | CFPropertyList 255 | naturally 256 | slack-notifier (2.3.2) 257 | sqlite3 (1.4.2) 258 | terminal-notifier (2.0.0) 259 | terminal-table (1.8.0) 260 | unicode-display_width (~> 1.1, >= 1.1.1) 261 | thread_safe (0.3.6) 262 | tty-cursor (0.7.1) 263 | tty-screen (0.8.1) 264 | tty-spinner (0.9.3) 265 | tty-cursor (~> 0.7) 266 | typhoeus (1.4.0) 267 | ethon (>= 0.9.0) 268 | tzinfo (1.2.7) 269 | thread_safe (~> 0.1) 270 | uber (0.1.0) 271 | unf (0.1.4) 272 | unf_ext 273 | unf_ext (0.0.7.7) 274 | unicode-display_width (1.7.0) 275 | word_wrap (1.0.0) 276 | xcinvoke (0.3.0) 277 | liferaft (~> 0.0.6) 278 | xcodeproj (1.18.0) 279 | CFPropertyList (>= 2.3.3, < 4.0) 280 | atomos (~> 0.1.3) 281 | claide (>= 1.0.2, < 2.0) 282 | colored2 (~> 3.1) 283 | nanaimo (~> 0.3.0) 284 | xcpretty (0.3.0) 285 | rouge (~> 2.0.7) 286 | xcpretty-travis-formatter (1.0.0) 287 | xcpretty (~> 0.2, >= 0.0.7) 288 | 289 | PLATFORMS 290 | ruby 291 | 292 | DEPENDENCIES 293 | cocoapods 294 | danger 295 | fastlane 296 | jazzy (>= 0.8.0) 297 | 298 | BUNDLED WITH 299 | 2.1.4 300 | -------------------------------------------------------------------------------- /docs/Classes/KeyboardDismissTextField.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | KeyboardDismissTextField Class Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 |

23 | 24 | ICInputAccessory Docs 25 | 26 | (73% documented) 27 |

28 | 29 |

30 |

31 | 32 |
33 |

34 | 35 |

36 | 37 | 38 | View on GitHub 39 | 40 |

41 | 42 |
43 | 44 | 49 | 50 |
51 | 91 |
92 | 93 |
94 |
95 |

KeyboardDismissTextField

96 |
97 |
98 |
@IBDesignable
 99 | open class KeyboardDismissTextField : UITextField
100 | 101 |
102 |
103 |

A text field that has a button to dismiss keyboard on the input accessory view.

104 | 105 |
106 |
107 | 108 |
109 |
110 |
111 |
    112 |
  • 113 |
    114 | 115 | 116 | 117 | keyboardAccessoryView 118 | 119 |
    120 |
    121 |
    122 |
    123 |
    124 |
    125 |

    The custom input accessory view with a button to dismiss keyboard.

    126 | 127 |
    128 |
    129 |

    Declaration

    130 |
    131 |

    Swift

    132 |
    @IBOutlet
    133 | public var keyboardAccessoryView: KeyboardDismissAccessoryView! { get set }
    134 | 135 |
    136 |
    137 |
    138 | Show on GitHub 139 |
    140 |
    141 |
    142 |
  • 143 |
144 |
145 |
146 |
147 | 148 | 149 | 150 |

Initialization

151 |
152 |
153 |
    154 |
  • 155 |
    156 | 157 | 158 | 159 | init(frame:) 160 | 161 |
    162 |
    163 |
    164 |
    165 |
    166 |
    167 |

    Initializes and returns a newly allocated view object with the specified frame rectangle.

    168 | 169 |
    170 |
    171 |

    Declaration

    172 |
    173 |

    Swift

    174 |
    public override init(frame: CGRect)
    175 | 176 |
    177 |
    178 |
    179 | Show on GitHub 180 |
    181 |
    182 |
    183 |
  • 184 |
  • 185 |
    186 | 187 | 188 | 189 | init(coder:) 190 | 191 |
    192 |
    193 |
    194 |
    195 |
    196 |
    197 |

    Returns an object initialized from data in a given unarchiver.

    198 | 199 |
    200 |
    201 |

    Declaration

    202 |
    203 |

    Swift

    204 |
    public required init?(coder aDecoder: NSCoder)
    205 | 206 |
    207 |
    208 |
    209 | Show on GitHub 210 |
    211 |
    212 |
    213 |
  • 214 |
215 |
216 |
217 |
218 | 219 |
220 |
221 | 225 | 226 | 227 | 228 | -------------------------------------------------------------------------------- /docs/search.json: -------------------------------------------------------------------------------- 1 | {"Structs/Option.html#/s:16ICInputAccessory6OptionV8optionalACyxGvpZ":{"name":"optional","abstract":"

Returns an option that displays the optional value of an OptionDescriptive type.

","parent_name":"Option"},"Structs/Option.html#/s:16ICInputAccessory6OptionVyACyxGxcfc":{"name":"init(_:)","abstract":"

Returns an initialized option with an instance of an OptionDescriptive type.

","parent_name":"Option"},"Structs/Option.html#/s:16ICInputAccessory6OptionV5titleSSvp":{"name":"title","abstract":"

Conformance to OptionDescriptive.

","parent_name":"Option"},"Structs/Option.html#/s:16ICInputAccessory6OptionV5valuexSgvp":{"name":"value","abstract":"

The OptionDescriptive value of the option. Returns nil when it’s the optional placeholder.

","parent_name":"Option"},"Structs/Option.html#/s:16ICInputAccessory6OptionV2eeoiySbACyxG_AEtFZ":{"name":"==(_:_:)","abstract":"

Returns true when two options’ values are equal.

","parent_name":"Option"},"Structs/Option.html":{"name":"Option","abstract":"

An option struct that carries the OptionDescriptive

"},"Protocols/TokenFieldDelegate.html#/c:@M@ICInputAccessory@objc(pl)TokenFieldDelegate(im)tokenFieldDidBeginEditing:":{"name":"tokenFieldDidBeginEditing(_:)","abstract":"

Tells the delegate that editing began for the token field.

","parent_name":"TokenFieldDelegate"},"Protocols/TokenFieldDelegate.html#/c:@M@ICInputAccessory@objc(pl)TokenFieldDelegate(im)tokenFieldDidEndEditing:":{"name":"tokenFieldDidEndEditing(_:)","abstract":"

Tells the delegate that editing stopped for the token field.

","parent_name":"TokenFieldDelegate"},"Protocols/TokenFieldDelegate.html#/c:@M@ICInputAccessory@objc(pl)TokenFieldDelegate(im)tokenFieldWillReturn:":{"name":"tokenFieldWillReturn(_:)","abstract":"

Tells the delegate that the token field will process the pressing of the return button.

","parent_name":"TokenFieldDelegate"},"Protocols/TokenFieldDelegate.html#/c:@M@ICInputAccessory@objc(pl)TokenFieldDelegate(im)tokenField:didChangeInputText:":{"name":"tokenField(_:didChangeInputText:)","abstract":"

Tells the delegate the input text is changed.

","parent_name":"TokenFieldDelegate"},"Protocols/TokenFieldDelegate.html#/c:@M@ICInputAccessory@objc(pl)TokenFieldDelegate(im)tokenField:shouldCompleteText:":{"name":"tokenField(_:shouldCompleteText:)","abstract":"

Asks the delegate if the text should become a token in the token field.

","parent_name":"TokenFieldDelegate"},"Protocols/TokenFieldDelegate.html#/c:@M@ICInputAccessory@objc(pl)TokenFieldDelegate(im)tokenField:didCompleteText:":{"name":"tokenField(_:didCompleteText:)","abstract":"

Tells the delegate that the text becomes a token in the token field.

","parent_name":"TokenFieldDelegate"},"Protocols/TokenFieldDelegate.html#/c:@M@ICInputAccessory@objc(pl)TokenFieldDelegate(im)tokenField:didDeleteText:atIndex:":{"name":"tokenField(_:didDeleteText:atIndex:)","abstract":"

Tells the delegate that the token at certain index is removed from the token field.

","parent_name":"TokenFieldDelegate"},"Protocols/TokenFieldDelegate.html#/c:@M@ICInputAccessory@objc(pl)TokenFieldDelegate(im)tokenField:subsequentDelimiterForCompletedText:":{"name":"tokenField(_:subsequentDelimiterForCompletedText:)","abstract":"

Asks the delegate for the subsequent delimiter string for a completed text in the token field.

","parent_name":"TokenFieldDelegate"},"Protocols/OptionDescriptive.html#/s:16ICInputAccessory17OptionDescriptiveP5titleSSvp":{"name":"title","abstract":"

The text for the row in the UIPickerView.

","parent_name":"OptionDescriptive"},"Protocols/OptionDescriptive.html#/s:16ICInputAccessory17OptionDescriptiveP21titleForOptionalValueSSvpZ":{"name":"titleForOptionalValue","abstract":"

The text for a placeholder row when the picker selection is optional.

","parent_name":"OptionDescriptive"},"Protocols/OptionDescriptive.html":{"name":"OptionDescriptive","abstract":"

The protocol defines the required variables to be displayed in a UIPickerView via OptionPickerControl.

"},"Protocols/TokenFieldDelegate.html":{"name":"TokenFieldDelegate","abstract":"

The protocol defines the messages sent to a delegate. All the methods are optional.

"},"Classes/TokenField.html#/s:16ICInputAccessory10TokenFieldC8delegateAA0cD8Delegate_pSgXwvp":{"name":"delegate","abstract":"

The receiver’s delegate.

","parent_name":"TokenField"},"Classes/TokenField.html#/s:16ICInputAccessory10TokenFieldC10delimitersSaySSGvp":{"name":"delimiters","abstract":"

Characters that completes a new token, defaults are whitespace and commas.

","parent_name":"TokenField"},"Classes/TokenField.html#/s:16ICInputAccessory10TokenFieldC5textsSaySSGvp":{"name":"texts","abstract":"

Texts of each created token.

","parent_name":"TokenField"},"Classes/TokenField.html#/c:@M@ICInputAccessory@objc(cs)TokenField(py)icon":{"name":"icon","abstract":"

The image on the left of text field.

","parent_name":"TokenField"},"Classes/TokenField.html#/s:16ICInputAccessory10TokenFieldC04textD0So06UITextD0Cvp":{"name":"textField","abstract":"

The text field that handles text inputs.","parent_name":"TokenField"},"Classes/TokenField.html#/c:@M@ICInputAccessory@objc(cs)TokenField(py)placeholder":{"name":"placeholder","abstract":"

The placeholder with the default color and font.

","parent_name":"TokenField"},"Classes/TokenField.html#/s:16ICInputAccessory10TokenFieldC21attributedPlaceholderSo18NSAttributedStringCSgvp":{"name":"attributedPlaceholder","abstract":"

The placeholder with customized attributes.

","parent_name":"TokenField"},"Classes/TokenField.html#/s:16ICInputAccessory10TokenFieldC06normalC10AttributesSDySo21NSAttributedStringKeyaSo8NSObjectCGSgvp":{"name":"normalTokenAttributes","abstract":"

Customized attributes for tokens in the normal state, e.g. .font and .foregroundColor.

","parent_name":"TokenField"},"Classes/TokenField.html#/s:16ICInputAccessory10TokenFieldC011highlightedC10AttributesSDySo21NSAttributedStringKeyaSo8NSObjectCGSgvp":{"name":"highlightedTokenAttributes","abstract":"

Customized attributes for tokens in the highlighted state.

","parent_name":"TokenField"},"Classes/TokenField.html#/c:@M@ICInputAccessory@objc(cs)TokenField(py)tintColor":{"name":"tintColor","abstract":"

The tint color of icon image and text field.

","parent_name":"TokenField"},"Classes/TokenField.html#/c:@M@ICInputAccessory@objc(cs)TokenField(im)initWithFrame:":{"name":"init(frame:)","abstract":"

Initializes and returns a newly allocated view object with the specified frame rectangle.

","parent_name":"TokenField"},"Classes/TokenField.html#/c:@M@ICInputAccessory@objc(cs)TokenField(im)initWithCoder:":{"name":"init(coder:)","abstract":"

Returns an object initialized from data in a given unarchiver.

","parent_name":"TokenField"},"Classes/TokenField.html#/s:16ICInputAccessory10TokenFieldC24completeCurrentInputTextyyF":{"name":"completeCurrentInputText()","abstract":"

Creates a token with the current input text.

","parent_name":"TokenField"},"Classes/TokenField.html#/s:16ICInputAccessory10TokenFieldC11resetTokensyyF":{"name":"resetTokens()","abstract":"

Removes the input text and all displayed tokens.

","parent_name":"TokenField"},"Classes/OptionPickerControl.html#/s:16ICInputAccessory19OptionPickerControlCACyxGycfc":{"name":"init()","abstract":"

Returns an initialized OptionPickerControl.

","parent_name":"OptionPickerControl"},"Classes/OptionPickerControl.html#/s:16ICInputAccessory19OptionPickerControlC5coderACyxGSgSo7NSCoderC_tcfc":{"name":"init(coder:)","abstract":"

Not supported. OptionPickerControl is not compatible with storyboards.

","parent_name":"OptionPickerControl"},"Classes/OptionPickerControl.html#/s:16ICInputAccessory19OptionPickerControlC7optionsSayAA0C0VyxGGvp":{"name":"options","abstract":"

Options that shows in the UIPickerView.

","parent_name":"OptionPickerControl"},"Classes/OptionPickerControl.html#/s:16ICInputAccessory19OptionPickerControlC08selectedC0AA0C0VyxGvp":{"name":"selectedOption","abstract":"

The currently selected item in the options.

","parent_name":"OptionPickerControl"},"Classes/OptionPickerControl.html#/s:16ICInputAccessory19OptionPickerControlC6pickerSo12UIPickerViewCvp":{"name":"picker","abstract":"

A reference to the displayed UIPickerView for customization.

","parent_name":"OptionPickerControl"},"Classes/OptionPickerControl.html#/s:16ICInputAccessory19OptionPickerControlC18numberOfComponents2inSiSo12UIPickerViewC_tF":{"name":"numberOfComponents(in:)","abstract":"

Currently OptionPickerControl only supports one component.

","parent_name":"OptionPickerControl"},"Classes/KeyboardDismissTextField.html#/c:@M@ICInputAccessory@objc(cs)KeyboardDismissTextField(py)keyboardAccessoryView":{"name":"keyboardAccessoryView","abstract":"

The custom input accessory view with a button to dismiss keyboard.

","parent_name":"KeyboardDismissTextField"},"Classes/KeyboardDismissTextField.html#/c:@M@ICInputAccessory@objc(cs)KeyboardDismissTextField(im)initWithFrame:":{"name":"init(frame:)","abstract":"

Initializes and returns a newly allocated view object with the specified frame rectangle.

","parent_name":"KeyboardDismissTextField"},"Classes/KeyboardDismissTextField.html#/c:@M@ICInputAccessory@objc(cs)KeyboardDismissTextField(im)initWithCoder:":{"name":"init(coder:)","abstract":"

Returns an object initialized from data in a given unarchiver.

","parent_name":"KeyboardDismissTextField"},"Classes/KeyboardDismissAccessoryView.html#/c:@M@ICInputAccessory@objc(cs)KeyboardDismissAccessoryView(py)buttonColor":{"name":"buttonColor","abstract":"

The background color of the button to dismiss keyboard.

","parent_name":"KeyboardDismissAccessoryView"},"Classes/KeyboardDismissAccessoryView.html#/s:16ICInputAccessory015KeyboardDismissB4ViewC13dismissButtonSo8UIButtonCvp":{"name":"dismissButton","abstract":"

The button to dismiss keyboard.

","parent_name":"KeyboardDismissAccessoryView"},"Classes/KeyboardDismissAccessoryView.html#/c:@M@ICInputAccessory@objc(cs)KeyboardDismissAccessoryView(im)initWithFrame:":{"name":"init(frame:)","abstract":"

Initializes and returns a newly allocated view object with the specified frame rectangle.

","parent_name":"KeyboardDismissAccessoryView"},"Classes/KeyboardDismissAccessoryView.html#/c:@M@ICInputAccessory@objc(cs)KeyboardDismissAccessoryView(im)initWithCoder:":{"name":"init(coder:)","abstract":"

Returns an object initialized from data in a given unarchiver.

","parent_name":"KeyboardDismissAccessoryView"},"Classes/KeyboardDismissAccessoryView.html":{"name":"KeyboardDismissAccessoryView","abstract":"

A customized keyboard accessory view with a dismiss button.

"},"Classes/KeyboardDismissTextField.html":{"name":"KeyboardDismissTextField","abstract":"

A text field that has a button to dismiss keyboard on the input accessory view.

"},"Classes/OptionPickerControl.html":{"name":"OptionPickerControl","abstract":"

A UIControl that displays a UIPickerView and notifies changed selection and via UIControlEvents .valueChanged.

"},"Classes/TokenField.html":{"name":"TokenField","abstract":"

A text field that groups input texts with delimiters.

"},"Classes.html":{"name":"Classes","abstract":"

The following classes are available globally.

"},"Protocols.html":{"name":"Protocols","abstract":"

The following protocols are available globally.

"},"Structs.html":{"name":"Structures","abstract":"

The following structures are available globally.

"}} 2 | -------------------------------------------------------------------------------- /Example/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | -------------------------------------------------------------------------------- /docs/Classes.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Classes Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 |

23 | 24 | ICInputAccessory Docs 25 | 26 | (73% documented) 27 |

28 | 29 |

30 |

31 | 32 |
33 |

34 | 35 |

36 | 37 | 38 | View on GitHub 39 | 40 |

41 | 42 |
43 | 44 | 49 | 50 |
51 | 91 |
92 | 93 |
94 |
95 |

Classes

96 |

The following classes are available globally.

97 | 98 |
99 |
100 | 101 |
102 |
103 |
104 |
    105 |
  • 106 |
    107 | 108 | 109 | 110 | KeyboardDismissAccessoryView 111 | 112 |
    113 |
    114 |
    115 |
    116 |
    117 |
    118 |

    A customized keyboard accessory view with a dismiss button.

    119 | 120 | See more 121 |
    122 |
    123 |

    Declaration

    124 |
    125 |

    Swift

    126 |
    @IBDesignable
    127 | open class KeyboardDismissAccessoryView : UIView
    128 | 129 |
    130 |
    131 |
    132 | Show on GitHub 133 |
    134 |
    135 |
    136 |
  • 137 |
138 |
139 |
140 |
    141 |
  • 142 |
    143 | 144 | 145 | 146 | KeyboardDismissTextField 147 | 148 |
    149 |
    150 |
    151 |
    152 |
    153 |
    154 |

    A text field that has a button to dismiss keyboard on the input accessory view.

    155 | 156 | See more 157 |
    158 |
    159 |

    Declaration

    160 |
    161 |

    Swift

    162 |
    @IBDesignable
    163 | open class KeyboardDismissTextField : UITextField
    164 | 165 |
    166 |
    167 |
    168 | Show on GitHub 169 |
    170 |
    171 |
    172 |
  • 173 |
174 |
175 |
176 |
    177 |
  • 178 |
    179 | 180 | 181 | 182 | OptionPickerControl 183 | 184 |
    185 |
    186 |
    187 |
    188 |
    189 |
    190 |

    A UIControl that displays a UIPickerView and notifies changed selection and via UIControlEvents .valueChanged.

    191 | 192 | See more 193 |
    194 |
    195 |

    Declaration

    196 |
    197 |

    Swift

    198 |
    open class OptionPickerControl<T> : UIControl, UIPickerViewDataSource, UIPickerViewDelegate where T : OptionDescriptive
    199 | 200 |
    201 |
    202 |
    203 | Show on GitHub 204 |
    205 |
    206 |
    207 |
  • 208 |
209 |
210 |
211 |
    212 |
  • 213 |
    214 | 215 | 216 | 217 | TokenField 218 | 219 |
    220 |
    221 |
    222 |
    223 |
    224 |
    225 |

    A text field that groups input texts with delimiters.

    226 | 227 | See more 228 |
    229 |
    230 |

    Declaration

    231 |
    232 |

    Swift

    233 |
    @IBDesignable
    234 | open class TokenField : UIView, UITextFieldDelegate, BackspaceTextFieldDelegate
    235 | 236 |
    237 |
    238 |
    239 | Show on GitHub 240 |
    241 |
    242 |
    243 |
  • 244 |
245 |
246 |
247 |
248 | 249 |
250 |
251 | 255 | 256 | 257 | 258 | -------------------------------------------------------------------------------- /docs/Classes/KeyboardDismissAccessoryView.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | KeyboardDismissAccessoryView Class Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 |

23 | 24 | ICInputAccessory Docs 25 | 26 | (73% documented) 27 |

28 | 29 |

30 |

31 | 32 |
33 |

34 | 35 |

36 | 37 | 38 | View on GitHub 39 | 40 |

41 | 42 |
43 | 44 | 49 | 50 |
51 | 91 |
92 | 93 |
94 |
95 |

KeyboardDismissAccessoryView

96 |
97 |
98 |
@IBDesignable
 99 | open class KeyboardDismissAccessoryView : UIView
100 | 101 |
102 |
103 |

A customized keyboard accessory view with a dismiss button.

104 | 105 |
106 |
107 | 108 |
109 |
110 |
111 |
    112 |
  • 113 |
    114 | 115 | 116 | 117 | buttonColor 118 | 119 |
    120 |
    121 |
    122 |
    123 |
    124 |
    125 |

    The background color of the button to dismiss keyboard.

    126 | 127 |
    128 |
    129 |

    Declaration

    130 |
    131 |

    Swift

    132 |
    @IBInspectable
    133 | public var buttonColor: UIColor { get set }
    134 | 135 |
    136 |
    137 |
    138 | Show on GitHub 139 |
    140 |
    141 |
    142 |
  • 143 |
  • 144 |
    145 | 146 | 147 | 148 | dismissButton 149 | 150 |
    151 |
    152 |
    153 |
    154 |
    155 |
    156 |

    The button to dismiss keyboard.

    157 | 158 |
    159 |
    160 |

    Declaration

    161 |
    162 |

    Swift

    163 |
    public private(set) lazy var dismissButton: UIButton { get set }
    164 | 165 |
    166 |
    167 |
    168 | Show on GitHub 169 |
    170 |
    171 |
    172 |
  • 173 |
174 |
175 |
176 |
177 | 178 | 179 | 180 |

Initialization

181 |
182 |
183 |
    184 |
  • 185 |
    186 | 187 | 188 | 189 | init(frame:) 190 | 191 |
    192 |
    193 |
    194 |
    195 |
    196 |
    197 |

    Initializes and returns a newly allocated view object with the specified frame rectangle.

    198 | 199 |
    200 |
    201 |

    Declaration

    202 |
    203 |

    Swift

    204 |
    public override init(frame: CGRect)
    205 | 206 |
    207 |
    208 |
    209 | Show on GitHub 210 |
    211 |
    212 |
    213 |
  • 214 |
  • 215 |
    216 | 217 | 218 | 219 | init(coder:) 220 | 221 |
    222 |
    223 |
    224 |
    225 |
    226 |
    227 |

    Returns an object initialized from data in a given unarchiver.

    228 | 229 |
    230 |
    231 |

    Declaration

    232 |
    233 |

    Swift

    234 |
    public required init?(coder aDecoder: NSCoder)
    235 | 236 |
    237 |
    238 |
    239 | Show on GitHub 240 |
    241 |
    242 |
    243 |
  • 244 |
245 |
246 |
247 |
248 | 249 |
250 |
251 | 255 | 256 | 257 | 258 | --------------------------------------------------------------------------------