├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── Example ├── Podfile ├── Podfile.lock ├── Pods │ ├── Local Podspecs │ │ └── RAGTextField.podspec.json │ ├── Manifest.lock │ ├── Pods.xcodeproj │ │ ├── project.pbxproj │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── RAGTextField.xcscheme │ └── Target Support Files │ │ ├── Pods-RAGTextField_Example │ │ ├── Info.plist │ │ ├── Pods-RAGTextField_Example-Info.plist │ │ ├── Pods-RAGTextField_Example-acknowledgements.markdown │ │ ├── Pods-RAGTextField_Example-acknowledgements.plist │ │ ├── Pods-RAGTextField_Example-dummy.m │ │ ├── Pods-RAGTextField_Example-frameworks.sh │ │ ├── Pods-RAGTextField_Example-resources.sh │ │ ├── Pods-RAGTextField_Example-umbrella.h │ │ ├── Pods-RAGTextField_Example.debug.xcconfig │ │ ├── Pods-RAGTextField_Example.modulemap │ │ └── Pods-RAGTextField_Example.release.xcconfig │ │ ├── Pods-RAGTextField_Tests │ │ ├── Info.plist │ │ ├── Pods-RAGTextField_Tests-Info.plist │ │ ├── Pods-RAGTextField_Tests-acknowledgements.markdown │ │ ├── Pods-RAGTextField_Tests-acknowledgements.plist │ │ ├── Pods-RAGTextField_Tests-dummy.m │ │ ├── Pods-RAGTextField_Tests-frameworks.sh │ │ ├── Pods-RAGTextField_Tests-resources.sh │ │ ├── Pods-RAGTextField_Tests-umbrella.h │ │ ├── Pods-RAGTextField_Tests.debug.xcconfig │ │ ├── Pods-RAGTextField_Tests.modulemap │ │ └── Pods-RAGTextField_Tests.release.xcconfig │ │ └── RAGTextField │ │ ├── Info.plist │ │ ├── RAGTextField-Info.plist │ │ ├── RAGTextField-dummy.m │ │ ├── RAGTextField-prefix.pch │ │ ├── RAGTextField-umbrella.h │ │ ├── RAGTextField.debug.xcconfig │ │ ├── RAGTextField.modulemap │ │ ├── RAGTextField.release.xcconfig │ │ └── RAGTextField.xcconfig ├── RAGTextField.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── xcshareddata │ │ └── xcschemes │ │ └── RAGTextField-Example.xcscheme ├── RAGTextField.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── WorkspaceSettings.xcsettings ├── RAGTextField │ ├── AppDelegate.swift │ ├── Base.lproj │ │ ├── LaunchScreen.xib │ │ └── Main.storyboard │ ├── ColorPalette.swift │ ├── Hint.storyboard │ ├── HintViewController.swift │ ├── Images.xcassets │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ ├── Contents.json │ │ ├── calendar.imageset │ │ │ ├── Contents.json │ │ │ ├── calendar@2x.png │ │ │ └── calendar@3x.png │ │ └── search.imageset │ │ │ ├── Contents.json │ │ │ ├── lupe@2x.png │ │ │ └── lupe@3x.png │ ├── Info.plist │ ├── KeyboardEvader.swift │ ├── LeftAndRightViews.storyboard │ ├── LeftAndRightViewsViewController.swift │ ├── Outline.storyboard │ ├── OutlineViewController.swift │ ├── Placeholder.storyboard │ ├── PlaceholderViewController.swift │ ├── TextAlignment.storyboard │ ├── TextAlignmentViewController.swift │ ├── TextPadding.storyboard │ ├── TextPaddingViewController.swift │ ├── UIColor+Hex.swift │ ├── Underline.storyboard │ ├── UnderlineViewController.swift │ └── ViewController.swift └── Tests │ ├── Info.plist │ └── Tests.swift ├── LICENSE ├── Package.swift ├── RAGTextField.podspec ├── README.md ├── Sources └── RAGTextField │ ├── Assets │ └── .gitkeep │ └── Classes │ ├── .gitkeep │ ├── Extensions │ └── String+RAGTextField.swift │ ├── PlaceholderView.swift │ ├── RAGTextField.swift │ ├── RAGTextFieldPlaceholderMode.swift │ ├── RAGTextPaddingMode.swift │ └── Views │ ├── OutlineView.swift │ └── UnderlineView.swift └── _Pods.xcodeproj /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xccheckout 23 | *.xcscmblueprint 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | *.ipa 28 | *.dSYM.zip 29 | *.dSYM 30 | 31 | ## Playgrounds 32 | timeline.xctimeline 33 | playground.xcworkspace 34 | 35 | # Swift Package Manager 36 | # 37 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 38 | # Packages/ 39 | # Package.pins 40 | .build/ 41 | 42 | # CocoaPods 43 | # 44 | # We recommend against adding the Pods directory to your .gitignore. However 45 | # you should judge for yourself, the pros and cons are mentioned at: 46 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 47 | # 48 | # Pods/ 49 | 50 | # Carthage 51 | # 52 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 53 | # Carthage/Checkouts 54 | 55 | Carthage/Build 56 | 57 | # fastlane 58 | # 59 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 60 | # screenshots whenever they are needed. 61 | # For more information about the recommended setup visit: 62 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 63 | 64 | fastlane/report.xml 65 | fastlane/Preview.html 66 | fastlane/screenshots 67 | fastlane/test_output 68 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # references: 2 | # * http://www.objc.io/issue-6/travis-ci.html 3 | 4 | os: osx 5 | osx_image: xcode10.2 6 | language: objective-c 7 | 8 | script: 9 | - set -o pipefail && xcodebuild test -workspace Example/RAGTextField.xcworkspace -scheme RAGTextField-Example -sdk iphonesimulator12.2 ONLY_ACTIVE_ARCH=NO | xcpretty 10 | - pod lib lint 11 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | All notable changes to this project will be documented in this file. 3 | 4 | This project adheres to [Semantic Versioning](http://semver.org/). 5 | 6 | ## 0.14.0 7 | 8 | - Add support for Swift Package Manager 9 | - Add placeholder mode "scalesAlways" which always transforms the placeholder 10 | 11 | ## 0.13.0 12 | 13 | Updated to Swift 5. 14 | 15 | ## 0.12.1 16 | 17 | ### Fixed 18 | - Minor bugfix 19 | 20 | ## 0.12.0 21 | 22 | ### Added 23 | - Added support of multi-line hints. 24 | 25 | ## 0.11.0 26 | 27 | ### Added 28 | - Added property `transformedPlaceholderColor` that sets the placeholder color when the text field is being edited and the placeholder is in its floating position. 29 | - Added property `layoutAlwaysIncludesHint` that always keeps the hint label in the layout even if the `hint` is `nil`. 30 | 31 | ### Fixed 32 | - Fixed the resizing of the placeholder when its text or font are changed. 33 | 34 | ## 0.10.0 35 | 36 | After updating to this version, you may have to adjust the `placeholderMode` values of your text fields because the default placeholder mode has changed. 37 | 38 | ### Added 39 | - Added the `textField` property to the `UnderlineView`. If not `nil`, the underline updates its appearance in accordance with the editing state of the text field. 40 | 41 | ### Changed 42 | - The default placeholder mode has been changed from "when not empty" to "when editing". 43 | 44 | ## 0.9.1 45 | 46 | ### Fixed 47 | - Fixed placeholder animation on iOS 9 48 | 49 | ## 0.9.0 50 | 51 | ### Changed 52 | - Implemented a much more helpful example project for the pod. Please "try". 53 | 54 | ### Fixed 55 | - The horizontal text position would not be updated if the left or right views of the text field were shown or hidden 56 | 57 | ## 0.8.1 58 | 59 | ### Fixed 60 | - View performs a layout pass when the text padding mode is set 61 | - View no longer animates the placeholder when the text propert is set 62 | 63 | ## 0.8.0 64 | 65 | ### Changed 66 | - The height of the background line of the `UnderlineView` is always 1 pixel wide. The property `foregroundLineWidth` (formerly "lineWidth") affects the height of the foreground line only (API breaking change). 67 | 68 | ### Fixed 69 | - Fixed default placeholder color 70 | - Fixed initial placeholder text alignment 71 | 72 | ## 0.7.0 73 | 74 | ### Added 75 | - Added property `textPaddingMode`. Used to apply the `textPadding` to just the text, or in addition to that to the placeholder, the hint or both. 76 | 77 | ## 0.6.1 78 | 79 | ### Fixed 80 | - Fixed horizontal intrinsic content size 81 | 82 | ## 0.6.0 83 | 84 | ### Changed 85 | - Replaced `horizontalTextPadding` and `verticalTextPadding` with `textPadding` of type `UIEdgeInsets` (API breaking change). Values can be set in IB. 86 | 87 | ## 0.5.0 88 | 89 | ### Added 90 | - Added optional vertical `hintOffset` of the hint label to the bottom of the text 91 | - Added Carthage support 92 | 93 | ## 0.4.0 94 | 95 | Pretty much redid the whole thing. There should be no API-breaking changes. 96 | 97 | ### Added 98 | - Added support for right-to-left languages 99 | - Added support for the left and right views 100 | - Added `UnderlineView` that can be used with the text view 101 | 102 | ### Changed 103 | - Updated to Swift 4 104 | - Improved handling of text alignments 105 | - Improved documentation 106 | - Bugfixes -------------------------------------------------------------------------------- /Example/Podfile: -------------------------------------------------------------------------------- 1 | platform :ios, '9.0' 2 | 3 | use_frameworks! 4 | 5 | target 'RAGTextField_Example' do 6 | pod 'RAGTextField', :path => '../' 7 | 8 | target 'RAGTextField_Tests' do 9 | inherit! :search_paths 10 | 11 | 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /Example/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - RAGTextField (0.13.0) 3 | 4 | DEPENDENCIES: 5 | - RAGTextField (from `../`) 6 | 7 | EXTERNAL SOURCES: 8 | RAGTextField: 9 | :path: "../" 10 | 11 | SPEC CHECKSUMS: 12 | RAGTextField: 5a94e334dba9bd1ce49cecb52e660026ca66de19 13 | 14 | PODFILE CHECKSUM: 49ab1e44f0e4c093a292143cccd721727174919d 15 | 16 | COCOAPODS: 1.9.3 17 | -------------------------------------------------------------------------------- /Example/Pods/Local Podspecs/RAGTextField.podspec.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "RAGTextField", 3 | "version": "0.13.0", 4 | "summary": "Subclass of UITextField featuring a floating placeholder and a hint label.", 5 | "description": "Subclass of UITextField that adds an animated placeholder and an optional hint label below the text. Supports an arbitrary view in the background of the text (outline and underline views are provided) as well as the left and right views, text alignments, flexible padding and offsets.", 6 | "homepage": "https://github.com/raginmari/RAGTextField", 7 | "license": { 8 | "type": "MIT", 9 | "file": "LICENSE" 10 | }, 11 | "authors": { 12 | "raginmari": "reimar.twelker@web.de" 13 | }, 14 | "source": { 15 | "git": "https://github.com/raginmari/RAGTextField.git", 16 | "tag": "0.13.0" 17 | }, 18 | "platforms": { 19 | "ios": "9.0" 20 | }, 21 | "swift_versions": "5.0", 22 | "source_files": "Sources/RAGTextField/Classes/**/*", 23 | "swift_version": "5.0" 24 | } 25 | -------------------------------------------------------------------------------- /Example/Pods/Manifest.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - RAGTextField (0.13.0) 3 | 4 | DEPENDENCIES: 5 | - RAGTextField (from `../`) 6 | 7 | EXTERNAL SOURCES: 8 | RAGTextField: 9 | :path: "../" 10 | 11 | SPEC CHECKSUMS: 12 | RAGTextField: 5a94e334dba9bd1ce49cecb52e660026ca66de19 13 | 14 | PODFILE CHECKSUM: 49ab1e44f0e4c093a292143cccd721727174919d 15 | 16 | COCOAPODS: 1.9.3 17 | -------------------------------------------------------------------------------- /Example/Pods/Pods.xcodeproj/xcshareddata/xcschemes/RAGTextField.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 45 | 46 | 52 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 66 | 67 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-RAGTextField_Example/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 | 1.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-RAGTextField_Example/Pods-RAGTextField_Example-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 | 1.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-RAGTextField_Example/Pods-RAGTextField_Example-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | 4 | ## RAGTextField 5 | 6 | Copyright (c) 2017 raginmari 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 16 | all 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 24 | THE SOFTWARE. 25 | 26 | Generated by CocoaPods - https://cocoapods.org 27 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-RAGTextField_Example/Pods-RAGTextField_Example-acknowledgements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreferenceSpecifiers 6 | 7 | 8 | FooterText 9 | This application makes use of the following third party libraries: 10 | Title 11 | Acknowledgements 12 | Type 13 | PSGroupSpecifier 14 | 15 | 16 | FooterText 17 | Copyright (c) 2017 raginmari <reimar.twelker@web.de> 18 | 19 | Permission is hereby granted, free of charge, to any person obtaining a copy 20 | of this software and associated documentation files (the "Software"), to deal 21 | in the Software without restriction, including without limitation the rights 22 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 23 | copies of the Software, and to permit persons to whom the Software is 24 | furnished to do so, subject to the following conditions: 25 | 26 | The above copyright notice and this permission notice shall be included in 27 | all copies or substantial portions of the Software. 28 | 29 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 30 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 31 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 32 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 33 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 34 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 35 | THE SOFTWARE. 36 | 37 | License 38 | MIT 39 | Title 40 | RAGTextField 41 | Type 42 | PSGroupSpecifier 43 | 44 | 45 | FooterText 46 | Generated by CocoaPods - https://cocoapods.org 47 | Title 48 | 49 | Type 50 | PSGroupSpecifier 51 | 52 | 53 | StringsTable 54 | Acknowledgements 55 | Title 56 | Acknowledgements 57 | 58 | 59 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-RAGTextField_Example/Pods-RAGTextField_Example-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_RAGTextField_Example : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_RAGTextField_Example 5 | @end 6 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-RAGTextField_Example/Pods-RAGTextField_Example-frameworks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | set -u 4 | set -o pipefail 5 | 6 | function on_error { 7 | echo "$(realpath -mq "${0}"):$1: error: Unexpected failure" 8 | } 9 | trap 'on_error $LINENO' ERR 10 | 11 | if [ -z ${FRAMEWORKS_FOLDER_PATH+x} ]; then 12 | # If FRAMEWORKS_FOLDER_PATH is not set, then there's nowhere for us to copy 13 | # frameworks to, so exit 0 (signalling the script phase was successful). 14 | exit 0 15 | fi 16 | 17 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 18 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 19 | 20 | COCOAPODS_PARALLEL_CODE_SIGN="${COCOAPODS_PARALLEL_CODE_SIGN:-false}" 21 | SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" 22 | 23 | # Used as a return value for each invocation of `strip_invalid_archs` function. 24 | STRIP_BINARY_RETVAL=0 25 | 26 | # This protects against multiple targets copying the same framework dependency at the same time. The solution 27 | # was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html 28 | RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") 29 | 30 | # Copies and strips a vendored framework 31 | install_framework() 32 | { 33 | if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then 34 | local source="${BUILT_PRODUCTS_DIR}/$1" 35 | elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then 36 | local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" 37 | elif [ -r "$1" ]; then 38 | local source="$1" 39 | fi 40 | 41 | local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 42 | 43 | if [ -L "${source}" ]; then 44 | echo "Symlinked..." 45 | source="$(readlink "${source}")" 46 | fi 47 | 48 | # Use filter instead of exclude so missing patterns don't throw errors. 49 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" 50 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" 51 | 52 | local basename 53 | basename="$(basename -s .framework "$1")" 54 | binary="${destination}/${basename}.framework/${basename}" 55 | 56 | if ! [ -r "$binary" ]; then 57 | binary="${destination}/${basename}" 58 | elif [ -L "${binary}" ]; then 59 | echo "Destination binary is symlinked..." 60 | dirname="$(dirname "${binary}")" 61 | binary="${dirname}/$(readlink "${binary}")" 62 | fi 63 | 64 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 65 | if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then 66 | strip_invalid_archs "$binary" 67 | fi 68 | 69 | # Resign the code if required by the build settings to avoid unstable apps 70 | code_sign_if_enabled "${destination}/$(basename "$1")" 71 | 72 | # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. 73 | if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then 74 | local swift_runtime_libs 75 | swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u) 76 | for lib in $swift_runtime_libs; do 77 | echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" 78 | rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" 79 | code_sign_if_enabled "${destination}/${lib}" 80 | done 81 | fi 82 | } 83 | 84 | # Copies and strips a vendored dSYM 85 | install_dsym() { 86 | local source="$1" 87 | warn_missing_arch=${2:-true} 88 | if [ -r "$source" ]; then 89 | # Copy the dSYM into the targets temp dir. 90 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${DERIVED_FILES_DIR}\"" 91 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${DERIVED_FILES_DIR}" 92 | 93 | local basename 94 | basename="$(basename -s .dSYM "$source")" 95 | binary_name="$(ls "$source/Contents/Resources/DWARF")" 96 | binary="${DERIVED_FILES_DIR}/${basename}.dSYM/Contents/Resources/DWARF/${binary_name}" 97 | 98 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 99 | if [[ "$(file "$binary")" == *"Mach-O "*"dSYM companion"* ]]; then 100 | strip_invalid_archs "$binary" "$warn_missing_arch" 101 | fi 102 | 103 | if [[ $STRIP_BINARY_RETVAL == 1 ]]; then 104 | # Move the stripped file into its final destination. 105 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${DERIVED_FILES_DIR}/${basename}.framework.dSYM\" \"${DWARF_DSYM_FOLDER_PATH}\"" 106 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${DERIVED_FILES_DIR}/${basename}.dSYM" "${DWARF_DSYM_FOLDER_PATH}" 107 | else 108 | # The dSYM was not stripped at all, in this case touch a fake folder so the input/output paths from Xcode do not reexecute this script because the file is missing. 109 | touch "${DWARF_DSYM_FOLDER_PATH}/${basename}.dSYM" 110 | fi 111 | fi 112 | } 113 | 114 | # Copies the bcsymbolmap files of a vendored framework 115 | install_bcsymbolmap() { 116 | local bcsymbolmap_path="$1" 117 | local destination="${BUILT_PRODUCTS_DIR}" 118 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${bcsymbolmap_path}" "${destination}"" 119 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${bcsymbolmap_path}" "${destination}" 120 | } 121 | 122 | # Signs a framework with the provided identity 123 | code_sign_if_enabled() { 124 | if [ -n "${EXPANDED_CODE_SIGN_IDENTITY:-}" -a "${CODE_SIGNING_REQUIRED:-}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then 125 | # Use the current code_sign_identity 126 | echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" 127 | local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS:-} --preserve-metadata=identifier,entitlements '$1'" 128 | 129 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 130 | code_sign_cmd="$code_sign_cmd &" 131 | fi 132 | echo "$code_sign_cmd" 133 | eval "$code_sign_cmd" 134 | fi 135 | } 136 | 137 | # Strip invalid architectures 138 | strip_invalid_archs() { 139 | binary="$1" 140 | warn_missing_arch=${2:-true} 141 | # Get architectures for current target binary 142 | binary_archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | awk '{$1=$1;print}' | rev)" 143 | # Intersect them with the architectures we are building for 144 | intersected_archs="$(echo ${ARCHS[@]} ${binary_archs[@]} | tr ' ' '\n' | sort | uniq -d)" 145 | # If there are no archs supported by this binary then warn the user 146 | if [[ -z "$intersected_archs" ]]; then 147 | if [[ "$warn_missing_arch" == "true" ]]; then 148 | echo "warning: [CP] Vendored binary '$binary' contains architectures ($binary_archs) none of which match the current build architectures ($ARCHS)." 149 | fi 150 | STRIP_BINARY_RETVAL=0 151 | return 152 | fi 153 | stripped="" 154 | for arch in $binary_archs; do 155 | if ! [[ "${ARCHS}" == *"$arch"* ]]; then 156 | # Strip non-valid architectures in-place 157 | lipo -remove "$arch" -output "$binary" "$binary" 158 | stripped="$stripped $arch" 159 | fi 160 | done 161 | if [[ "$stripped" ]]; then 162 | echo "Stripped $binary of architectures:$stripped" 163 | fi 164 | STRIP_BINARY_RETVAL=1 165 | } 166 | 167 | install_artifact() { 168 | artifact="$1" 169 | base="$(basename "$artifact")" 170 | case $base in 171 | *.framework) 172 | install_framework "$artifact" 173 | ;; 174 | *.dSYM) 175 | # Suppress arch warnings since XCFrameworks will include many dSYM files 176 | install_dsym "$artifact" "false" 177 | ;; 178 | *.bcsymbolmap) 179 | install_bcsymbolmap "$artifact" 180 | ;; 181 | *) 182 | echo "error: Unrecognized artifact "$artifact"" 183 | ;; 184 | esac 185 | } 186 | 187 | copy_artifacts() { 188 | file_list="$1" 189 | while read artifact; do 190 | install_artifact "$artifact" 191 | done <$file_list 192 | } 193 | 194 | ARTIFACT_LIST_FILE="${BUILT_PRODUCTS_DIR}/cocoapods-artifacts-${CONFIGURATION}.txt" 195 | if [ -r "${ARTIFACT_LIST_FILE}" ]; then 196 | copy_artifacts "${ARTIFACT_LIST_FILE}" 197 | fi 198 | 199 | if [[ "$CONFIGURATION" == "Debug" ]]; then 200 | install_framework "${BUILT_PRODUCTS_DIR}/RAGTextField/RAGTextField.framework" 201 | fi 202 | if [[ "$CONFIGURATION" == "Release" ]]; then 203 | install_framework "${BUILT_PRODUCTS_DIR}/RAGTextField/RAGTextField.framework" 204 | fi 205 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 206 | wait 207 | fi 208 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-RAGTextField_Example/Pods-RAGTextField_Example-resources.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 5 | 6 | RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt 7 | > "$RESOURCES_TO_COPY" 8 | 9 | XCASSET_FILES=() 10 | 11 | case "${TARGETED_DEVICE_FAMILY}" in 12 | 1,2) 13 | TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone" 14 | ;; 15 | 1) 16 | TARGET_DEVICE_ARGS="--target-device iphone" 17 | ;; 18 | 2) 19 | TARGET_DEVICE_ARGS="--target-device ipad" 20 | ;; 21 | *) 22 | TARGET_DEVICE_ARGS="--target-device mac" 23 | ;; 24 | esac 25 | 26 | install_resource() 27 | { 28 | if [[ "$1" = /* ]] ; then 29 | RESOURCE_PATH="$1" 30 | else 31 | RESOURCE_PATH="${PODS_ROOT}/$1" 32 | fi 33 | if [[ ! -e "$RESOURCE_PATH" ]] ; then 34 | cat << EOM 35 | error: Resource "$RESOURCE_PATH" not found. Run 'pod install' to update the copy resources script. 36 | EOM 37 | exit 1 38 | fi 39 | case $RESOURCE_PATH in 40 | *.storyboard) 41 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" 42 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} 43 | ;; 44 | *.xib) 45 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" 46 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} 47 | ;; 48 | *.framework) 49 | echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 50 | mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 51 | echo "rsync -av $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 52 | rsync -av "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 53 | ;; 54 | *.xcdatamodel) 55 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\"" 56 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom" 57 | ;; 58 | *.xcdatamodeld) 59 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\"" 60 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd" 61 | ;; 62 | *.xcmappingmodel) 63 | echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\"" 64 | xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm" 65 | ;; 66 | *.xcassets) 67 | ABSOLUTE_XCASSET_FILE="$RESOURCE_PATH" 68 | XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE") 69 | ;; 70 | *) 71 | echo "$RESOURCE_PATH" 72 | echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY" 73 | ;; 74 | esac 75 | } 76 | 77 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 78 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 79 | if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then 80 | mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 81 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 82 | fi 83 | rm -f "$RESOURCES_TO_COPY" 84 | 85 | if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "$XCASSET_FILES" ] 86 | then 87 | # Find all other xcassets (this unfortunately includes those of path pods and other targets). 88 | OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d) 89 | while read line; do 90 | if [[ $line != "${PODS_ROOT}*" ]]; then 91 | XCASSET_FILES+=("$line") 92 | fi 93 | done <<<"$OTHER_XCASSETS" 94 | 95 | printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 96 | fi 97 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-RAGTextField_Example/Pods-RAGTextField_Example-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double Pods_RAGTextField_ExampleVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char Pods_RAGTextField_ExampleVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-RAGTextField_Example/Pods-RAGTextField_Example.debug.xcconfig: -------------------------------------------------------------------------------- 1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES 2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/RAGTextField" 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/RAGTextField/RAGTextField.framework/Headers" 5 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 6 | OTHER_LDFLAGS = $(inherited) -framework "RAGTextField" 7 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS 8 | PODS_BUILD_DIR = ${BUILD_DIR} 9 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 10 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 11 | PODS_ROOT = ${SRCROOT}/Pods 12 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 13 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-RAGTextField_Example/Pods-RAGTextField_Example.modulemap: -------------------------------------------------------------------------------- 1 | framework module Pods_RAGTextField_Example { 2 | umbrella header "Pods-RAGTextField_Example-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-RAGTextField_Example/Pods-RAGTextField_Example.release.xcconfig: -------------------------------------------------------------------------------- 1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES 2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/RAGTextField" 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/RAGTextField/RAGTextField.framework/Headers" 5 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 6 | OTHER_LDFLAGS = $(inherited) -framework "RAGTextField" 7 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS 8 | PODS_BUILD_DIR = ${BUILD_DIR} 9 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 10 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 11 | PODS_ROOT = ${SRCROOT}/Pods 12 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 13 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-RAGTextField_Tests/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 | 1.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-RAGTextField_Tests/Pods-RAGTextField_Tests-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 | 1.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-RAGTextField_Tests/Pods-RAGTextField_Tests-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | Generated by CocoaPods - https://cocoapods.org 4 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-RAGTextField_Tests/Pods-RAGTextField_Tests-acknowledgements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreferenceSpecifiers 6 | 7 | 8 | FooterText 9 | This application makes use of the following third party libraries: 10 | Title 11 | Acknowledgements 12 | Type 13 | PSGroupSpecifier 14 | 15 | 16 | FooterText 17 | Generated by CocoaPods - https://cocoapods.org 18 | Title 19 | 20 | Type 21 | PSGroupSpecifier 22 | 23 | 24 | StringsTable 25 | Acknowledgements 26 | Title 27 | Acknowledgements 28 | 29 | 30 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-RAGTextField_Tests/Pods-RAGTextField_Tests-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_RAGTextField_Tests : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_RAGTextField_Tests 5 | @end 6 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-RAGTextField_Tests/Pods-RAGTextField_Tests-frameworks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 5 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 6 | 7 | SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" 8 | 9 | install_framework() 10 | { 11 | if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then 12 | local source="${BUILT_PRODUCTS_DIR}/$1" 13 | elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then 14 | local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" 15 | elif [ -r "$1" ]; then 16 | local source="$1" 17 | fi 18 | 19 | local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 20 | 21 | if [ -L "${source}" ]; then 22 | echo "Symlinked..." 23 | source="$(readlink "${source}")" 24 | fi 25 | 26 | # use filter instead of exclude so missing patterns dont' throw errors 27 | echo "rsync -av --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" 28 | rsync -av --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" 29 | 30 | local basename 31 | basename="$(basename -s .framework "$1")" 32 | binary="${destination}/${basename}.framework/${basename}" 33 | if ! [ -r "$binary" ]; then 34 | binary="${destination}/${basename}" 35 | fi 36 | 37 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 38 | if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then 39 | strip_invalid_archs "$binary" 40 | fi 41 | 42 | # Resign the code if required by the build settings to avoid unstable apps 43 | code_sign_if_enabled "${destination}/$(basename "$1")" 44 | 45 | # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. 46 | if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then 47 | local swift_runtime_libs 48 | swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u && exit ${PIPESTATUS[0]}) 49 | for lib in $swift_runtime_libs; do 50 | echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" 51 | rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" 52 | code_sign_if_enabled "${destination}/${lib}" 53 | done 54 | fi 55 | } 56 | 57 | # Signs a framework with the provided identity 58 | code_sign_if_enabled() { 59 | if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then 60 | # Use the current code_sign_identitiy 61 | echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" 62 | echo "/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS} --preserve-metadata=identifier,entitlements \"$1\"" 63 | /usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS} --preserve-metadata=identifier,entitlements "$1" 64 | fi 65 | } 66 | 67 | # Strip invalid architectures 68 | strip_invalid_archs() { 69 | binary="$1" 70 | # Get architectures for current file 71 | archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | rev)" 72 | stripped="" 73 | for arch in $archs; do 74 | if ! [[ "${VALID_ARCHS}" == *"$arch"* ]]; then 75 | # Strip non-valid architectures in-place 76 | lipo -remove "$arch" -output "$binary" "$binary" || exit 1 77 | stripped="$stripped $arch" 78 | fi 79 | done 80 | if [[ "$stripped" ]]; then 81 | echo "Stripped $binary of architectures:$stripped" 82 | fi 83 | } 84 | 85 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-RAGTextField_Tests/Pods-RAGTextField_Tests-resources.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 5 | 6 | RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt 7 | > "$RESOURCES_TO_COPY" 8 | 9 | XCASSET_FILES=() 10 | 11 | case "${TARGETED_DEVICE_FAMILY}" in 12 | 1,2) 13 | TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone" 14 | ;; 15 | 1) 16 | TARGET_DEVICE_ARGS="--target-device iphone" 17 | ;; 18 | 2) 19 | TARGET_DEVICE_ARGS="--target-device ipad" 20 | ;; 21 | *) 22 | TARGET_DEVICE_ARGS="--target-device mac" 23 | ;; 24 | esac 25 | 26 | install_resource() 27 | { 28 | if [[ "$1" = /* ]] ; then 29 | RESOURCE_PATH="$1" 30 | else 31 | RESOURCE_PATH="${PODS_ROOT}/$1" 32 | fi 33 | if [[ ! -e "$RESOURCE_PATH" ]] ; then 34 | cat << EOM 35 | error: Resource "$RESOURCE_PATH" not found. Run 'pod install' to update the copy resources script. 36 | EOM 37 | exit 1 38 | fi 39 | case $RESOURCE_PATH in 40 | *.storyboard) 41 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" 42 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} 43 | ;; 44 | *.xib) 45 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" 46 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} 47 | ;; 48 | *.framework) 49 | echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 50 | mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 51 | echo "rsync -av $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 52 | rsync -av "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 53 | ;; 54 | *.xcdatamodel) 55 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\"" 56 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom" 57 | ;; 58 | *.xcdatamodeld) 59 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\"" 60 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd" 61 | ;; 62 | *.xcmappingmodel) 63 | echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\"" 64 | xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm" 65 | ;; 66 | *.xcassets) 67 | ABSOLUTE_XCASSET_FILE="$RESOURCE_PATH" 68 | XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE") 69 | ;; 70 | *) 71 | echo "$RESOURCE_PATH" 72 | echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY" 73 | ;; 74 | esac 75 | } 76 | 77 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 78 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 79 | if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then 80 | mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 81 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 82 | fi 83 | rm -f "$RESOURCES_TO_COPY" 84 | 85 | if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "$XCASSET_FILES" ] 86 | then 87 | # Find all other xcassets (this unfortunately includes those of path pods and other targets). 88 | OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d) 89 | while read line; do 90 | if [[ $line != "${PODS_ROOT}*" ]]; then 91 | XCASSET_FILES+=("$line") 92 | fi 93 | done <<<"$OTHER_XCASSETS" 94 | 95 | printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 96 | fi 97 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-RAGTextField_Tests/Pods-RAGTextField_Tests-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double Pods_RAGTextField_TestsVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char Pods_RAGTextField_TestsVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-RAGTextField_Tests/Pods-RAGTextField_Tests.debug.xcconfig: -------------------------------------------------------------------------------- 1 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/RAGTextField" 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/RAGTextField/RAGTextField.framework/Headers" 4 | OTHER_LDFLAGS = $(inherited) -framework "RAGTextField" 5 | PODS_BUILD_DIR = ${BUILD_DIR} 6 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 7 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 8 | PODS_ROOT = ${SRCROOT}/Pods 9 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 10 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-RAGTextField_Tests/Pods-RAGTextField_Tests.modulemap: -------------------------------------------------------------------------------- 1 | framework module Pods_RAGTextField_Tests { 2 | umbrella header "Pods-RAGTextField_Tests-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-RAGTextField_Tests/Pods-RAGTextField_Tests.release.xcconfig: -------------------------------------------------------------------------------- 1 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/RAGTextField" 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/RAGTextField/RAGTextField.framework/Headers" 4 | OTHER_LDFLAGS = $(inherited) -framework "RAGTextField" 5 | PODS_BUILD_DIR = ${BUILD_DIR} 6 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 7 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 8 | PODS_ROOT = ${SRCROOT}/Pods 9 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 10 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/RAGTextField/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 | 0.1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/RAGTextField/RAGTextField-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 | 0.13.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/RAGTextField/RAGTextField-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_RAGTextField : NSObject 3 | @end 4 | @implementation PodsDummy_RAGTextField 5 | @end 6 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/RAGTextField/RAGTextField-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/RAGTextField/RAGTextField-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double RAGTextFieldVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char RAGTextFieldVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/RAGTextField/RAGTextField.debug.xcconfig: -------------------------------------------------------------------------------- 1 | CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/RAGTextField 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS 4 | PODS_BUILD_DIR = ${BUILD_DIR} 5 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 6 | PODS_ROOT = ${SRCROOT} 7 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/../.. 8 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 9 | SKIP_INSTALL = YES 10 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 11 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/RAGTextField/RAGTextField.modulemap: -------------------------------------------------------------------------------- 1 | framework module RAGTextField { 2 | umbrella header "RAGTextField-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/RAGTextField/RAGTextField.release.xcconfig: -------------------------------------------------------------------------------- 1 | CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/RAGTextField 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS 4 | PODS_BUILD_DIR = ${BUILD_DIR} 5 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 6 | PODS_ROOT = ${SRCROOT} 7 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/../.. 8 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 9 | SKIP_INSTALL = YES 10 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 11 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/RAGTextField/RAGTextField.xcconfig: -------------------------------------------------------------------------------- 1 | CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/RAGTextField 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS 4 | PODS_BUILD_DIR = ${BUILD_DIR} 5 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 6 | PODS_ROOT = ${SRCROOT} 7 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/../.. 8 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 9 | SKIP_INSTALL = YES 10 | -------------------------------------------------------------------------------- /Example/RAGTextField.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Example/RAGTextField.xcodeproj/xcshareddata/xcschemes/RAGTextField-Example.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 44 | 45 | 47 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 65 | 66 | 67 | 68 | 78 | 80 | 86 | 87 | 88 | 89 | 90 | 91 | 97 | 99 | 105 | 106 | 107 | 108 | 110 | 111 | 114 | 115 | 116 | -------------------------------------------------------------------------------- /Example/RAGTextField.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Example/RAGTextField.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Example/RAGTextField.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Example/RAGTextField/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | @UIApplicationMain 4 | class AppDelegate: UIResponder, UIApplicationDelegate { 5 | 6 | var window: UIWindow? 7 | 8 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 9 | return true 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Example/RAGTextField/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 20 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /Example/RAGTextField/Base.lproj/Main.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 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /Example/RAGTextField/ColorPalette.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | enum ColorPalette { 4 | 5 | static let chalk = UIColor(hex: 0xECF0F1) 6 | static let flame = UIColor(hex: 0xE74C3C) 7 | static let sky = UIColor(hex: 0x3498DB) 8 | static let meadow = UIColor(hex: 0x2ECC71) 9 | static let savanna = UIColor(hex: 0xE67E22) 10 | static let bramble = UIColor(hex: 0x9B59B6) 11 | static let midnight = UIColor(hex: 0x34495E) 12 | static let stone = UIColor(hex: 0x95A5A6) 13 | static let star = UIColor(hex: 0xF1C40F) 14 | } 15 | -------------------------------------------------------------------------------- /Example/RAGTextField/Hint.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 | 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 | -------------------------------------------------------------------------------- /Example/RAGTextField/HintViewController.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import RAGTextField 3 | 4 | final class HintViewController: UIViewController, UITextFieldDelegate { 5 | 6 | @IBOutlet private weak var hintTextField: RAGTextField! { 7 | didSet { 8 | setUp(hintTextField) 9 | hintTextField.hintColor = .darkGray 10 | hintTextField.hintFont = UIFont.systemFont(ofSize: 10.0) 11 | hintTextField.hint = "An info or error message" 12 | } 13 | } 14 | 15 | @IBOutlet private weak var coloredHintTextField: RAGTextField! { 16 | didSet { 17 | setUp(coloredHintTextField) 18 | coloredHintTextField.hintColor = ColorPalette.flame 19 | coloredHintTextField.hintFont = UIFont.systemFont(ofSize: 10.0) 20 | coloredHintTextField.hintOffset = 2.0 21 | coloredHintTextField.hint = "A password should contain at least 10 characters including lowercase and uppercase letters, digits and symbols. Hints should support multiple lines of text." 22 | } 23 | } 24 | 25 | @IBOutlet private weak var offsetHintTextField: RAGTextField! { 26 | didSet { 27 | setUp(offsetHintTextField) 28 | offsetHintTextField.hintColor = ColorPalette.sky 29 | offsetHintTextField.hintFont = UIFont.systemFont(ofSize: 11.0) 30 | offsetHintTextField.hint = "Keep a distance" 31 | } 32 | } 33 | 34 | @IBOutlet private weak var hintVisibilityControl: UISegmentedControl! { 35 | didSet { 36 | hintVisibilityControl.tintColor = .darkGray 37 | } 38 | } 39 | 40 | @IBOutlet private weak var hintOffsetControl: UISegmentedControl! { 41 | didSet { 42 | hintOffsetControl.tintColor = ColorPalette.sky 43 | } 44 | } 45 | 46 | private func setUp(_ textField: RAGTextField) { 47 | 48 | textField.delegate = self 49 | textField.textColor = ColorPalette.midnight 50 | textField.tintColor = ColorPalette.midnight 51 | textField.textBackgroundView = makeTextBackgroundView() 52 | textField.textPadding = UIEdgeInsets(top: 4.0, left: 4.0, bottom: 4.0, right: 4.0) 53 | textField.textPaddingMode = .textAndPlaceholderAndHint 54 | textField.scaledPlaceholderOffset = 2.0 55 | textField.placeholderMode = .scalesWhenEditing 56 | textField.placeholderScaleWhenEditing = 0.8 57 | textField.placeholderColor = ColorPalette.stone 58 | } 59 | 60 | private func makeTextBackgroundView() -> UIView { 61 | 62 | let view = UIView() 63 | view.layer.cornerRadius = 4.0 64 | view.backgroundColor = ColorPalette.chalk 65 | 66 | return view 67 | } 68 | 69 | override func viewDidLoad() { 70 | 71 | title = "Hint label" 72 | 73 | super.viewDidLoad() 74 | 75 | setHintVisibility(at: hintVisibilityControl.selectedSegmentIndex) 76 | setHintOffset(at: hintOffsetControl.selectedSegmentIndex) 77 | } 78 | 79 | @IBAction func onHintVisibilityDidChange(_ control: UISegmentedControl) { 80 | 81 | setHintVisibility(at: control.selectedSegmentIndex) 82 | } 83 | 84 | @IBAction func onHintOffsetDidChange(_ control: UISegmentedControl) { 85 | 86 | setHintOffset(at: control.selectedSegmentIndex) 87 | } 88 | 89 | private func setHintVisibility(at index: Int) { 90 | 91 | _ = hintTextField.resignFirstResponder() 92 | 93 | let value: String? = ["An info or error message", "", nil, nil][index] 94 | hintTextField.hint = value 95 | 96 | if index == 3 { 97 | hintTextField.layoutAlwaysIncludesHint = true 98 | } else { 99 | hintTextField.layoutAlwaysIncludesHint = false 100 | } 101 | } 102 | 103 | private func setHintOffset(at index: Int) { 104 | 105 | _ = offsetHintTextField.resignFirstResponder() 106 | 107 | let offset: CGFloat = [0.0, 8.0, 16.0][index] 108 | offsetHintTextField.hintOffset = offset 109 | } 110 | 111 | func textFieldShouldReturn(_ textField: UITextField) -> Bool { 112 | 113 | textField.resignFirstResponder() 114 | 115 | return false 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /Example/RAGTextField/Images.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 | } -------------------------------------------------------------------------------- /Example/RAGTextField/Images.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Example/RAGTextField/Images.xcassets/calendar.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "calendar@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "calendar@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | }, 22 | "properties" : { 23 | "template-rendering-intent" : "template" 24 | } 25 | } -------------------------------------------------------------------------------- /Example/RAGTextField/Images.xcassets/calendar.imageset/calendar@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raginmari/RAGTextField/9a4b1237aed03e365931887d18b7398bc477df26/Example/RAGTextField/Images.xcassets/calendar.imageset/calendar@2x.png -------------------------------------------------------------------------------- /Example/RAGTextField/Images.xcassets/calendar.imageset/calendar@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raginmari/RAGTextField/9a4b1237aed03e365931887d18b7398bc477df26/Example/RAGTextField/Images.xcassets/calendar.imageset/calendar@3x.png -------------------------------------------------------------------------------- /Example/RAGTextField/Images.xcassets/search.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "lupe@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "lupe@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | }, 22 | "properties" : { 23 | "template-rendering-intent" : "template" 24 | } 25 | } -------------------------------------------------------------------------------- /Example/RAGTextField/Images.xcassets/search.imageset/lupe@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raginmari/RAGTextField/9a4b1237aed03e365931887d18b7398bc477df26/Example/RAGTextField/Images.xcassets/search.imageset/lupe@2x.png -------------------------------------------------------------------------------- /Example/RAGTextField/Images.xcassets/search.imageset/lupe@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raginmari/RAGTextField/9a4b1237aed03e365931887d18b7398bc477df26/Example/RAGTextField/Images.xcassets/search.imageset/lupe@3x.png -------------------------------------------------------------------------------- /Example/RAGTextField/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 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /Example/RAGTextField/KeyboardEvader.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | final class KeyboardEvader: NSObject { 4 | 5 | @IBOutlet private weak var scrollView: UIScrollView? 6 | 7 | override func awakeFromNib() { 8 | 9 | super.awakeFromNib() 10 | 11 | registerForKeyboardNotifications() 12 | } 13 | 14 | private func registerForKeyboardNotifications() { 15 | 16 | NotificationCenter.default.addObserver(self, 17 | selector: #selector(onShowKeyboard(_:)), 18 | name: UIResponder.keyboardWillShowNotification, 19 | object: nil) 20 | 21 | NotificationCenter.default.addObserver(self, 22 | selector: #selector(onHideKeyboard(_:)), 23 | name: UIResponder.keyboardWillHideNotification, 24 | object: nil) 25 | } 26 | 27 | @objc private func onShowKeyboard(_ notification: Notification) { 28 | 29 | guard let scrollView = scrollView else { return } 30 | guard let keyboardFrame = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect else { return } 31 | 32 | let insets = UIEdgeInsets(top: 0, left: 0, bottom: keyboardFrame.height, right: 0) 33 | scrollView.contentInset = insets 34 | scrollView.scrollIndicatorInsets = insets 35 | } 36 | 37 | @objc private func onHideKeyboard(_ notification: Notification) { 38 | 39 | guard let scrollView = scrollView else { return } 40 | scrollView.contentInset = .zero 41 | scrollView.scrollIndicatorInsets = .zero 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Example/RAGTextField/LeftAndRightViewsViewController.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import RAGTextField 3 | 4 | private enum Constants { 5 | 6 | static let leftAndRightViewTintColor = ColorPalette.flame 7 | } 8 | 9 | final class LeftAndRightViewsViewController: UIViewController, UITextFieldDelegate { 10 | 11 | @IBOutlet private weak var notificationView: UIView! 12 | @IBOutlet private weak var notificationLabel: UILabel! 13 | 14 | @IBOutlet private weak var leftViewSwitch: UISwitch! { 15 | didSet { 16 | leftViewSwitch.tintColor = Constants.leftAndRightViewTintColor 17 | leftViewSwitch.onTintColor = Constants.leftAndRightViewTintColor 18 | } 19 | } 20 | 21 | @IBOutlet private weak var leftViewModeControl: UISegmentedControl! { 22 | didSet { 23 | leftViewModeControl.tintColor = Constants.leftAndRightViewTintColor 24 | } 25 | } 26 | 27 | @IBOutlet private weak var rightViewSwitch: UISwitch! { 28 | didSet { 29 | rightViewSwitch.tintColor = Constants.leftAndRightViewTintColor 30 | rightViewSwitch.onTintColor = Constants.leftAndRightViewTintColor 31 | } 32 | } 33 | 34 | @IBOutlet private weak var rightViewModeControl: UISegmentedControl! { 35 | didSet { 36 | rightViewModeControl.tintColor = Constants.leftAndRightViewTintColor 37 | } 38 | } 39 | 40 | @IBOutlet private weak var leftViewTextField: RAGTextField! { 41 | didSet { 42 | setUp(leftViewTextField) 43 | 44 | leftViewTextField.placeholderMode = .simple 45 | leftViewTextField.placeholderColor = ColorPalette.stone 46 | leftViewTextField.leftView = makeSearchIconView() 47 | leftViewTextField.leftViewMode = .always 48 | leftViewTextField.textColor = ColorPalette.midnight 49 | leftViewTextField.tintColor = ColorPalette.stone 50 | leftViewTextField.hintFont = UIFont.systemFont(ofSize: 11.0) 51 | leftViewTextField.hintColor = ColorPalette.savanna 52 | leftViewTextField.hintOffset = 4.0 53 | leftViewTextField.hint = "Left view mode is .always" 54 | } 55 | } 56 | 57 | @IBOutlet private weak var rightViewTextField: RAGTextField! { 58 | didSet { 59 | setUp(rightViewTextField) 60 | 61 | rightViewTextField.placeholderMode = .scalesWhenEditing 62 | rightViewTextField.placeholderColor = ColorPalette.stone 63 | rightViewTextField.rightView = makeCalendarButtonView() 64 | rightViewTextField.rightViewMode = .unlessEditing 65 | rightViewTextField.textColor = ColorPalette.midnight 66 | rightViewTextField.tintColor = ColorPalette.stone 67 | rightViewTextField.hintFont = UIFont.systemFont(ofSize: 11.0) 68 | rightViewTextField.hintColor = ColorPalette.bramble 69 | rightViewTextField.hintOffset = 2.0 70 | rightViewTextField.hint = "Right view mode is .unlessEditing" 71 | } 72 | } 73 | 74 | @IBOutlet private weak var leftAndRightViewTextField: RAGTextField! { 75 | didSet { 76 | setUp(leftAndRightViewTextField, color: Constants.leftAndRightViewTintColor) 77 | leftAndRightViewTextField.placeholderMode = .scalesWhenEditing 78 | leftAndRightViewTextField.placeholderColor = ColorPalette.chalk 79 | leftAndRightViewTextField.textColor = .white 80 | leftAndRightViewTextField.tintColor = ColorPalette.chalk 81 | } 82 | } 83 | 84 | @IBOutlet private weak var uiTextField: UITextField! { 85 | didSet { 86 | uiTextField.delegate = self 87 | uiTextField.tintColor = ColorPalette.stone 88 | } 89 | } 90 | 91 | private func setUp(_ textField: RAGTextField, color: UIColor = ColorPalette.chalk) { 92 | 93 | textField.delegate = self 94 | textField.textBackgroundView = makeTextBackgroundView(color: color) 95 | textField.textPadding = UIEdgeInsets(top: 8.0, left: 8.0, bottom: 8.0, right: 8.0) 96 | textField.textPaddingMode = .textAndPlaceholderAndHint 97 | textField.scaledPlaceholderOffset = 2.0 98 | textField.placeholderScaleWhenEditing = 0.8 99 | } 100 | 101 | private func makeTextBackgroundView(color: UIColor) -> UIView { 102 | 103 | let view = UIView() 104 | view.layer.cornerRadius = 4.0 105 | view.backgroundColor = color 106 | 107 | return view 108 | } 109 | 110 | private func makeSearchIconView() -> UIView { 111 | 112 | return UIImageView(image: UIImage(named: "search")) 113 | } 114 | 115 | private func makeCalendarButtonView() -> UIView { 116 | 117 | let image = UIImage(named: "calendar")! 118 | 119 | let button = UIButton(type: .custom) 120 | button.setImage(image, for: .normal) 121 | button.frame = CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height) 122 | button.addTarget(self, action: #selector(onCalendarButtonTapped(_:)), for: .touchUpInside) 123 | 124 | return button 125 | } 126 | 127 | @objc private func onCalendarButtonTapped(_ button: UIButton) { 128 | 129 | showNotification("Right view tapped") 130 | } 131 | 132 | override func viewDidLoad() { 133 | 134 | title = "Left and right views" 135 | 136 | setLeftView(visible: leftViewSwitch.isOn) 137 | setLeftViewMode(at: leftViewModeControl.selectedSegmentIndex) 138 | 139 | setRightView(visible: rightViewSwitch.isOn) 140 | setRightViewMode(at: rightViewModeControl.selectedSegmentIndex) 141 | 142 | super.viewDidLoad() 143 | } 144 | 145 | private func setLeftView(visible: Bool) { 146 | 147 | if visible { 148 | leftAndRightViewTextField.leftView = makeSearchIconView() 149 | uiTextField.leftView = makeSearchIconView() 150 | } else { 151 | leftAndRightViewTextField.leftView = nil 152 | uiTextField.leftView = nil 153 | } 154 | } 155 | 156 | private func setRightView(visible: Bool) { 157 | 158 | if visible { 159 | leftAndRightViewTextField.rightView = makeCalendarButtonView() 160 | uiTextField.rightView = makeCalendarButtonView() 161 | } else { 162 | leftAndRightViewTextField.rightView = nil 163 | uiTextField.rightView = nil 164 | } 165 | } 166 | 167 | private func setLeftViewMode(at index: Int) { 168 | 169 | let mode: UITextField.ViewMode = [.always, .whileEditing, .unlessEditing][index] 170 | leftAndRightViewTextField.leftViewMode = mode 171 | uiTextField.leftViewMode = mode 172 | } 173 | 174 | private func setRightViewMode(at index: Int) { 175 | 176 | let mode: UITextField.ViewMode = [.always, .whileEditing, .unlessEditing][index] 177 | leftAndRightViewTextField.rightViewMode = mode 178 | uiTextField.rightViewMode = mode 179 | } 180 | 181 | @IBAction private func onLeftViewModeChanged(_ control: UISegmentedControl) { 182 | 183 | setLeftViewMode(at: control.selectedSegmentIndex) 184 | } 185 | 186 | @IBAction private func onLeftViewToggled(_ control: UISwitch) { 187 | 188 | setLeftView(visible: control.isOn) 189 | } 190 | 191 | @IBAction private func onRightViewModeChanged(_ control: UISegmentedControl) { 192 | 193 | setRightViewMode(at: control.selectedSegmentIndex) 194 | } 195 | 196 | @IBAction private func onRightViewToggled(_ control: UISwitch) { 197 | 198 | setRightView(visible: control.isOn) 199 | } 200 | 201 | func textFieldShouldReturn(_ textField: UITextField) -> Bool { 202 | 203 | textField.resignFirstResponder() 204 | 205 | return false 206 | } 207 | 208 | private func showNotification(_ message: String) { 209 | 210 | notificationLabel.text = message 211 | 212 | notificationView.alpha = 1.0 213 | let animations: () -> Void = { [view = notificationView] in view?.alpha = 0 } 214 | UIView.animate(withDuration: 0.33, delay: 0.66, options: [], animations: animations) 215 | } 216 | } 217 | 218 | -------------------------------------------------------------------------------- /Example/RAGTextField/Outline.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 | 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 | -------------------------------------------------------------------------------- /Example/RAGTextField/OutlineViewController.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import RAGTextField 3 | 4 | final class OutlineViewController: UIViewController, UITextFieldDelegate { 5 | 6 | @IBOutlet private weak var outlineTextField: RAGTextField! { 7 | didSet { 8 | outlineTextField.delegate = self 9 | 10 | let bgView = OutlineView(frame: .zero) 11 | bgView.lineWidth = 1 12 | bgView.lineColor = ColorPalette.savanna 13 | bgView.fillColor = nil 14 | bgView.cornerRadius = 6.0 15 | outlineTextField.textColor = ColorPalette.stone 16 | outlineTextField.tintColor = ColorPalette.stone 17 | outlineTextField.textBackgroundView = bgView 18 | outlineTextField.textPadding = UIEdgeInsets(top: 8.0, left: 8.0, bottom: 8.0, right: 8.0) 19 | outlineTextField.textPaddingMode = .text 20 | outlineTextField.scaledPlaceholderOffset = 0.0 21 | outlineTextField.placeholderMode = .scalesWhenEditing 22 | outlineTextField.placeholderScaleWhenEditing = 0.8 23 | outlineTextField.placeholderColor = ColorPalette.savanna 24 | } 25 | } 26 | 27 | @IBOutlet private weak var outlineAndFillTextField: RAGTextField! { 28 | didSet { 29 | outlineAndFillTextField.delegate = self 30 | 31 | let bgView = OutlineView(frame: .zero) 32 | bgView.lineWidth = 2 33 | bgView.lineColor = ColorPalette.stone 34 | bgView.fillColor = ColorPalette.midnight 35 | bgView.cornerRadius = 4.0 36 | 37 | outlineAndFillTextField.textColor = ColorPalette.star 38 | outlineAndFillTextField.tintColor = ColorPalette.star 39 | outlineAndFillTextField.textBackgroundView = bgView 40 | outlineAndFillTextField.textPadding = UIEdgeInsets(top: 12.0, left: 8.0, bottom: 12.0, right: 8.0) 41 | outlineAndFillTextField.textPaddingMode = .textAndPlaceholder 42 | outlineAndFillTextField.scaledPlaceholderOffset = 0.0 43 | outlineAndFillTextField.placeholderMode = .scalesWhenEditing 44 | outlineAndFillTextField.placeholderScaleWhenEditing = 0.8 45 | outlineAndFillTextField.placeholderColor = ColorPalette.stone 46 | outlineAndFillTextField.transformedPlaceholderColor = outlineAndFillTextField.tintColor 47 | outlineAndFillTextField.hintColor = ColorPalette.stone 48 | outlineAndFillTextField.hintOffset = 2.0 49 | outlineAndFillTextField.hintFont = UIFont.systemFont(ofSize: 11.0) 50 | outlineAndFillTextField.hint = "Enter some text" 51 | } 52 | } 53 | 54 | @IBOutlet private weak var boxTextField: RAGTextField! { 55 | didSet { 56 | boxTextField.delegate = self 57 | 58 | let bgView = OutlineView(frame: .zero) 59 | bgView.lineWidth = 1.0 / UIScreen.main.scale // 1 pixel 60 | bgView.lineColor = ColorPalette.sky 61 | bgView.fillColor = ColorPalette.chalk 62 | bgView.cornerRadius = 0.0 63 | boxTextField.textBackgroundView = bgView 64 | boxTextField.textPadding = UIEdgeInsets(top: 16.0, left: 16.0, bottom: 16.0, right: 16.0) 65 | boxTextField.textPaddingMode = .textAndPlaceholderAndHint 66 | boxTextField.scaledPlaceholderOffset = 0.0 67 | boxTextField.placeholderMode = .scalesWhenEditing 68 | boxTextField.placeholderScaleWhenEditing = 0.8 69 | boxTextField.placeholderColor = ColorPalette.sky 70 | boxTextField.textColor = ColorPalette.midnight 71 | boxTextField.tintColor = ColorPalette.midnight 72 | boxTextField.hintColor = ColorPalette.meadow 73 | boxTextField.hintOffset = 0.0 74 | boxTextField.hintFont = UIFont.systemFont(ofSize: 11.0) 75 | boxTextField.hint = "Enter some text" 76 | } 77 | } 78 | 79 | override func viewDidLoad() { 80 | 81 | title = "Outline" 82 | 83 | super.viewDidLoad() 84 | } 85 | 86 | func textFieldShouldReturn(_ textField: UITextField) -> Bool { 87 | 88 | textField.resignFirstResponder() 89 | 90 | return false 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /Example/RAGTextField/Placeholder.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 | 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 | -------------------------------------------------------------------------------- /Example/RAGTextField/PlaceholderViewController.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import RAGTextField 3 | 4 | final class PlaceholderViewController: UIViewController, UITextFieldDelegate { 5 | 6 | @IBOutlet private weak var simpleTextField: RAGTextField! { 7 | didSet { 8 | simpleTextField.placeholderMode = .simple 9 | setUp(simpleTextField) 10 | } 11 | } 12 | 13 | @IBOutlet private weak var whenNotEmptyTextField: RAGTextField! { 14 | didSet { 15 | whenNotEmptyTextField.placeholderMode = .scalesWhenNotEmpty 16 | setUp(whenNotEmptyTextField) 17 | } 18 | } 19 | 20 | @IBOutlet private weak var whenEditingTextField: RAGTextField! { 21 | didSet { 22 | whenEditingTextField.placeholderMode = .scalesWhenEditing 23 | setUp(whenEditingTextField) 24 | } 25 | } 26 | 27 | @IBOutlet private weak var offsetTextField: RAGTextField! { 28 | didSet { 29 | offsetTextField.placeholderMode = .scalesWhenEditing 30 | setUp(offsetTextField, color: ColorPalette.savanna.withAlphaComponent(0.1)) 31 | } 32 | } 33 | 34 | @IBOutlet weak var placeholderOffsetControl: UISegmentedControl! { 35 | didSet { 36 | placeholderOffsetControl.tintColor = ColorPalette.savanna 37 | } 38 | } 39 | 40 | private func setUp(_ textField: RAGTextField, color: UIColor = ColorPalette.chalk) { 41 | 42 | textField.delegate = self 43 | textField.textColor = ColorPalette.midnight 44 | textField.tintColor = ColorPalette.midnight 45 | textField.textBackgroundView = makeTextBackgroundView(color: color) 46 | textField.textPadding = UIEdgeInsets(top: 4.0, left: 4.0, bottom: 4.0, right: 4.0) 47 | textField.textPaddingMode = .textAndPlaceholderAndHint 48 | textField.scaledPlaceholderOffset = 2.0 49 | textField.placeholderScaleWhenEditing = 0.8 50 | textField.placeholderColor = ColorPalette.stone 51 | } 52 | 53 | private func makeTextBackgroundView(color: UIColor) -> UIView { 54 | 55 | let view = UIView() 56 | view.layer.cornerRadius = 4.0 57 | view.backgroundColor = color 58 | 59 | return view 60 | } 61 | 62 | override func viewDidLoad() { 63 | 64 | title = "Placeholder" 65 | 66 | setPlaceholderOffset(at: placeholderOffsetControl.selectedSegmentIndex) 67 | 68 | super.viewDidLoad() 69 | } 70 | 71 | @IBAction func onPlaceholderOffsetChanged(_ control: UISegmentedControl) { 72 | 73 | setPlaceholderOffset(at: control.selectedSegmentIndex) 74 | } 75 | 76 | private func setPlaceholderOffset(at index: Int) { 77 | 78 | _ = offsetTextField.resignFirstResponder() 79 | 80 | let offset: CGFloat = [0.0, 8.0, 16.0][index] 81 | offsetTextField.scaledPlaceholderOffset = offset 82 | 83 | let value = "Offset by \(Int(offset))pt" 84 | offsetTextField.text = value 85 | } 86 | 87 | func textFieldShouldReturn(_ textField: UITextField) -> Bool { 88 | 89 | textField.resignFirstResponder() 90 | 91 | return false 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /Example/RAGTextField/TextAlignment.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 | 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 | -------------------------------------------------------------------------------- /Example/RAGTextField/TextAlignmentViewController.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import RAGTextField 3 | 4 | final class TextAlignmentViewController: UIViewController, UITextFieldDelegate { 5 | 6 | @IBOutlet private weak var naturalAlignmentTextField: RAGTextField! { 7 | didSet { 8 | setUp(naturalAlignmentTextField) 9 | naturalAlignmentTextField.hintColor = .darkGray 10 | naturalAlignmentTextField.hintFont = UIFont.systemFont(ofSize: 11.0) 11 | naturalAlignmentTextField.hint = "Based on user interface direction" 12 | } 13 | } 14 | 15 | @IBOutlet private weak var differentAlignmentsTextField: RAGTextField! { 16 | didSet { 17 | setUp(differentAlignmentsTextField) 18 | differentAlignmentsTextField.hintColor = ColorPalette.meadow 19 | differentAlignmentsTextField.hintFont = UIFont.systemFont(ofSize: 11.0) 20 | } 21 | } 22 | 23 | @IBOutlet private weak var textAlignmentControl: UISegmentedControl! { 24 | didSet { 25 | textAlignmentControl.tintColor = ColorPalette.meadow 26 | } 27 | } 28 | 29 | private func setUp(_ textField: RAGTextField) { 30 | 31 | textField.delegate = self 32 | textField.clearButtonMode = .whileEditing 33 | textField.textColor = ColorPalette.midnight 34 | textField.tintColor = ColorPalette.midnight 35 | textField.textBackgroundView = makeTextBackgroundView() 36 | textField.textPadding = UIEdgeInsets(top: 4.0, left: 4.0, bottom: 4.0, right: 4.0) 37 | textField.textPaddingMode = .textAndPlaceholderAndHint 38 | textField.scaledPlaceholderOffset = 2.0 39 | textField.placeholderMode = .scalesWhenEditing 40 | textField.placeholderScaleWhenEditing = 0.8 41 | textField.placeholderColor = ColorPalette.stone 42 | } 43 | 44 | private func makeTextBackgroundView() -> UIView { 45 | 46 | let view = UIView() 47 | view.layer.cornerRadius = 4.0 48 | view.backgroundColor = ColorPalette.chalk 49 | 50 | return view 51 | } 52 | 53 | override func viewDidLoad() { 54 | 55 | title = "Text alignment" 56 | 57 | super.viewDidLoad() 58 | 59 | setTextAlignement(at: textAlignmentControl.selectedSegmentIndex) 60 | } 61 | 62 | @IBAction func onTextAlignmentChanged(_ control: UISegmentedControl) { 63 | 64 | setTextAlignement(at: control.selectedSegmentIndex) 65 | } 66 | 67 | private func setTextAlignement(at index: Int) { 68 | 69 | _ = differentAlignmentsTextField.resignFirstResponder() 70 | 71 | let alignment: NSTextAlignment = [.left, .center, .right][index] 72 | differentAlignmentsTextField.textAlignment = alignment 73 | differentAlignmentsTextField.hint = hint(for: alignment) 74 | } 75 | 76 | private func hint(for textAlignment: NSTextAlignment) -> String { 77 | 78 | switch textAlignment { 79 | case .left: 80 | return "Left alignment" 81 | case .center: 82 | return "Center alignment" 83 | case .right: 84 | return "Right alignment" 85 | case .natural: 86 | return "Natural alignment" 87 | case .justified: 88 | return "Justified alignment" 89 | @unknown default: 90 | return "Unknown alignment" 91 | } 92 | } 93 | 94 | func textFieldShouldReturn(_ textField: UITextField) -> Bool { 95 | 96 | textField.resignFirstResponder() 97 | 98 | return false 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /Example/RAGTextField/TextPadding.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 | 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 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 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 | -------------------------------------------------------------------------------- /Example/RAGTextField/TextPaddingViewController.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import RAGTextField 3 | 4 | final class TextPaddingViewController: UIViewController, UITextFieldDelegate { 5 | 6 | @IBOutlet private weak var defaultTextField: RAGTextField! { 7 | didSet { 8 | setUp(defaultTextField, color: ColorPalette.chalk) 9 | defaultTextField.textPadding = UIEdgeInsets(top: 4.0, left: 4.0, bottom: 4.0, right: 4.0) 10 | } 11 | } 12 | 13 | @IBOutlet private weak var paddedTextField: RAGTextField! { 14 | didSet { 15 | setUp(paddedTextField, color: ColorPalette.bramble.withAlphaComponent(0.2)) 16 | paddedTextField.placeholderColor = ColorPalette.midnight.withAlphaComponent(0.66) 17 | paddedTextField.hintColor = ColorPalette.midnight 18 | paddedTextField.hintFont = UIFont.systemFont(ofSize: 11.0) 19 | paddedTextField.textColor = ColorPalette.bramble 20 | paddedTextField.tintColor = ColorPalette.stone 21 | paddedTextField.text = "Text" 22 | } 23 | } 24 | 25 | @IBOutlet private weak var paddingControl: UISegmentedControl! { 26 | didSet { 27 | paddingControl.tintColor = ColorPalette.bramble 28 | } 29 | } 30 | 31 | @IBOutlet private weak var includePlaceholderSwitch: UISwitch! { 32 | didSet { 33 | includePlaceholderSwitch.tintColor = ColorPalette.bramble 34 | includePlaceholderSwitch.onTintColor = ColorPalette.bramble 35 | } 36 | } 37 | 38 | @IBOutlet private weak var includeHintSwitch: UISwitch! { 39 | didSet { 40 | includeHintSwitch.tintColor = ColorPalette.bramble 41 | includeHintSwitch.onTintColor = ColorPalette.bramble 42 | } 43 | } 44 | 45 | private func setUp(_ textField: RAGTextField, color: UIColor) { 46 | 47 | textField.delegate = self 48 | textField.textColor = ColorPalette.midnight 49 | textField.tintColor = ColorPalette.midnight 50 | textField.textBackgroundView = makeTextBackgroundView(color: color) 51 | textField.textPaddingMode = .textAndPlaceholderAndHint 52 | textField.hintOffset = 2.0 53 | textField.scaledPlaceholderOffset = 2.0 54 | textField.placeholderMode = .scalesWhenEditing 55 | textField.placeholderScaleWhenEditing = 0.8 56 | textField.placeholderColor = ColorPalette.stone 57 | } 58 | 59 | private func makeTextBackgroundView(color: UIColor) -> UIView { 60 | 61 | let view = UIView() 62 | view.layer.cornerRadius = 4.0 63 | view.backgroundColor = color 64 | 65 | return view 66 | } 67 | 68 | override func viewDidLoad() { 69 | 70 | title = "Text padding" 71 | 72 | super.viewDidLoad() 73 | 74 | setPadding(at: paddingControl.selectedSegmentIndex) 75 | updateTextPaddingMode() 76 | } 77 | 78 | private func setPadding(at index: Int) { 79 | 80 | _ = paddedTextField.resignFirstResponder() 81 | 82 | let padding: CGFloat = [4.0, 8.0, 16.0][index] 83 | paddedTextField.textPadding = UIEdgeInsets(top: padding, left: padding, bottom: padding, right: padding) 84 | } 85 | 86 | private func updateTextPaddingMode() { 87 | 88 | let mode: RAGTextPaddingMode 89 | switch (includePlaceholderSwitch.isOn, includeHintSwitch.isOn) { 90 | case (true, true): 91 | mode = .textAndPlaceholderAndHint 92 | case (true, _): 93 | mode = .textAndPlaceholder 94 | case (_, true): 95 | mode = .textAndHint 96 | default: 97 | mode = .text 98 | } 99 | 100 | paddedTextField.textPaddingMode = mode 101 | paddedTextField.hint = hint(for: mode) 102 | } 103 | 104 | @IBAction private func onIncludePlaceholderChanged(_ control: UISwitch) { 105 | 106 | updateTextPaddingMode() 107 | } 108 | 109 | @IBAction private func onIncludeHintChanged(_ control: UISwitch) { 110 | 111 | updateTextPaddingMode() 112 | } 113 | 114 | private func hint(for mode: RAGTextPaddingMode) -> String { 115 | 116 | switch mode { 117 | case .text: 118 | return "Text mode" 119 | case .textAndHint: 120 | return "Text + hint mode" 121 | case .textAndPlaceholder: 122 | return "Text + placeholder mode" 123 | case .textAndPlaceholderAndHint: 124 | return "Text + placeholder + hint mode" 125 | } 126 | } 127 | 128 | @IBAction private func onPaddingChanged(_ control: UISegmentedControl) { 129 | 130 | setPadding(at: control.selectedSegmentIndex) 131 | } 132 | 133 | func textFieldShouldReturn(_ textField: UITextField) -> Bool { 134 | 135 | textField.resignFirstResponder() 136 | 137 | return false 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /Example/RAGTextField/UIColor+Hex.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | extension UIColor { 4 | 5 | /// Creates a color from the given hex value. 6 | /// 7 | /// - parameter hex: A 6-digit hex value, e.g. 0xff0000. 8 | /// 9 | /// - returns: The color corresponding to the given hex value 10 | convenience init(hex: Int) { 11 | let r = CGFloat((hex >> 16) & 0xff) / 255.0 12 | let g = CGFloat((hex >> 8) & 0xff) / 255.0 13 | let b = CGFloat((hex >> 0) & 0xff) / 255.0 14 | 15 | self.init(red: r, green: g, blue: b, alpha: 1.0) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Example/RAGTextField/Underline.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 | 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 | -------------------------------------------------------------------------------- /Example/RAGTextField/UnderlineViewController.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import RAGTextField 3 | 4 | final class UnderlineViewController: UIViewController, UITextFieldDelegate { 5 | 6 | @IBOutlet private weak var underlineTextField: RAGTextField! { 7 | didSet { 8 | underlineTextField.delegate = self 9 | 10 | let bgView = UnderlineView(frame: .zero) 11 | bgView.textField = underlineTextField 12 | bgView.backgroundLineColor = ColorPalette.midnight 13 | bgView.foregroundLineColor = ColorPalette.flame 14 | bgView.foregroundLineWidth = 2.0 15 | bgView.expandDuration = 0.2 16 | bgView.backgroundColor = ColorPalette.chalk 17 | if #available(iOS 11, *) { 18 | bgView.layer.cornerRadius = 4.0 19 | bgView.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner] 20 | } 21 | 22 | underlineTextField.tintColor = ColorPalette.flame 23 | underlineTextField.textBackgroundView = bgView 24 | underlineTextField.textPadding = UIEdgeInsets(top: 8.0, left: 8.0, bottom: 8.0, right: 8.0) 25 | underlineTextField.textPaddingMode = .textAndPlaceholderAndHint 26 | underlineTextField.scaledPlaceholderOffset = 0.0 27 | underlineTextField.placeholderMode = .scalesWhenEditing 28 | underlineTextField.placeholderScaleWhenEditing = 0.8 29 | underlineTextField.placeholderColor = ColorPalette.midnight.withAlphaComponent(0.66) 30 | underlineTextField.transformedPlaceholderColor = underlineTextField.tintColor 31 | underlineTextField.hint = nil 32 | } 33 | } 34 | 35 | @IBOutlet private weak var underlineModeTextField: RAGTextField! { 36 | didSet { 37 | underlineModeTextField.delegate = self 38 | 39 | let bgView = UnderlineView(frame: .zero) 40 | bgView.textField = underlineModeTextField 41 | bgView.backgroundLineColor = ColorPalette.midnight 42 | bgView.foregroundLineColor = ColorPalette.midnight 43 | bgView.foregroundLineWidth = 3.0 44 | bgView.expandDuration = 0.2 45 | bgView.backgroundColor = ColorPalette.sky 46 | if #available(iOS 11, *) { 47 | bgView.layer.cornerRadius = 8.0 48 | bgView.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner] 49 | } 50 | 51 | underlineModeTextField.textColor = .white 52 | underlineModeTextField.tintColor = .white 53 | underlineModeTextField.textBackgroundView = bgView 54 | underlineModeTextField.textPadding = UIEdgeInsets(top: 12.0, left: 12.0, bottom: 12.0, right: 12.0) 55 | underlineModeTextField.textPaddingMode = .textAndPlaceholder 56 | underlineModeTextField.scaledPlaceholderOffset = 0.0 57 | underlineModeTextField.placeholderMode = .scalesWhenEditing 58 | underlineModeTextField.placeholderScaleWhenEditing = 0.66 59 | underlineModeTextField.placeholderColor = .white 60 | underlineModeTextField.hintFont = UIFont.systemFont(ofSize: 11.0) 61 | underlineModeTextField.hintColor = ColorPalette.sky 62 | underlineModeTextField.hintOffset = 3.0 63 | underlineModeTextField.hint = "Natural mode supported as well" 64 | } 65 | } 66 | 67 | @IBOutlet private weak var boxTextField: RAGTextField! { 68 | didSet { 69 | boxTextField.delegate = self 70 | 71 | let bgView = UnderlineView(frame: .zero) 72 | bgView.textField = boxTextField 73 | bgView.backgroundLineColor = ColorPalette.stone 74 | bgView.foregroundLineColor = ColorPalette.bramble 75 | bgView.foregroundLineWidth = 2.0 76 | bgView.expandDuration = 0.2 77 | bgView.expandMode = .expandsInUserInterfaceDirection 78 | bgView.backgroundColor = ColorPalette.chalk 79 | 80 | boxTextField.textColor = ColorPalette.bramble 81 | boxTextField.tintColor = ColorPalette.bramble 82 | boxTextField.textBackgroundView = bgView 83 | boxTextField.textPadding = UIEdgeInsets(top: 8.0, left: 16.0, bottom: 8.0, right: 16.0) 84 | boxTextField.textPaddingMode = .textAndPlaceholderAndHint 85 | boxTextField.scaledPlaceholderOffset = 0.0 86 | boxTextField.placeholderMode = .scalesWhenEditing 87 | boxTextField.placeholderScaleWhenEditing = 0.8 88 | boxTextField.placeholderColor = ColorPalette.midnight.withAlphaComponent(0.66) 89 | boxTextField.transformedPlaceholderColor = boxTextField.tintColor 90 | boxTextField.hintFont = UIFont.systemFont(ofSize: 11.0) 91 | boxTextField.hintColor = ColorPalette.midnight 92 | boxTextField.hintOffset = 0.0 93 | boxTextField.hint = "Enter some text" 94 | } 95 | } 96 | 97 | @IBOutlet private weak var underlineModeControl: UISegmentedControl! { 98 | didSet { 99 | underlineModeControl.tintColor = ColorPalette.sky 100 | } 101 | } 102 | 103 | override func viewDidLoad() { 104 | 105 | title = "Underline" 106 | 107 | setUnderlineMode(at: underlineModeControl.selectedSegmentIndex) 108 | 109 | super.viewDidLoad() 110 | } 111 | 112 | private func setUnderlineMode(at index: Int) { 113 | 114 | let mode: UnderlineView.Mode = [.expandsFromLeft, .expandsFromCenter, .expandsFromRight, .notAnimated][index] 115 | (underlineModeTextField.textBackgroundView as? UnderlineView)?.expandMode = mode 116 | } 117 | 118 | @IBAction private func onUnderlineModeChanged(_ control: UISegmentedControl) { 119 | 120 | setUnderlineMode(at: control.selectedSegmentIndex) 121 | } 122 | 123 | func textFieldShouldReturn(_ textField: UITextField) -> Bool { 124 | 125 | textField.resignFirstResponder() 126 | 127 | return false 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /Example/RAGTextField/ViewController.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate { 4 | 5 | private enum Topic: String { 6 | 7 | case placeholder 8 | case hint 9 | case textAlignment 10 | case textPadding 11 | case outline 12 | case underline 13 | case leftAndRightViews 14 | 15 | static var allTopics: [Topic] { 16 | return [ 17 | .placeholder, 18 | .hint, 19 | .textAlignment, 20 | .textPadding, 21 | .outline, 22 | .underline, 23 | .leftAndRightViews 24 | ] 25 | } 26 | } 27 | 28 | override func viewDidLoad() { 29 | 30 | title = "Overview" 31 | 32 | if #available(iOS 11, *) { 33 | navigationController?.navigationBar.prefersLargeTitles = true 34 | } 35 | 36 | super.viewDidLoad() 37 | } 38 | 39 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 40 | 41 | return Topic.allTopics.count 42 | } 43 | 44 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 45 | 46 | let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) 47 | cell.textLabel?.text = title(for: Topic.allTopics[indexPath.row]) 48 | cell.textLabel?.textColor = .darkGray 49 | cell.accessoryType = .disclosureIndicator 50 | 51 | return cell 52 | } 53 | 54 | private func title(for topic: Topic) -> String { 55 | 56 | switch topic { 57 | case .placeholder: 58 | return "Animated placeholder" 59 | case .hint: 60 | return "Hint label" 61 | case .textAlignment: 62 | return "Text alignments" 63 | case .textPadding: 64 | return "Text padding" 65 | case .outline: 66 | return "Outlined style" 67 | case .underline: 68 | return "Underlined style" 69 | case .leftAndRightViews: 70 | return "Left and right views" 71 | } 72 | } 73 | 74 | func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 75 | 76 | tableView.deselectRow(at: indexPath, animated: true) 77 | 78 | let topic = Topic.allTopics[indexPath.row] 79 | 80 | let storyboardName = formatStoryboardName(for: topic) 81 | guard Bundle.main.path(forResource: storyboardName, ofType: "storyboardc") != nil else { 82 | return 83 | } 84 | 85 | let storyboard = UIStoryboard(name: storyboardName, bundle: nil) 86 | if let viewController = storyboard.instantiateInitialViewController() { 87 | navigationController?.pushViewController(viewController, animated: true) 88 | } 89 | } 90 | 91 | func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat { 92 | 93 | return CGFloat.leastNonzeroMagnitude 94 | } 95 | 96 | private func formatStoryboardName(for topic: Topic) -> String { 97 | 98 | let value = topic.rawValue 99 | let name = value.prefix(1).uppercased() + value.dropFirst() 100 | 101 | return name 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /Example/Tests/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 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /Example/Tests/Tests.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import XCTest 3 | import RAGTextField 4 | 5 | class Tests: XCTestCase { 6 | 7 | override func setUp() { 8 | super.setUp() 9 | // Put setup code here. This method is called before the invocation of each test method in the class. 10 | } 11 | 12 | override func tearDown() { 13 | // Put teardown code here. This method is called after the invocation of each test method in the class. 14 | super.tearDown() 15 | } 16 | 17 | func testExample() { 18 | // This is an example of a functional test case. 19 | XCTAssert(true, "Pass") 20 | } 21 | 22 | func testPerformanceExample() { 23 | // This is an example of a performance test case. 24 | self.measure() { 25 | // Put the code you want to measure the time of here. 26 | } 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 raginmari 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.0 2 | import PackageDescription 3 | 4 | let package = Package( 5 | name: "RAGTextField", 6 | platforms: [ 7 | .iOS(.v9) 8 | ], 9 | products: [ 10 | .library(name: "RAGTextField", targets: ["RAGTextField"]) 11 | ], 12 | targets: [ 13 | .target(name: "RAGTextField") 14 | ] 15 | ) 16 | -------------------------------------------------------------------------------- /RAGTextField.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = 'RAGTextField' 3 | s.version = '0.14.0' 4 | s.summary = 'Subclass of UITextField featuring a floating placeholder and a hint label.' 5 | s.description = 'Subclass of UITextField that adds an animated placeholder and an optional hint label below the text. Supports an arbitrary view in the background of the text (outline and underline views are provided) as well as the left and right views, text alignments, flexible padding and offsets.' 6 | s.homepage = 'https://github.com/raginmari/RAGTextField' 7 | s.license = { :type => 'MIT', :file => 'LICENSE' } 8 | s.author = { 'raginmari' => 'reimar.twelker@web.de' } 9 | s.source = { :git => 'https://github.com/raginmari/RAGTextField.git', :tag => s.version.to_s } 10 | s.ios.deployment_target = '9.0' 11 | s.swift_version = '5.0' 12 | s.source_files = 'Sources/RAGTextField/Classes/**/*' 13 | end 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RAGTextField 2 | 3 | [![CI Status](https://build.appcenter.ms/v0.1/apps/ca98a10c-2613-4600-a16a-5ddc9a9ff1c3/branches/master/badge)](https://appcenter.ms/users/raginmari/apps/RAGTextField-Demo-App/build/branches) 4 | [![Version](https://img.shields.io/cocoapods/v/RAGTextField.svg?style=flat)](http://cocoapods.org/pods/RAGTextField) 5 | [![License](https://img.shields.io/cocoapods/l/RAGTextField.svg?style=flat)](http://cocoapods.org/pods/RAGTextField) 6 | [![Platform](https://img.shields.io/cocoapods/p/RAGTextField.svg?style=flat)](http://cocoapods.org/pods/RAGTextField) 7 | [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) 8 | 9 | Subclass of `UITextField` that adds an animated placeholder and an optional hint label below the text. 10 | 11 | It supports an arbitrary view in the background of the text (outline and underline views are provided) as well as the left and right views, text alignments, flexible padding and offsets. 12 | 13 | ## Example 14 | 15 | To run the example project, clone the repo, and run `pod install` from the Example directory first. 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | ## Requirements 25 | 26 | Written in Swift 5. Requires iOS 9. 27 | 28 | Swift support: 29 | 30 | | Swift version | Library version | 31 | |---------------|-----------------| 32 | | 3 | <= 0.2.1 | 33 | | 4 | <= 0.12.1 | 34 | | 5 | from 0.13.0 | 35 | 36 | ## Installation 37 | 38 | `RAGTextField` is available through [Swift Package Manager](https://swift.org/package-manager) (SPM), [CocoaPods](http://cocoapods.org) and [Carthage](https://github.com/Carthage/Carthage). 39 | 40 | #### Cocoapods 41 | 42 | Add the following line to your Podfile: 43 | 44 | ```ruby 45 | pod "RAGTextField" 46 | ``` 47 | 48 | Please try the pod by calling `pod try RAGTextField` in the terminal. 49 | 50 | #### Carthage 51 | 52 | Add the following line to your Cartfile: 53 | 54 | ```ruby 55 | github "raginmari/RAGTextField" 56 | ``` 57 | 58 | ## How to use 59 | 60 | After installing the lib and importing the module `RAGTextField`, the text field can be used like any other text field (both in the code and in your storyboards and nibs). 61 | 62 | These are the key differences to `UITextField`: 63 | - A **floating placeholder** inspired by the one described in Google's [Material Design](https://material.io/guidelines/components/text-fields.html#text-fields-labels) guidelines. 64 | - A **hint label** below the actual text which can be used to provide additional hints or report errors. 65 | - A view in the **background of the text** (excluding the placeholder and the hint label) which can be used to style the appearance of the text entry. The example project uses this capability. 66 | 67 | Many properties of the text field are `IBInspectable`. 68 | 69 | #### The placeholder 70 | 71 | The placeholder replaces the superclass placeholder. Values assigned to the `placeholder` property are handled exclusively by `RAGTextField`. The text alignment of the placeholder matches the text alignment of the text field. 72 | 73 | These are the different ways you can **customize the appearance** and behavior of the placeholder: 74 | 75 | - Use the `placeholderFont` property to assign a **custom font or font size** to the placeholder. By default, the placeholder uses the font of the text field. 76 | - Use the `placeholderColor` property to **change the color** of the placeholder. By default, the placeholder uses the text color of the text field. 77 | - Use the `transformedPlaceholderColor` property to set a color that is applied to the placeholder when the text field is being edited and the placeholder is transformed to its floating position. 78 | - Use the `placeholderScaleWhenEditing` property to specify the **scale applied to the placeholder** in its floating position above the text. The default value is 1. 79 | - Use the `scaledPlaceholderOffset` property to offset the placeholder in its floating position from the text. The default value is 0. Positive values **move the placeholder up**, away from the text. 80 | - The value of the `placeholderMode` property determines the **behavior of the placeholder**: 81 | - `scalesWhenEditing` (default): the placeholder is transformed as soon as the text field becomes the first responder. Moreover, the placeholder remains transformed as long as there is text in the text field. 82 | - `scalesWhenNotEmpty`: the placeholder is transformed as soon as and for as long as there is text in the text field. 83 | - `scalesAlways`: the placeholder is always displayed in the transformed position. 84 | - `simple`: the floating placeholder is disabled. The behavior of the placeholder resembles that of the superclass. 85 | - Use the `placeholderAnimationDuration` property to adjust the **duration of the animation of the placeholder** when it moves to or from the floating position. If the value is `nil`, a default value is used. Set the value to 0 to **disable** the placeholder animation. 86 | 87 | #### The hint label 88 | 89 | The hint label is disabled by default and while the value of the `hint` property is `nil`. If a non-nil value is assigned to the `hint` property (including the empty string), the layout of the label is updated and space for the hint label is reserved. The text alignment of the hint matches the text alignment of the text field. The hint supports multiple lines of text. 90 | 91 | These are the different ways you can **customize the appearance** of the hint: 92 | 93 | - Use the `hintFont` property to assign a **custom font or font size** to the hint. By default, the hint uses the font of the text field. 94 | - Use the `hintColor` property to **change the color** of the hint. By default, the hint uses the text color of the text field. 95 | - Use the `hintOffset` property to offset the hint label from the text. The default value is 0. Positive values **move the hint down**, away from the text. 96 | - Use the `layoutAlwaysIncludesHint` property to always keep the hint label in the layout even if the `hint`value is `nil`. The default value of this property is `false`. 97 | 98 | #### The text background view 99 | 100 | Add a view to the background of the text by assigning an arbitrary view to the `textBackgroundView` property. Its frame is updated by `RAGTextField` when required. The view is sized so that it matches the size of the text (and placeholder and/or hint) plus padding. 101 | 102 | These are the different ways you can **customize the appearance** of the text background view. 103 | 104 | - Use the `textPadding` property to apply padding to the text. The padding expands the text background view. By default, the padding is `.zero`. 105 | - Use the `textPaddingMode` property to apply the text padding to just the text or in addition to that to the placeholder, the hint or both. Can be used to wrap the respective subviews into the text background view. The example project uses the text padding mode. 106 | 107 | #### The underline view 108 | 109 | Use the class `UnderlineView` to draw an **animated underline** below the text. Assign an instance of the class to the `textBackgroundView` property of the text field. The appearance of the underline can either be updated automatically by the view itself (see its `textField` property) or be manually controlled from a view controller or text field delegate. The example project uses the underline view. 110 | 111 | #### The outline view 112 | Use the class `OutlineView` to draw an outline around the text. The outline can have rounded corners and it can be filled and inset. Assign an instance of the class to the `textBackgroundView` property of the text field. Depending on the `textPaddingMode` value, the outline optionally includes the placeholder and/or hint label. 113 | 114 | ## Bond 115 | If you want to use the text field with the fantastic [Bond](https://github.com/DeclarativeHub/Bond), you can add these extensions so that you can bind to the `hint` and `placeholder` properties: 116 | 117 | ```swift 118 | extension ReactiveExtensions where Base: RAGTextField { 119 | 120 | var hint: Bond { 121 | return bond { textField, hint in 122 | textField.hint = hint 123 | } 124 | } 125 | 126 | var placeholder: Bond { 127 | return bond { textField, placeholder in 128 | textField.placeholder = placeholder 129 | } 130 | } 131 | } 132 | ``` 133 | 134 | ## Known issues 135 | 136 | These are known or possible issues with `RAGTextField`: 137 | 138 | - The `attributedText` property of UITextField *may* not be supported. 139 | - The `attributedPlaceholder` of UITextField is not supported. 140 | - The `borderStyle` property should be `.none`. 141 | 142 | These issues will hopefully be addressed in future updates. 143 | 144 | ## Author 145 | 146 | raginmari, reimar.twelker@web.de 147 | 148 | ## License 149 | 150 | RAGTextField is available under the MIT license. See the LICENSE file for more info. 151 | -------------------------------------------------------------------------------- /Sources/RAGTextField/Assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raginmari/RAGTextField/9a4b1237aed03e365931887d18b7398bc477df26/Sources/RAGTextField/Assets/.gitkeep -------------------------------------------------------------------------------- /Sources/RAGTextField/Classes/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raginmari/RAGTextField/9a4b1237aed03e365931887d18b7398bc477df26/Sources/RAGTextField/Classes/.gitkeep -------------------------------------------------------------------------------- /Sources/RAGTextField/Classes/Extensions/String+RAGTextField.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2017 Reimar Twelker 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | // 22 | 23 | import UIKit 24 | 25 | extension String { 26 | 27 | func size(using font: UIFont, availableWidth: CGFloat = .greatestFiniteMagnitude) -> CGSize { 28 | 29 | let size = CGSize(width: availableWidth, height: .greatestFiniteMagnitude) 30 | let options: NSStringDrawingOptions = [ .usesLineFragmentOrigin, .usesFontLeading ] 31 | let boundingRect = self.boundingRect(with: size, options: options, attributes: [ .font: font ], context: nil) 32 | let ceilSize = CGSize(width: ceil(boundingRect.width), height: ceil(boundingRect.height)) 33 | 34 | return ceilSize 35 | } 36 | } 37 | 38 | -------------------------------------------------------------------------------- /Sources/RAGTextField/Classes/PlaceholderView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2017 Reimar Twelker 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | // 22 | 23 | import UIKit 24 | 25 | private enum Constants { 26 | 27 | static let scaleAnimationKey = "scale" 28 | } 29 | 30 | /// Used to animate and transform a placeholder label using Core Animation in a view hierarchy that otherwise uses Auto Layout. 31 | final class PlaceholderView: UIView { 32 | 33 | override init(frame: CGRect) { 34 | 35 | super.init(frame: frame) 36 | commonInit() 37 | } 38 | 39 | required init?(coder: NSCoder) { 40 | 41 | super.init(coder: coder) 42 | commonInit() 43 | } 44 | 45 | private func commonInit() { 46 | 47 | isUserInteractionEnabled = false 48 | } 49 | 50 | /// The embedded label. 51 | /// 52 | /// Its layout does not use Auto Layout so that Core Animation can be used to transform the label. 53 | private(set) lazy var label: UILabel = { 54 | 55 | let label = UILabel() 56 | label.textColor = UIColor(white: 0.75, alpha: 1.0) 57 | addSubview(label) 58 | 59 | return label 60 | }() 61 | 62 | /// Updates the text alignment property of the label. 63 | /// 64 | /// - Warning 65 | /// Must be used instead of setting the property of the label directly. 66 | var textAlignment: NSTextAlignment { 67 | get { 68 | return label.textAlignment 69 | } 70 | set { 71 | label.textAlignment = newValue 72 | updateAnchorPoint(of: label, textAlignment: newValue) 73 | } 74 | } 75 | 76 | private func updateAnchorPoint(of view: UIView, textAlignment: NSTextAlignment) { 77 | 78 | switch (textAlignment, UIApplication.shared.userInterfaceLayoutDirection) { 79 | case (.natural, .leftToRight), (.justified, .leftToRight), (.left, _): 80 | view.layer.anchorPoint = CGPoint(x: 0.0, y: 0.5) 81 | case (.natural, .rightToLeft), (.justified, .rightToLeft), (.right, _): 82 | view.layer.anchorPoint = CGPoint(x: 1.0, y: 0.5) 83 | case (.center, _): 84 | view.layer.anchorPoint = CGPoint(x: 0.5, y: 0.5) 85 | @unknown default: 86 | // Use left-to-right value 87 | view.layer.anchorPoint = CGPoint(x: 0.0, y: 0.5) 88 | } 89 | 90 | view.frame = bounds 91 | } 92 | 93 | func scaleLabel(to scale: CGFloat, animated: Bool, duration: TimeInterval) { 94 | 95 | layoutIfNeeded() 96 | label.layer.removeAllAnimations() 97 | 98 | let transform = CATransform3DMakeScale(scale, scale, 1.0) 99 | 100 | if animated { 101 | let animation = CABasicAnimation(keyPath: "transform") 102 | let currentTransform = label.layer.presentation()?.transform ?? label.layer.transform 103 | animation.fromValue = currentTransform 104 | animation.toValue = transform 105 | animation.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut) 106 | animation.duration = duration 107 | label.layer.add(animation, forKey: Constants.scaleAnimationKey) 108 | } 109 | 110 | label.layer.transform = transform 111 | } 112 | 113 | override var intrinsicContentSize: CGSize { 114 | 115 | let infinite = CGFloat.greatestFiniteMagnitude 116 | let size = label.systemLayoutSizeFitting(CGSize(width: infinite, height: infinite)) 117 | 118 | return size 119 | } 120 | 121 | override func layoutSubviews() { 122 | 123 | super.layoutSubviews() 124 | 125 | if !isAnimating() { 126 | label.frame = bounds 127 | } 128 | } 129 | 130 | private func isAnimating() -> Bool { 131 | 132 | return label.layer.animation(forKey: Constants.scaleAnimationKey) != nil 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /Sources/RAGTextField/Classes/RAGTextFieldPlaceholderMode.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2017 Reimar Twelker 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | // 22 | 23 | import Foundation 24 | 25 | /// The different modes of the placeholder 26 | public enum RAGTextFieldPlaceholderMode { 27 | 28 | /// The default behavior of `UITextField` 29 | case simple 30 | 31 | /// The placeholder scales when it is not empty and when the text field is being edited 32 | case scalesWhenEditing 33 | 34 | /// The placeholder scales when it is not empty 35 | case scalesWhenNotEmpty 36 | 37 | /// The placeholder is locked in the transformed position above the text field 38 | case scalesAlways 39 | 40 | var scalesPlaceholder: Bool { 41 | switch self { 42 | case .simple: 43 | return false 44 | case .scalesWhenEditing: 45 | return true 46 | case .scalesWhenNotEmpty: 47 | return true 48 | case .scalesAlways: 49 | return true 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Sources/RAGTextField/Classes/RAGTextPaddingMode.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2017 Reimar Twelker 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | // 22 | 23 | import Foundation 24 | 25 | /// The different ways the text padding is applied to the subviews of the text field. 26 | public enum RAGTextPaddingMode { 27 | 28 | /// Text padding is applied to the text. 29 | case text 30 | 31 | /// Text padding is applied to the text and the placeholder. 32 | case textAndPlaceholder 33 | 34 | /// Text padding is applied to the text, the placeholder and the hint. 35 | case textAndPlaceholderAndHint 36 | 37 | /// Text padding is applied to the text and the hint. 38 | case textAndHint 39 | } 40 | -------------------------------------------------------------------------------- /Sources/RAGTextField/Classes/Views/OutlineView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2019 Reimar Twelker 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | // 22 | 23 | import UIKit 24 | 25 | /// Draws an outline along its bounds. 26 | /// 27 | /// The outline can be inset and its corners can be rounded. The inside of the outline can be filled. 28 | open class OutlineView: UIView { 29 | 30 | /// Uses a shape layer instead of the default one. 31 | override open class var layerClass : AnyClass { 32 | 33 | return CAShapeLayer.self 34 | } 35 | 36 | /// Convenience method force casts the layer to `CAShapeLayer`. 37 | private var shapeLayer: CAShapeLayer { 38 | 39 | return layer as! CAShapeLayer 40 | } 41 | 42 | /// The padding around the outline. 43 | open var outlineInsets: UIEdgeInsets = .zero { 44 | didSet { 45 | setNeedsLayout() 46 | } 47 | } 48 | 49 | /// The width of the outline in points. 50 | @IBInspectable open var lineWidth: CGFloat = 1.0 { 51 | didSet { 52 | shapeLayer.lineWidth = lineWidth 53 | } 54 | } 55 | 56 | /// The color of the outline. 57 | @IBInspectable open var lineColor: UIColor? = .black { 58 | didSet { 59 | shapeLayer.strokeColor = lineColor?.cgColor 60 | } 61 | } 62 | 63 | /// The color of the inside of the outline. 64 | @IBInspectable open var fillColor: UIColor? = .clear { 65 | didSet { 66 | shapeLayer.fillColor = fillColor?.cgColor 67 | } 68 | } 69 | 70 | /// The corner radius of the outline. 71 | @IBInspectable open var cornerRadius: CGFloat = 0.0 { 72 | didSet { 73 | shapeLayer.cornerRadius = cornerRadius 74 | setNeedsLayout() 75 | } 76 | } 77 | 78 | public override init(frame: CGRect) { 79 | 80 | super.init(frame: frame) 81 | commonInit() 82 | } 83 | 84 | public required init?(coder aDecoder: NSCoder) { 85 | 86 | super.init(coder: aDecoder) 87 | commonInit() 88 | } 89 | 90 | private func commonInit() { 91 | 92 | backgroundColor = .clear 93 | shapeLayer.fillColor = nil 94 | } 95 | 96 | open override func layoutSubviews() { 97 | 98 | super.layoutSubviews() 99 | 100 | shapeLayer.path = makePath().cgPath 101 | } 102 | 103 | /// Creates the path of the outline. 104 | /// 105 | /// - Returns: The new path. 106 | private func makePath() -> UIBezierPath { 107 | 108 | let rect: CGRect 109 | if outlineInsets != .zero { 110 | rect = bounds.inset(by: outlineInsets) 111 | } else { 112 | rect = bounds 113 | } 114 | 115 | let path: UIBezierPath 116 | if cornerRadius != 0.0 { 117 | path = UIBezierPath(roundedRect: rect, cornerRadius: cornerRadius) 118 | } else { 119 | path = UIBezierPath(rect: rect) 120 | } 121 | 122 | return path 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /Sources/RAGTextField/Classes/Views/UnderlineView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2019 Reimar Twelker 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | // 22 | 23 | import UIKit 24 | 25 | /// Draws two colored lines at the bottom on top of one another that extend from the left to the right edge. 26 | /// 27 | /// The foreground line is initially not visible. It can be expanded to fully cover the background line. 28 | /// The expansion can be animated in different ways. 29 | /// 30 | /// - Note 31 | /// The view is meant to be used with `RAGTextField`. Set it as the `textBackgroundView` to approximate the look and feel of a 32 | /// Material text field. The expansion of the line has to be controlled manually, for example from the text field delegate. 33 | open class UnderlineView: UIView { 34 | 35 | /// The different ways in which the expanding line is animated. 36 | public enum Mode { 37 | 38 | /// The line equally expands from the center of the view to its left and right edges. 39 | case expandsFromCenter 40 | 41 | /// The line expands from the right edge of the view to the left. 42 | case expandsFromRight 43 | 44 | /// The line expands from the left edge of the view to the right. 45 | case expandsFromLeft 46 | 47 | /// The line expands from the leading edge of the view to the trailing one. 48 | case expandsInUserInterfaceDirection 49 | 50 | /// The line is not animated. 51 | case notAnimated 52 | } 53 | 54 | /// The width of both the foreground line in points. 55 | @IBInspectable open var foregroundLineWidth: CGFloat = 1.0 { 56 | didSet { 57 | heightConstraint?.constant = foregroundLineWidth 58 | } 59 | } 60 | 61 | /// The color of the background line. 62 | @IBInspectable open var backgroundLineColor: UIColor = .clear { 63 | didSet { 64 | underlineBackgroundView.backgroundColor = backgroundLineColor 65 | } 66 | } 67 | 68 | /// The color of the foreground line. 69 | @IBInspectable open var foregroundLineColor: UIColor = .black { 70 | didSet { 71 | underlineView.backgroundColor = foregroundLineColor 72 | } 73 | } 74 | 75 | /// The way the foreground line is expanded. 76 | open var expandMode: Mode = .expandsFromCenter { 77 | didSet { 78 | setNeedsUpdateConstraints() 79 | } 80 | } 81 | 82 | /// The duration of the animation of the foreground line. 83 | open var expandDuration: TimeInterval = 0.2 84 | 85 | private let underlineView = UIView() 86 | private let underlineBackgroundView = UIView() 87 | 88 | /// Used to pin the foreground line to the leading edge of the view. 89 | /// 90 | /// Enabled and disabled depending on the `expandMode` value. 91 | private var leadingConstraint: NSLayoutConstraint? 92 | 93 | /// Used to pin the foreground line to the trailing edge of the view. 94 | /// 95 | /// Enabled and disabled depending on the `expandMode` value. 96 | private var trailingConstraint: NSLayoutConstraint? 97 | 98 | /// Used to animate the foreground line. 99 | private var widthConstraint: NSLayoutConstraint? 100 | 101 | /// Updated when `lineWidth` is changed. 102 | private var heightConstraint: NSLayoutConstraint? 103 | 104 | /// If `true`, the foreground line is currently expanded. 105 | private var isExpanded = false 106 | 107 | /// Whether the underline is animated when the associated text field begins editing. 108 | /// 109 | /// If `false`, the underline is updated but not animated. The default value is `true`. 110 | /// 111 | /// - Note 112 | /// For this property to take effect, the `textField` property must be set. 113 | open var animatesWhenEditingBegins = true 114 | 115 | /// Whether the underline is animated when the associated text field ends editing. 116 | /// 117 | /// If `false`, the underline is updated but not animated. The default value is `true`. 118 | /// 119 | /// - Note 120 | /// For this property to take effect, the `textField` property must be set. 121 | open var animatesWhenEditingEnds = true 122 | 123 | /// Refers to the text field whose editing state is used to update the appearance of the underline automatically. 124 | /// 125 | /// If `nil`, the appearance of the underline must be updated manually, for example from a view controller or text field delegate. 126 | open weak var textField: UITextField? { 127 | didSet { 128 | if let oldTextField = oldValue { 129 | stopObserving(oldTextField) 130 | } 131 | 132 | if let newTextField = textField { 133 | startObserving(newTextField) 134 | } 135 | } 136 | } 137 | 138 | /// The tint color of the `UIView` overwrites the current `expandedLineColor`. 139 | open override var tintColor: UIColor! { 140 | didSet { 141 | foregroundLineColor = tintColor 142 | } 143 | } 144 | 145 | public override init(frame: CGRect) { 146 | super.init(frame: frame) 147 | commonInit() 148 | } 149 | 150 | public required init?(coder aDecoder: NSCoder) { 151 | super.init(coder: aDecoder) 152 | commonInit() 153 | } 154 | 155 | private func commonInit() { 156 | 157 | addSubview(underlineBackgroundView) 158 | addSubview(underlineView) 159 | setUpUnderlineBackground() 160 | setUpUnderline() 161 | } 162 | 163 | /// Sets up the underline background view. Sets properties and configures constraints. 164 | private func setUpUnderlineBackground() { 165 | 166 | underlineBackgroundView.backgroundColor = backgroundLineColor 167 | underlineBackgroundView.translatesAutoresizingMaskIntoConstraints = false 168 | 169 | let views = ["v": underlineBackgroundView] 170 | addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[v]|", options: [], metrics: nil, views: views)) 171 | 172 | // Cling to the bottom of the view 173 | underlineBackgroundView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true 174 | 175 | // Always be as high as the underline 176 | let onePixel = 1.0 / UIScreen.main.scale 177 | underlineBackgroundView.heightAnchor.constraint(equalToConstant: onePixel).isActive = true 178 | } 179 | 180 | /// Sets up the underline view. Sets properties and configures constraints. 181 | private func setUpUnderline() { 182 | 183 | underlineView.backgroundColor = foregroundLineColor 184 | underlineView.translatesAutoresizingMaskIntoConstraints = false 185 | 186 | // Cling to the bottom of the view 187 | underlineView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true 188 | 189 | heightConstraint = underlineView.heightAnchor.constraint(equalToConstant: foregroundLineWidth) 190 | heightConstraint?.isActive = true 191 | 192 | // (De)activating the higher priority width constraint animates the underline 193 | widthConstraint = underlineView.widthAnchor.constraint(equalTo: widthAnchor) 194 | widthConstraint?.priority = .defaultHigh 195 | 196 | let zeroWidthConstraint = underlineView.widthAnchor.constraint(equalToConstant: 0.0) 197 | zeroWidthConstraint.priority = .defaultHigh - 1 198 | zeroWidthConstraint.isActive = true 199 | 200 | leadingConstraint = underlineView.leadingAnchor.constraint(equalTo: leadingAnchor) 201 | // Do not activate just yet 202 | 203 | trailingConstraint = underlineView.trailingAnchor.constraint(equalTo: trailingAnchor) 204 | // Do not activate just yet 205 | 206 | // Center with low priority 207 | let centerConstraint = underlineView.centerXAnchor.constraint(equalTo: centerXAnchor) 208 | centerConstraint.priority = .defaultLow 209 | centerConstraint.isActive = true 210 | 211 | setNeedsUpdateConstraints() 212 | } 213 | 214 | private func stopObserving(_ textField: UITextField) { 215 | 216 | NotificationCenter.default.removeObserver(self, name: UITextField.textDidBeginEditingNotification, object: textField) 217 | NotificationCenter.default.removeObserver(self, name: UITextField.textDidEndEditingNotification, object: textField) 218 | } 219 | 220 | private func startObserving(_ textField: UITextField) { 221 | 222 | NotificationCenter.default.addObserver(self, 223 | selector: #selector(onDidBeginEditing(_:)), 224 | name: UITextField.textDidBeginEditingNotification, 225 | object: textField) 226 | NotificationCenter.default.addObserver(self, 227 | selector: #selector(onDidEndEditing(_:)), 228 | name: UITextField.textDidEndEditingNotification, 229 | object: textField) 230 | } 231 | 232 | @objc private func onDidBeginEditing(_ notification: Notification) { 233 | 234 | setExpanded(true, animated: animatesWhenEditingBegins) 235 | } 236 | 237 | @objc private func onDidEndEditing(_ notification: Notification) { 238 | 239 | setExpanded(false, animated: animatesWhenEditingEnds) 240 | } 241 | 242 | open override func updateConstraints() { 243 | 244 | // Enable the leading and trailing constraints depending on the `expandMode`. 245 | switch expandMode { 246 | case .expandsFromCenter, .notAnimated: 247 | leadingConstraint?.isActive = false 248 | trailingConstraint?.isActive = false 249 | case .expandsFromRight where UIApplication.shared.userInterfaceLayoutDirection == .leftToRight: 250 | leadingConstraint?.isActive = false 251 | trailingConstraint?.isActive = true 252 | case .expandsFromRight: 253 | leadingConstraint?.isActive = true 254 | trailingConstraint?.isActive = false 255 | case .expandsFromLeft where UIApplication.shared.userInterfaceLayoutDirection == .leftToRight: 256 | leadingConstraint?.isActive = true 257 | trailingConstraint?.isActive = false 258 | case .expandsFromLeft: 259 | leadingConstraint?.isActive = false 260 | trailingConstraint?.isActive = true 261 | case .expandsInUserInterfaceDirection: 262 | leadingConstraint?.isActive = true 263 | trailingConstraint?.isActive = false 264 | } 265 | 266 | super.updateConstraints() 267 | } 268 | 269 | /// Sets the foreground line to its expanded or contracted state depending on the given parameter. Optionally, the change is animated. 270 | /// 271 | /// - Parameters: 272 | /// - expanded: If `true`, the line is expanded. 273 | /// - animated: If `true`, the change is animated. 274 | open func setExpanded(_ expanded: Bool, animated: Bool) { 275 | 276 | guard expanded != isExpanded else { 277 | return 278 | } 279 | 280 | widthConstraint?.isActive = expanded 281 | 282 | if animated && expandMode != .notAnimated { 283 | UIView.animate(withDuration: expandDuration) { [unowned self] in 284 | self.layoutIfNeeded() 285 | } 286 | } 287 | 288 | isExpanded = expanded 289 | } 290 | } 291 | -------------------------------------------------------------------------------- /_Pods.xcodeproj: -------------------------------------------------------------------------------- 1 | Example/Pods/Pods.xcodeproj --------------------------------------------------------------------------------