├── logo.png ├── style.png ├── SYKeyboard.gif ├── Snip20150129_1.png ├── KeyboardTextFieldDemo ├── KeyboardTextFieldDemo.xcodeproj │ ├── project.xcworkspace │ │ ├── xcuserdata │ │ │ └── yushuyi.xcuserdatad │ │ │ │ └── UserInterfaceState.xcuserstate │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── SYKeyboardTextFieldDemo.xccheckout │ ├── xcuserdata │ │ └── yushuyi.xcuserdatad │ │ │ ├── xcschemes │ │ │ ├── xcschememanagement.plist │ │ │ └── KeyboardTextFieldDemo.xcscheme │ │ │ └── xcdebugger │ │ │ └── Breakpoints_v2.xcbkptlist │ └── project.pbxproj └── KeyboardTextFieldDemo │ ├── Images.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json │ ├── Info.plist │ ├── AppDelegate.swift │ ├── Base.lproj │ ├── Main.storyboard │ └── LaunchScreen.xib │ └── ViewController.swift ├── README.md └── KeyboardTextField └── KeyboardTextField.swift /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yushuyi/KeyboardTextField/HEAD/logo.png -------------------------------------------------------------------------------- /style.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yushuyi/KeyboardTextField/HEAD/style.png -------------------------------------------------------------------------------- /SYKeyboard.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yushuyi/KeyboardTextField/HEAD/SYKeyboard.gif -------------------------------------------------------------------------------- /Snip20150129_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yushuyi/KeyboardTextField/HEAD/Snip20150129_1.png -------------------------------------------------------------------------------- /KeyboardTextFieldDemo/KeyboardTextFieldDemo.xcodeproj/project.xcworkspace/xcuserdata/yushuyi.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yushuyi/KeyboardTextField/HEAD/KeyboardTextFieldDemo/KeyboardTextFieldDemo.xcodeproj/project.xcworkspace/xcuserdata/yushuyi.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /KeyboardTextFieldDemo/KeyboardTextFieldDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /KeyboardTextFieldDemo/KeyboardTextFieldDemo.xcodeproj/xcuserdata/yushuyi.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | KeyboardTextFieldDemo.xcscheme 8 | 9 | orderHint 10 | 2 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 22918F5A1A79C48200547672 16 | 17 | primary 18 | 19 | 20 | 22918F6F1A79C48300547672 21 | 22 | primary 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /KeyboardTextFieldDemo/KeyboardTextFieldDemo/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /KeyboardTextFieldDemo/KeyboardTextFieldDemo/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 | UIInterfaceOrientationLandscapeRight 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | * [System requirements](#system-requirements) 2 | 3 |

4 | 5 |

6 | 7 | KeyboardTextField is a lightweight, simple, non-invasive keyboard accompanying input box! Write in Swift! 8 | 9 | 10 | 11 | ## Requirements 12 | 13 | - Swift 4 14 | - iOS 8.0 or later 15 | 16 | 17 | ## Installation 18 | - Drag the file to your project 19 | 20 | ## Usage 21 | keyboardTextField = KeyboardTextField(point: CGPoint(x: 0, y: 0), width: self.view.bounds.size.width) 22 | keyboardTextField.delegate = self 23 | keyboardTextField.isLeftButtonHidden = false 24 | keyboardTextField.isRightButtonHidden = false 25 | keyboardTextField.autoresizingMask = [UIViewAutoresizing.flexibleWidth , UIViewAutoresizing.flexibleTopMargin] 26 | self.view.addSubview(keyboardTextField) 27 | keyboardTextField.toFullyBottom() 28 | ## How to custom UI Style ? 29 | //UI 30 | lazy var keyboardView = UIView() 31 | lazy var textView : KeyboardTextView = KeyboardTextView() 32 | lazy var placeholderLabel = UILabel() 33 | lazy var textViewBackground = UIImageView() 34 | lazy var leftButton = UIButton() 35 | lazy var rightButton = UIButton() 36 | 37 | 38 | 39 | ## Author 40 | 41 | [@余书懿](http://weibo.com/ysy441088327) 42 | 43 | ## License 44 | 45 | KeyboardTextField is available under the MIT license. 46 | System requirements 47 | -------------------------------------------------------------------------------- /KeyboardTextFieldDemo/KeyboardTextFieldDemo.xcodeproj/project.xcworkspace/xcshareddata/SYKeyboardTextFieldDemo.xccheckout: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDESourceControlProjectFavoriteDictionaryKey 6 | 7 | IDESourceControlProjectIdentifier 8 | 564EA73C-149C-4C7E-833B-6F7B8284793C 9 | IDESourceControlProjectName 10 | SYKeyboardTextFieldDemo 11 | IDESourceControlProjectOriginsDictionary 12 | 13 | 511F8F817E53F0962706C08958EE2371E64696D1 14 | https://github.com/441088327/SYKeyboardTextField.git 15 | 16 | IDESourceControlProjectPath 17 | SYKeyboardTextFieldDemo/SYKeyboardTextFieldDemo.xcodeproj 18 | IDESourceControlProjectRelativeInstallPathDictionary 19 | 20 | 511F8F817E53F0962706C08958EE2371E64696D1 21 | ../../.. 22 | 23 | IDESourceControlProjectURL 24 | https://github.com/441088327/SYKeyboardTextField.git 25 | IDESourceControlProjectVersion 26 | 111 27 | IDESourceControlProjectWCCIdentifier 28 | 511F8F817E53F0962706C08958EE2371E64696D1 29 | IDESourceControlProjectWCConfigurations 30 | 31 | 32 | IDESourceControlRepositoryExtensionIdentifierKey 33 | public.vcs.git 34 | IDESourceControlWCCIdentifierKey 35 | 511F8F817E53F0962706C08958EE2371E64696D1 36 | IDESourceControlWCCName 37 | SYKeyboardTextField 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /KeyboardTextFieldDemo/KeyboardTextFieldDemo/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // KeyboardTextFieldDemo 4 | // 5 | // Created by yushuyi on 15/1/29. 6 | // Copyright (c) 2015年 yushuyi. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(_ application: UIApplication) { 23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 24 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 25 | } 26 | 27 | func applicationDidEnterBackground(_ application: UIApplication) { 28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | func applicationWillEnterForeground(_ application: UIApplication) { 33 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | func applicationDidBecomeActive(_ application: UIApplication) { 37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 38 | } 39 | 40 | func applicationWillTerminate(_ application: UIApplication) { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /KeyboardTextFieldDemo/KeyboardTextFieldDemo/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /KeyboardTextFieldDemo/KeyboardTextFieldDemo/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 21 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /KeyboardTextFieldDemo/KeyboardTextFieldDemo.xcodeproj/xcuserdata/yushuyi.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 8 | 20 | 21 | 22 | 24 | 36 | 37 | 38 | 40 | 52 | 53 | 54 | 56 | 68 | 69 | 70 | 72 | 84 | 85 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /KeyboardTextFieldDemo/KeyboardTextFieldDemo/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // KeyboardTextFieldDemo 4 | // 5 | // Created by yushuyi on 15/1/29. 6 | // Copyright (c) 2015年 yushuyi. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ViewController: UIViewController { 12 | 13 | var keyboardTextField : KeyboardTextField! 14 | 15 | 16 | override func loadView() { 17 | super.loadView() 18 | keyboardTextField = DNKeyboardTextField(point: CGPoint(x: 0, y: 0), width: self.view.bounds.size.width) 19 | keyboardTextField.delegate = self 20 | keyboardTextField.isLeftButtonHidden = true 21 | keyboardTextField.isRightButtonHidden = false 22 | keyboardTextField.autoresizingMask = [UIViewAutoresizing.flexibleWidth , UIViewAutoresizing.flexibleTopMargin] 23 | self.view.addSubview(keyboardTextField) 24 | keyboardTextField.toFullyBottom() 25 | 26 | let view = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100)) 27 | view.backgroundColor = UIColor.red 28 | keyboardTextField.addAttachmentView(view) 29 | } 30 | 31 | 32 | override func viewDidLoad() { 33 | super.viewDidLoad() 34 | 35 | } 36 | 37 | override func didReceiveMemoryWarning() { 38 | super.didReceiveMemoryWarning() 39 | // Dispose of any resources that can be recreated. 40 | } 41 | } 42 | 43 | //MARK: KeyboardTextFieldDelegate 44 | extension ViewController : KeyboardTextFieldDelegate { 45 | func keyboardTextFieldPressReturnButton(_ keyboardTextField: KeyboardTextField) { 46 | UIAlertView(title: "", message: "Action", delegate: nil, cancelButtonTitle: "OK").show() 47 | } 48 | } 49 | 50 | 51 | class DNKeyboardTextField : KeyboardTextField { 52 | 53 | override init(frame : CGRect) { 54 | super.init(frame : frame) 55 | self.clearTestColor() 56 | 57 | //Right Button 58 | self.rightButton.showsTouchWhenHighlighted = true 59 | self.rightButton.backgroundColor = UIColor(rgb: (252,49,89)) 60 | self.rightButton.clipsToBounds = true 61 | self.rightButton.layer.cornerRadius = 18 62 | self.rightButton.setTitle("^0^", for: .normal) 63 | self.rightButton.titleLabel?.font = UIFont.systemFont(ofSize: 16) 64 | 65 | //TextView 66 | self.textViewBackground.layer.borderColor = UIColor(rgb: (191,191,191)).cgColor 67 | self.textViewBackground.backgroundColor = UIColor.white 68 | self.textViewBackground.layer.cornerRadius = 18 69 | self.textViewBackground.layer.masksToBounds = true 70 | self.keyboardView.backgroundColor = UIColor(rgb: (238,238,238)) 71 | self.placeholderLabel.textAlignment = .center 72 | self.placeholderLabel.text = "^_^" 73 | self.placeholderLabel.textColor = UIColor(rgb: (153,153,153)) 74 | 75 | self.leftRightDistance = 15.0 76 | self.middleDistance = 5.0 77 | self.buttonMinWidth = 60 78 | } 79 | 80 | required init?(coder aDecoder: NSCoder) { 81 | super.init(coder: aDecoder) 82 | } 83 | 84 | } 85 | 86 | extension UIView { 87 | func toFullyBottom() { 88 | self.bottom = superview!.bounds.size.height 89 | self.autoresizingMask = [UIViewAutoresizing.flexibleTopMargin, UIViewAutoresizing.flexibleWidth] 90 | } 91 | 92 | public var bottom: CGFloat{ 93 | get { 94 | return self.frame.origin.y + self.frame.size.height 95 | } 96 | set { 97 | var frame = self.frame; 98 | frame.origin.y = newValue - frame.size.height; 99 | self.frame = frame; 100 | } 101 | } 102 | } 103 | 104 | extension UIColor { 105 | 106 | convenience init(rgb: (r: CGFloat, g: CGFloat, b: CGFloat)) { 107 | self.init(red: rgb.r/255, green: rgb.g/255, blue: rgb.b/255, alpha: 1) 108 | } 109 | convenience init(rgba: (r: CGFloat, g: CGFloat, b: CGFloat, a: CGFloat)) { 110 | self.init(red: rgba.r/255, green: rgba.g/255, blue: rgba.b/255, alpha: rgba.a) 111 | } 112 | } 113 | 114 | -------------------------------------------------------------------------------- /KeyboardTextFieldDemo/KeyboardTextFieldDemo.xcodeproj/xcuserdata/yushuyi.xcuserdatad/xcschemes/KeyboardTextFieldDemo.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 45 | 46 | 48 | 54 | 55 | 56 | 57 | 58 | 64 | 65 | 66 | 67 | 68 | 69 | 80 | 82 | 88 | 89 | 90 | 91 | 92 | 93 | 99 | 101 | 107 | 108 | 109 | 110 | 112 | 113 | 116 | 117 | 118 | -------------------------------------------------------------------------------- /KeyboardTextFieldDemo/KeyboardTextFieldDemo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 2277E8512064A3080042AB77 /* KeyboardTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2277E8502064A3070042AB77 /* KeyboardTextField.swift */; }; 11 | 22918F611A79C48200547672 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22918F601A79C48200547672 /* AppDelegate.swift */; }; 12 | 22918F631A79C48200547672 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22918F621A79C48200547672 /* ViewController.swift */; }; 13 | 22918F661A79C48200547672 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 22918F641A79C48200547672 /* Main.storyboard */; }; 14 | 22918F681A79C48200547672 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 22918F671A79C48200547672 /* Images.xcassets */; }; 15 | 22918F6B1A79C48200547672 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 22918F691A79C48200547672 /* LaunchScreen.xib */; }; 16 | /* End PBXBuildFile section */ 17 | 18 | /* Begin PBXFileReference section */ 19 | 2277E8502064A3070042AB77 /* KeyboardTextField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyboardTextField.swift; sourceTree = ""; }; 20 | 22918F5B1A79C48200547672 /* KeyboardTextFieldDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = KeyboardTextFieldDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 21 | 22918F5F1A79C48200547672 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 22 | 22918F601A79C48200547672 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 23 | 22918F621A79C48200547672 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 24 | 22918F651A79C48200547672 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 25 | 22918F671A79C48200547672 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 26 | 22918F6A1A79C48200547672 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; 27 | /* End PBXFileReference section */ 28 | 29 | /* Begin PBXFrameworksBuildPhase section */ 30 | 22918F581A79C48200547672 /* Frameworks */ = { 31 | isa = PBXFrameworksBuildPhase; 32 | buildActionMask = 2147483647; 33 | files = ( 34 | ); 35 | runOnlyForDeploymentPostprocessing = 0; 36 | }; 37 | /* End PBXFrameworksBuildPhase section */ 38 | 39 | /* Begin PBXGroup section */ 40 | 22918F521A79C48200547672 = { 41 | isa = PBXGroup; 42 | children = ( 43 | 22918F801A79C7EA00547672 /* KeyboardTextField */, 44 | 22918F5D1A79C48200547672 /* KeyboardTextFieldDemo */, 45 | 22918F5C1A79C48200547672 /* Products */, 46 | ); 47 | sourceTree = ""; 48 | }; 49 | 22918F5C1A79C48200547672 /* Products */ = { 50 | isa = PBXGroup; 51 | children = ( 52 | 22918F5B1A79C48200547672 /* KeyboardTextFieldDemo.app */, 53 | ); 54 | name = Products; 55 | sourceTree = ""; 56 | }; 57 | 22918F5D1A79C48200547672 /* KeyboardTextFieldDemo */ = { 58 | isa = PBXGroup; 59 | children = ( 60 | 22918F601A79C48200547672 /* AppDelegate.swift */, 61 | 22918F621A79C48200547672 /* ViewController.swift */, 62 | 22918F641A79C48200547672 /* Main.storyboard */, 63 | 22918F671A79C48200547672 /* Images.xcassets */, 64 | 22918F691A79C48200547672 /* LaunchScreen.xib */, 65 | 22918F5E1A79C48200547672 /* Supporting Files */, 66 | ); 67 | path = KeyboardTextFieldDemo; 68 | sourceTree = ""; 69 | }; 70 | 22918F5E1A79C48200547672 /* Supporting Files */ = { 71 | isa = PBXGroup; 72 | children = ( 73 | 22918F5F1A79C48200547672 /* Info.plist */, 74 | ); 75 | name = "Supporting Files"; 76 | sourceTree = ""; 77 | }; 78 | 22918F801A79C7EA00547672 /* KeyboardTextField */ = { 79 | isa = PBXGroup; 80 | children = ( 81 | 2277E8502064A3070042AB77 /* KeyboardTextField.swift */, 82 | ); 83 | name = KeyboardTextField; 84 | path = ../KeyboardTextField; 85 | sourceTree = ""; 86 | }; 87 | /* End PBXGroup section */ 88 | 89 | /* Begin PBXNativeTarget section */ 90 | 22918F5A1A79C48200547672 /* KeyboardTextFieldDemo */ = { 91 | isa = PBXNativeTarget; 92 | buildConfigurationList = 22918F7A1A79C48300547672 /* Build configuration list for PBXNativeTarget "KeyboardTextFieldDemo" */; 93 | buildPhases = ( 94 | 22918F571A79C48200547672 /* Sources */, 95 | 22918F581A79C48200547672 /* Frameworks */, 96 | 22918F591A79C48200547672 /* Resources */, 97 | ); 98 | buildRules = ( 99 | ); 100 | dependencies = ( 101 | ); 102 | name = KeyboardTextFieldDemo; 103 | productName = SYKeyboardTextFieldDemo; 104 | productReference = 22918F5B1A79C48200547672 /* KeyboardTextFieldDemo.app */; 105 | productType = "com.apple.product-type.application"; 106 | }; 107 | /* End PBXNativeTarget section */ 108 | 109 | /* Begin PBXProject section */ 110 | 22918F531A79C48200547672 /* Project object */ = { 111 | isa = PBXProject; 112 | attributes = { 113 | LastSwiftUpdateCheck = 0700; 114 | LastUpgradeCheck = 0800; 115 | ORGANIZATIONNAME = yushuyi; 116 | TargetAttributes = { 117 | 22918F5A1A79C48200547672 = { 118 | CreatedOnToolsVersion = 6.1.1; 119 | DevelopmentTeam = EWEN586K6F; 120 | LastSwiftMigration = 0800; 121 | }; 122 | }; 123 | }; 124 | buildConfigurationList = 22918F561A79C48200547672 /* Build configuration list for PBXProject "KeyboardTextFieldDemo" */; 125 | compatibilityVersion = "Xcode 3.2"; 126 | developmentRegion = English; 127 | hasScannedForEncodings = 0; 128 | knownRegions = ( 129 | en, 130 | Base, 131 | ); 132 | mainGroup = 22918F521A79C48200547672; 133 | productRefGroup = 22918F5C1A79C48200547672 /* Products */; 134 | projectDirPath = ""; 135 | projectRoot = ""; 136 | targets = ( 137 | 22918F5A1A79C48200547672 /* KeyboardTextFieldDemo */, 138 | ); 139 | }; 140 | /* End PBXProject section */ 141 | 142 | /* Begin PBXResourcesBuildPhase section */ 143 | 22918F591A79C48200547672 /* Resources */ = { 144 | isa = PBXResourcesBuildPhase; 145 | buildActionMask = 2147483647; 146 | files = ( 147 | 22918F661A79C48200547672 /* Main.storyboard in Resources */, 148 | 22918F6B1A79C48200547672 /* LaunchScreen.xib in Resources */, 149 | 22918F681A79C48200547672 /* Images.xcassets in Resources */, 150 | ); 151 | runOnlyForDeploymentPostprocessing = 0; 152 | }; 153 | /* End PBXResourcesBuildPhase section */ 154 | 155 | /* Begin PBXSourcesBuildPhase section */ 156 | 22918F571A79C48200547672 /* Sources */ = { 157 | isa = PBXSourcesBuildPhase; 158 | buildActionMask = 2147483647; 159 | files = ( 160 | 2277E8512064A3080042AB77 /* KeyboardTextField.swift in Sources */, 161 | 22918F631A79C48200547672 /* ViewController.swift in Sources */, 162 | 22918F611A79C48200547672 /* AppDelegate.swift in Sources */, 163 | ); 164 | runOnlyForDeploymentPostprocessing = 0; 165 | }; 166 | /* End PBXSourcesBuildPhase section */ 167 | 168 | /* Begin PBXVariantGroup section */ 169 | 22918F641A79C48200547672 /* Main.storyboard */ = { 170 | isa = PBXVariantGroup; 171 | children = ( 172 | 22918F651A79C48200547672 /* Base */, 173 | ); 174 | name = Main.storyboard; 175 | sourceTree = ""; 176 | }; 177 | 22918F691A79C48200547672 /* LaunchScreen.xib */ = { 178 | isa = PBXVariantGroup; 179 | children = ( 180 | 22918F6A1A79C48200547672 /* Base */, 181 | ); 182 | name = LaunchScreen.xib; 183 | sourceTree = ""; 184 | }; 185 | /* End PBXVariantGroup section */ 186 | 187 | /* Begin XCBuildConfiguration section */ 188 | 22918F781A79C48300547672 /* Debug */ = { 189 | isa = XCBuildConfiguration; 190 | buildSettings = { 191 | ALWAYS_SEARCH_USER_PATHS = NO; 192 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 193 | CLANG_CXX_LIBRARY = "libc++"; 194 | CLANG_ENABLE_MODULES = YES; 195 | CLANG_ENABLE_OBJC_ARC = YES; 196 | CLANG_WARN_BOOL_CONVERSION = YES; 197 | CLANG_WARN_CONSTANT_CONVERSION = YES; 198 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 199 | CLANG_WARN_EMPTY_BODY = YES; 200 | CLANG_WARN_ENUM_CONVERSION = YES; 201 | CLANG_WARN_INFINITE_RECURSION = YES; 202 | CLANG_WARN_INT_CONVERSION = YES; 203 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 204 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 205 | CLANG_WARN_UNREACHABLE_CODE = YES; 206 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 207 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 208 | COPY_PHASE_STRIP = NO; 209 | ENABLE_STRICT_OBJC_MSGSEND = YES; 210 | ENABLE_TESTABILITY = YES; 211 | GCC_C_LANGUAGE_STANDARD = gnu99; 212 | GCC_DYNAMIC_NO_PIC = NO; 213 | GCC_NO_COMMON_BLOCKS = YES; 214 | GCC_OPTIMIZATION_LEVEL = 0; 215 | GCC_PREPROCESSOR_DEFINITIONS = ( 216 | "DEBUG=1", 217 | "$(inherited)", 218 | ); 219 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 220 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 221 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 222 | GCC_WARN_UNDECLARED_SELECTOR = YES; 223 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 224 | GCC_WARN_UNUSED_FUNCTION = YES; 225 | GCC_WARN_UNUSED_VARIABLE = YES; 226 | IPHONEOS_DEPLOYMENT_TARGET = 8.1; 227 | MTL_ENABLE_DEBUG_INFO = YES; 228 | ONLY_ACTIVE_ARCH = YES; 229 | SDKROOT = iphoneos; 230 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 231 | }; 232 | name = Debug; 233 | }; 234 | 22918F791A79C48300547672 /* Release */ = { 235 | isa = XCBuildConfiguration; 236 | buildSettings = { 237 | ALWAYS_SEARCH_USER_PATHS = NO; 238 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 239 | CLANG_CXX_LIBRARY = "libc++"; 240 | CLANG_ENABLE_MODULES = YES; 241 | CLANG_ENABLE_OBJC_ARC = YES; 242 | CLANG_WARN_BOOL_CONVERSION = YES; 243 | CLANG_WARN_CONSTANT_CONVERSION = YES; 244 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 245 | CLANG_WARN_EMPTY_BODY = YES; 246 | CLANG_WARN_ENUM_CONVERSION = YES; 247 | CLANG_WARN_INFINITE_RECURSION = YES; 248 | CLANG_WARN_INT_CONVERSION = YES; 249 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 250 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 251 | CLANG_WARN_UNREACHABLE_CODE = YES; 252 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 253 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 254 | COPY_PHASE_STRIP = YES; 255 | ENABLE_NS_ASSERTIONS = NO; 256 | ENABLE_STRICT_OBJC_MSGSEND = YES; 257 | GCC_C_LANGUAGE_STANDARD = gnu99; 258 | GCC_NO_COMMON_BLOCKS = YES; 259 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 260 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 261 | GCC_WARN_UNDECLARED_SELECTOR = YES; 262 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 263 | GCC_WARN_UNUSED_FUNCTION = YES; 264 | GCC_WARN_UNUSED_VARIABLE = YES; 265 | IPHONEOS_DEPLOYMENT_TARGET = 8.1; 266 | MTL_ENABLE_DEBUG_INFO = NO; 267 | SDKROOT = iphoneos; 268 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 269 | VALIDATE_PRODUCT = YES; 270 | }; 271 | name = Release; 272 | }; 273 | 22918F7B1A79C48300547672 /* Debug */ = { 274 | isa = XCBuildConfiguration; 275 | buildSettings = { 276 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 277 | DEVELOPMENT_TEAM = EWEN586K6F; 278 | INFOPLIST_FILE = KeyboardTextFieldDemo/Info.plist; 279 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 280 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 281 | PRODUCT_BUNDLE_IDENTIFIER = "com.yushuyi.$(PRODUCT_NAME:rfc1034identifier)"; 282 | PRODUCT_NAME = "$(TARGET_NAME)"; 283 | SWIFT_VERSION = 4.0; 284 | }; 285 | name = Debug; 286 | }; 287 | 22918F7C1A79C48300547672 /* Release */ = { 288 | isa = XCBuildConfiguration; 289 | buildSettings = { 290 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 291 | DEVELOPMENT_TEAM = EWEN586K6F; 292 | INFOPLIST_FILE = KeyboardTextFieldDemo/Info.plist; 293 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 294 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 295 | PRODUCT_BUNDLE_IDENTIFIER = "com.yushuyi.$(PRODUCT_NAME:rfc1034identifier)"; 296 | PRODUCT_NAME = "$(TARGET_NAME)"; 297 | SWIFT_VERSION = 4.0; 298 | }; 299 | name = Release; 300 | }; 301 | /* End XCBuildConfiguration section */ 302 | 303 | /* Begin XCConfigurationList section */ 304 | 22918F561A79C48200547672 /* Build configuration list for PBXProject "KeyboardTextFieldDemo" */ = { 305 | isa = XCConfigurationList; 306 | buildConfigurations = ( 307 | 22918F781A79C48300547672 /* Debug */, 308 | 22918F791A79C48300547672 /* Release */, 309 | ); 310 | defaultConfigurationIsVisible = 0; 311 | defaultConfigurationName = Release; 312 | }; 313 | 22918F7A1A79C48300547672 /* Build configuration list for PBXNativeTarget "KeyboardTextFieldDemo" */ = { 314 | isa = XCConfigurationList; 315 | buildConfigurations = ( 316 | 22918F7B1A79C48300547672 /* Debug */, 317 | 22918F7C1A79C48300547672 /* Release */, 318 | ); 319 | defaultConfigurationIsVisible = 0; 320 | defaultConfigurationName = Release; 321 | }; 322 | /* End XCConfigurationList section */ 323 | }; 324 | rootObject = 22918F531A79C48200547672 /* Project object */; 325 | } 326 | -------------------------------------------------------------------------------- /KeyboardTextField/KeyboardTextField.swift: -------------------------------------------------------------------------------- 1 | // 2 | // KeyboardTextField.swift 3 | // DoudouApp 4 | // Version 3.2 iOS 8.0 and Swift 4 and Xcode9.2 5 | // Created by yushuyi on 15/1/17. 6 | // Copyright (c) 2015年 DoudouApp. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @objc public protocol KeyboardTextFieldDelegate : class { 12 | 13 | /** 14 | 点击左边按钮的委托 15 | */ 16 | @objc optional func keyboardTextFieldPressLeftButton(_ keyboardTextField :KeyboardTextField) 17 | 18 | /** 19 | 点击右边按钮的委托 20 | */ 21 | @objc optional func keyboardTextFieldPressRightButton(_ keyboardTextField :KeyboardTextField) 22 | 23 | /** 24 | 点击键盘上面的回车按钮响应委托 25 | */ 26 | @objc optional func keyboardTextFieldPressReturnButton(_ keyboardTextField :KeyboardTextField) 27 | 28 | @objc optional func keyboardTextFieldWillBeginEditing(_ keyboardTextField :KeyboardTextField) 29 | @objc optional func keyboardTextFieldDidBeginEditing(_ keyboardTextField :KeyboardTextField) 30 | 31 | @objc optional func keyboardTextFieldWillEndEditing(_ keyboardTextField :KeyboardTextField) 32 | @objc optional func keyboardTextFieldDidEndEditing(_ keyboardTextField :KeyboardTextField) 33 | 34 | 35 | /** 36 | 键盘文本内容被改变时触发 37 | - parameter text: 本次写入的值 38 | */ 39 | @objc optional func keyboardTextField(_ keyboardTextField :KeyboardTextField , didChangeText text:String) 40 | 41 | } 42 | 43 | fileprivate var KeyboardTextFieldDebugMode : Bool = false 44 | 45 | fileprivate var keyboardViewDefaultHeight : CGFloat = 48.0 46 | fileprivate let textViewDefaultHeight : CGFloat = 36.0 47 | 48 | 49 | 50 | 51 | open class KeyboardTextField: UIView { 52 | 53 | public enum AttachmentViewLocation { 54 | case up 55 | case down 56 | } 57 | 58 | //Delegate 59 | open weak var delegate : KeyboardTextFieldDelegate? 60 | 61 | //Init 62 | public convenience init(point : CGPoint,width : CGFloat) { 63 | self.init(frame: CGRect(x: point.x, y: point.y, width: width, height: keyboardViewDefaultHeight)) 64 | } 65 | 66 | public required init?(coder aDecoder: NSCoder) { 67 | super.init(coder: aDecoder) 68 | } 69 | 70 | public override init(frame : CGRect) { 71 | super.init(frame : frame) 72 | keyboardViewDefaultHeight = frame.height 73 | backgroundColor = UIColor.red 74 | 75 | keyboardView.frame = bounds 76 | keyboardView.backgroundColor = UIColor.yellow 77 | addSubview(keyboardView) 78 | 79 | keyboardView.addSubview(textViewBackground) 80 | 81 | textView.font = UIFont.systemFont(ofSize: 15.0); 82 | textView.autocapitalizationType = .none 83 | textView.scrollIndicatorInsets = UIEdgeInsetsMake(0, -1, 0, 1);//滚动指示器 皮条 84 | textView.textContainerInset = UIEdgeInsetsMake(9.0, 3.0, 7.0, 0.0); 85 | textView.autocorrectionType = .no 86 | textView.keyboardType = UIKeyboardType.default; 87 | textView.returnKeyType = UIReturnKeyType.done; 88 | textView.enablesReturnKeyAutomatically = true; 89 | textView.delegate = self 90 | textView.textColor = UIColor(white: 0.200, alpha: 1.000) 91 | textView.backgroundColor = UIColor.green 92 | textView.addObserver(self, forKeyPath: "contentSize", options: NSKeyValueObservingOptions.new, context: nil) 93 | textView.scrollsToTop = false 94 | keyboardView.addSubview(textView) 95 | 96 | placeholderLabel.textAlignment = NSTextAlignment.left 97 | placeholderLabel.numberOfLines = 1 98 | placeholderLabel.backgroundColor = UIColor.clear 99 | placeholderLabel.textColor = UIColor.lightGray 100 | placeholderLabel.font = textView.font; 101 | placeholderLabel.isHidden = false 102 | placeholderLabel.text = "placeholder" 103 | textView.addSubview(placeholderLabel) 104 | 105 | 106 | leftButton.backgroundColor = UIColor.red 107 | leftButton.titleLabel?.font = UIFont.systemFont(ofSize: 14.0) 108 | leftButton.setTitle("Left", for: .normal) 109 | leftButton.addTarget(self, action: #selector(KeyboardTextField.leftButtonAction(_:)), for: UIControlEvents.touchUpInside) 110 | keyboardView.addSubview(leftButton) 111 | 112 | rightButton.backgroundColor = UIColor.red 113 | rightButton.titleLabel?.font = UIFont.systemFont(ofSize: 14.0) 114 | rightButton.setTitle("Right", for: .normal) 115 | rightButton.addTarget(self, action: #selector(KeyboardTextField.rightButtonAction(_:)), for: UIControlEvents.touchUpInside) 116 | keyboardView.addSubview(rightButton) 117 | 118 | registeringKeyboardNotification() 119 | 120 | 121 | //当键盘的高度改变时,keyboardTextField 会吸附会去,这的过程中间会看到底部 透明蒙层,效果不好 加上这个视图以作修饰 122 | bottomBackgroundView.frame = CGRect(x: 0, y: bounds.size.height, width: bounds.size.width, height: 100) 123 | bottomBackgroundView.autoresizingMask = [.flexibleTopMargin,.flexibleWidth] 124 | bottomBackgroundView.backgroundColor = UIColor(red: 210.0/255.0, green: 213.0/255.0, blue: 219.0/255.0, alpha: 1.0) 125 | bottomBackgroundView.isUserInteractionEnabled = false 126 | bottomBackgroundView.isHidden = true 127 | insertSubview(bottomBackgroundView, at: 0) 128 | 129 | } 130 | 131 | open func show() { 132 | textView.becomeFirstResponder() 133 | } 134 | 135 | open func hide() { 136 | if isAutoLayout { 137 | isAutoLayout = false 138 | translatesAutoresizingMaskIntoConstraints = false 139 | } 140 | attachmentView?.moveToBottom() 141 | delegate?.keyboardTextFieldWillEndEditing?(self) 142 | bottomBackgroundView.isHidden = true 143 | isEditing = false 144 | isHideing = true 145 | 146 | //这里判断一下键盘是否已经隐藏了,如果隐藏了 也需要调用一下 DidEnd回调以保证完整性 147 | if let superview = self.superview { 148 | if self.bottom == superview.bounds.size.height { 149 | delegate?.keyboardTextFieldDidEndEditing?(self) 150 | } 151 | } 152 | 153 | endEditing(true) 154 | setTapButtonHidden(true) 155 | 156 | } 157 | 158 | open func addAttachmentView(_ view: UIView,location: AttachmentViewLocation = .up) { 159 | removeAttachmentView() 160 | insertSubview(view, at: 0) 161 | view.alpha = 0 162 | view.isUserInteractionEnabled = false 163 | view.autoresizingMask = [] 164 | attachmentView = view 165 | attachmentViewLocation = location 166 | } 167 | 168 | public func removeAttachmentView() { 169 | if let attachmentView = attachmentView { 170 | attachmentView.removeFromSuperview() 171 | self.attachmentView = nil 172 | } 173 | } 174 | 175 | public func done() { 176 | delegate?.keyboardTextFieldPressReturnButton?(self) 177 | } 178 | 179 | //Status 180 | public var isSending = false 181 | 182 | public var isEnabled: Bool = true { 183 | didSet { 184 | textView.isEditable = isEnabled 185 | leftButton.isEnabled = isEnabled 186 | rightButton.isEnabled = isEnabled 187 | } 188 | } 189 | public var isEditing: Bool = false 190 | 191 | public var isLeftButtonHidden : Bool = true { 192 | didSet { 193 | leftButton.isHidden = isLeftButtonHidden 194 | setNeedsLayout() 195 | } 196 | } 197 | 198 | public var isRightButtonHidden : Bool = true { 199 | didSet { 200 | rightButton.isHidden = isRightButtonHidden 201 | setNeedsLayout() 202 | } 203 | } 204 | 205 | public var attachmentViewLocation: AttachmentViewLocation = .up 206 | 207 | //text 208 | public var text : String! { 209 | get { 210 | return textView.text 211 | } 212 | set { 213 | textView.text = newValue 214 | textViewDidChange(textView) 215 | layoutIfNeeded() 216 | } 217 | } 218 | 219 | open var maxNumberOfWords : Int = 140 220 | open var minNumberOfWords : Int = 0 221 | open var maxNumberOfLines : Int = 4 222 | 223 | 224 | //UI 225 | public var attachmentView: UIView? 226 | public lazy var keyboardView = UIView() 227 | public lazy var textView : KeyboardTextView = KeyboardTextView() 228 | public lazy var placeholderLabel = UILabel() 229 | public lazy var textViewBackground = UIImageView() 230 | public lazy var leftButton = UIButton() 231 | public lazy var rightButton = UIButton() 232 | public lazy var bottomBackgroundView = UIView() 233 | public func clearTestColor() { 234 | backgroundColor = UIColor.clear 235 | leftButton.backgroundColor = UIColor.clear 236 | rightButton.backgroundColor = UIColor.clear 237 | textView.backgroundColor = UIColor.clear 238 | textViewBackground.backgroundColor = UIColor.clear 239 | } 240 | public var tapButtonBackgroundColor: UIColor = UIColor.clear 241 | 242 | //Layout 243 | fileprivate var lastKeyboardFrame : CGRect = CGRect.zero 244 | 245 | open var leftRightDistance : CGFloat = 8.0 246 | open var middleDistance : CGFloat = 8.0 247 | 248 | open var buttonMaxWidth : CGFloat = 65.0 249 | open var buttonMinWidth : CGFloat = 45.0 250 | 251 | open override func layoutSubviews() { 252 | super.layoutSubviews() 253 | 254 | if isEditing { 255 | switch attachmentViewLocation { 256 | case .up: 257 | keyboardView.frame = CGRect(x: 0, y: (attachmentView?.bounds.size.height ?? 0), width: bounds.size.width, height: bounds.size.height - (attachmentView?.bounds.size.height ?? 0)) 258 | case .down: 259 | keyboardView.frame = CGRect(x: 0, y: 0, width: bounds.size.width, height: bounds.size.height - (attachmentView?.bounds.size.height ?? 0)) 260 | } 261 | }else { 262 | keyboardView.frame = CGRect(x: 0, y: 0, width: bounds.size.width, height: bounds.size.height) 263 | } 264 | 265 | if isLeftButtonHidden == false { 266 | var leftButtonWidth : CGFloat = 0.0 267 | leftButton.sizeToFit() 268 | if (buttonMinWidth <= leftButton.bounds.size.width) { 269 | leftButtonWidth = leftButton.bounds.size.width + 10 270 | }else { 271 | leftButtonWidth = buttonMinWidth 272 | } 273 | if (leftButton.bounds.size.width > buttonMaxWidth) 274 | { 275 | leftButtonWidth = buttonMaxWidth 276 | } 277 | leftButton.frame = CGRect(x: leftRightDistance, y: 0, width: leftButtonWidth, height: textViewDefaultHeight); 278 | leftButton.ktf_toBottom(offset: (keyboardViewDefaultHeight - textViewDefaultHeight) / 2.0) 279 | } 280 | 281 | if isRightButtonHidden == false { 282 | var rightButtonWidth : CGFloat = 0.0 283 | rightButton.sizeToFit() 284 | if (buttonMinWidth <= rightButton.bounds.size.width) { 285 | rightButtonWidth = rightButton.bounds.size.width + 10; 286 | }else { 287 | rightButtonWidth = buttonMinWidth 288 | } 289 | if (rightButton.bounds.size.width > buttonMaxWidth) 290 | { 291 | rightButtonWidth = buttonMaxWidth; 292 | } 293 | rightButton.frame = CGRect(x: keyboardView.bounds.size.width - leftRightDistance - rightButtonWidth, y: 0, width: rightButtonWidth, height: textViewDefaultHeight); 294 | rightButton.ktf_toBottom(offset: (keyboardViewDefaultHeight - textViewDefaultHeight) / 2.0) 295 | } 296 | 297 | textView.frame = 298 | CGRect( 299 | x: (isLeftButtonHidden == false ? leftButton.frame.origin.x + leftButton.bounds.size.width + middleDistance : leftRightDistance + middleDistance), 300 | y: (keyboardViewDefaultHeight - textViewDefaultHeight) / 2.0 + 0.5, 301 | width: keyboardView.bounds.size.width 302 | - (isLeftButtonHidden == false ? leftButton.bounds.size.width + middleDistance:middleDistance) 303 | - (isRightButtonHidden == false ? rightButton.bounds.size.width + middleDistance:middleDistance) 304 | - leftRightDistance * 2, 305 | height: 306 | textView.ktf_numberOfLines() < maxNumberOfLines ? 307 | textViewCurrentHeightForLines(textView.ktf_numberOfLines()) : 308 | textViewCurrentHeightForLines(maxNumberOfLines) 309 | ) 310 | textViewBackground.frame = textView.frame; 311 | 312 | if placeholderLabel.textAlignment == .left { 313 | placeholderLabel.sizeToFit() 314 | placeholderLabel.frame.origin = CGPoint(x: 8.0, y: (textViewDefaultHeight - placeholderLabel.bounds.size.height) / 2); 315 | 316 | }else if placeholderLabel.textAlignment == .center { 317 | placeholderLabel.frame = placeholderLabel.superview!.bounds 318 | } 319 | 320 | 321 | if let attachmentView = attachmentView { 322 | attachmentView.bounds.size.width = bounds.size.width 323 | switch attachmentViewLocation { 324 | case .up: 325 | attachmentView.frame.origin = CGPoint.zero 326 | case .down: 327 | attachmentView.frame.origin = CGPoint(x: 0, y: keyboardView.bounds.size.height) 328 | } 329 | } 330 | 331 | } 332 | 333 | deinit { 334 | if KeyboardTextFieldDebugMode { 335 | print("\(NSStringFromClass(classForCoder)) has release!") 336 | } 337 | 338 | NotificationCenter.default.removeObserver(self) 339 | } 340 | 341 | fileprivate var isHideing = false 342 | fileprivate var isShowing = false 343 | fileprivate var isAutoLayout = false 344 | 345 | } 346 | 347 | //MARK: TextViewHeight 348 | extension KeyboardTextField { 349 | 350 | fileprivate func textViewCurrentHeightForLines(_ numberOfLines : Int) -> CGFloat { 351 | var height = textViewDefaultHeight - textView.font!.lineHeight 352 | let lineTotalHeight = textView.font!.lineHeight * CGFloat(numberOfLines) 353 | height += CGFloat(roundf(Float(lineTotalHeight))) 354 | return CGFloat(Int(height)); 355 | } 356 | 357 | fileprivate func appropriateInputbarHeight() -> CGFloat { 358 | var height : CGFloat = 0.0; 359 | 360 | if textView.ktf_numberOfLines() == 1 { 361 | height = textViewDefaultHeight; 362 | }else if textView.ktf_numberOfLines() < maxNumberOfLines { 363 | height = textViewCurrentHeightForLines(textView.ktf_numberOfLines()) 364 | } 365 | else { 366 | height = textViewCurrentHeightForLines(maxNumberOfLines) 367 | } 368 | 369 | height += keyboardViewDefaultHeight - textViewDefaultHeight; 370 | 371 | if (height < keyboardViewDefaultHeight) { 372 | height = keyboardViewDefaultHeight; 373 | } 374 | return CGFloat(roundf(Float(height))); 375 | } 376 | 377 | override open func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { 378 | 379 | guard let object = object as? KeyboardTextView,let change = change else { return } 380 | 381 | if object == textView && keyPath == "contentSize" { 382 | if KeyboardTextFieldDebugMode { 383 | if let sizeValue = (change[NSKeyValueChangeKey.newKey] as? NSValue)?.cgSizeValue { 384 | print("\(sizeValue)---\(appropriateInputbarHeight())") 385 | } 386 | } 387 | 388 | let newKeyboardHeight = appropriateInputbarHeight() 389 | if newKeyboardHeight != keyboardView.bounds.size.height && superview != nil { 390 | UIView.animate(withDuration: 0.2, delay: 0.0, options: UIViewAnimationOptions(), animations: { () -> Void in 391 | let lastKeyboardFrameHeight = (self.lastKeyboardFrame.origin.y == 0.0 ? self.superview!.bounds.size.height : self.lastKeyboardFrame.origin.y) 392 | if self.isEditing { 393 | self.frame = CGRect(x: self.frame.origin.x, y: lastKeyboardFrameHeight - newKeyboardHeight - (self.attachmentView?.bounds.size.height ?? 0), width: self.frame.size.width, height: newKeyboardHeight + (self.attachmentView?.bounds.size.height ?? 0)) 394 | self.layoutIfNeeded() 395 | }else { 396 | self.frame = CGRect(x: self.frame.origin.x, y: lastKeyboardFrameHeight - newKeyboardHeight, width: self.frame.size.width, height: newKeyboardHeight) 397 | } 398 | 399 | }, completion:{_ in 400 | }) 401 | } 402 | } 403 | } 404 | } 405 | 406 | //MARK: Keyboard Notification 407 | extension KeyboardTextField { 408 | 409 | public var keyboardAnimationOptions : UIViewAnimationOptions { 410 | return UIViewAnimationOptions(rawValue: (7 as UInt) << 16) 411 | } 412 | public var keyboardAnimationDuration : TimeInterval { 413 | return TimeInterval(0.25) 414 | } 415 | 416 | func registeringKeyboardNotification() { 417 | // Registering for keyboard notification. 418 | 419 | NotificationCenter.default.addObserver(self, selector: #selector(KeyboardTextField.keyboardWillChangeFrame(_:)),name:NSNotification.Name.UIKeyboardWillChangeFrame, object: nil) 420 | NotificationCenter.default.addObserver(self, selector: #selector(KeyboardTextField.keyboardDidChangeFrame(_:)),name:NSNotification.Name.UIKeyboardDidChangeFrame, object: nil) 421 | 422 | // Registering for orientation changes notification 423 | NotificationCenter.default.addObserver(self, selector: #selector(KeyboardTextField.willChangeStatusBarOrientation(_:)),name: NSNotification.Name.UIApplicationWillChangeStatusBarOrientation, object: nil) 424 | 425 | } 426 | 427 | @objc func keyboardWillChangeFrame(_ notification : Notification) { 428 | if window == nil { return } 429 | if !window!.isKeyWindow { return } 430 | 431 | guard let userInfo = notification.userInfo else { return } 432 | let keyboardFrameValue = userInfo[UIKeyboardFrameEndUserInfoKey] as! NSValue 433 | let keyboardFrame = keyboardFrameValue.cgRectValue 434 | lastKeyboardFrame = superview!.convert(keyboardFrame, from: UIApplication.shared.keyWindow) 435 | if KeyboardTextFieldDebugMode { 436 | print("keyboardFrame : \(keyboardFrame)") 437 | } 438 | 439 | UIView.animate(withDuration: keyboardAnimationDuration, delay: 0.0, options: keyboardAnimationOptions, animations: { () -> Void in 440 | if self.isEditing { 441 | self.frame.origin.y = self.lastKeyboardFrame.origin.y - self.keyboardView.bounds.size.height - (self.attachmentView?.bounds.size.height ?? 0) 442 | self.frame.size.height = self.keyboardView.bounds.size.height + (self.attachmentView?.bounds.size.height ?? 0) 443 | self.attachmentView?.alpha = 1 444 | self.attachmentView?.isUserInteractionEnabled = true 445 | }else { 446 | self.frame.origin.y = self.superview!.bounds.size.height - self.keyboardView.bounds.size.height 447 | self.frame.size.height = self.keyboardView.bounds.size.height 448 | self.keyboardView.frame = self.bounds 449 | self.attachmentView?.alpha = 0 450 | self.attachmentView?.isUserInteractionEnabled = false 451 | } 452 | 453 | }, completion: {_ in 454 | if !self.isEditing && self.isHideing { 455 | self.isHideing = false 456 | self.delegate?.keyboardTextFieldDidEndEditing?(self) 457 | } 458 | if self.isEditing && self.isShowing { 459 | self.attachmentView?.moveToTop() 460 | self.bottomBackgroundView.isHidden = false 461 | self.isShowing = false 462 | self.delegate?.keyboardTextFieldDidBeginEditing?(self) 463 | } 464 | }) 465 | } 466 | 467 | @objc func keyboardDidChangeFrame(_ notification : Notification) {} 468 | @objc func willChangeStatusBarOrientation(_ notification : Notification) {} 469 | 470 | } 471 | 472 | 473 | //MARK: TapButtonAction 474 | extension KeyboardTextField { 475 | 476 | @objc func leftButtonAction(_ button : UIButton) { 477 | delegate?.keyboardTextFieldPressLeftButton?(self) 478 | } 479 | 480 | @objc func rightButtonAction(_ button : UIButton) { 481 | delegate?.keyboardTextFieldPressRightButton?(self) 482 | } 483 | 484 | fileprivate var tapButtonTag : Int { return 12345 } 485 | public var tapButton : UIButton { return superview!.viewWithTag(tapButtonTag) as! UIButton } 486 | @objc func tapAction(_ button : UIButton) { 487 | hide() 488 | } 489 | 490 | fileprivate func setTapButtonHidden(_ hidden : Bool) { 491 | if hidden == false { 492 | tapButton.isHidden = hidden 493 | if let tapButtonSuperView = tapButton.superview { 494 | tapButtonSuperView.insertSubview(tapButton, belowSubview: self) 495 | } 496 | tapButton.alpha = 0.0 497 | UIView.animate(withDuration: keyboardAnimationDuration, delay: 0.0, options: keyboardAnimationOptions, animations: { () -> Void in 498 | self.tapButton.alpha = 1.0 499 | }, completion: {_ in 500 | 501 | }) 502 | }else { 503 | UIView.animate(withDuration: keyboardAnimationDuration, delay: 0.0, options: keyboardAnimationOptions, animations: { () -> Void in 504 | self.tapButton.alpha = 0.0 505 | }, completion: {_ in 506 | self.tapButton.isHidden = hidden 507 | }) 508 | } 509 | } 510 | 511 | override open func didMoveToSuperview() { 512 | if let superview = superview { 513 | let tapButton = UIButton(frame: superview.bounds) 514 | tapButton.addTarget(self, action: #selector(KeyboardTextField.tapAction(_:)), for: UIControlEvents.touchUpInside) 515 | tapButton.tag = tapButtonTag 516 | tapButton.isHidden = true 517 | tapButton.autoresizingMask = [UIViewAutoresizing.flexibleWidth, UIViewAutoresizing.flexibleHeight] 518 | tapButton.backgroundColor = tapButtonBackgroundColor 519 | superview.insertSubview(tapButton, at: 0) 520 | } 521 | } 522 | 523 | override open func willMove(toSuperview newSuperview: UIView?) { 524 | if ((superview != nil) && newSuperview == nil) { 525 | superview?.viewWithTag(tapButtonTag)?.removeFromSuperview() 526 | textView.removeObserver(self, forKeyPath: "contentSize", context: nil) 527 | } 528 | } 529 | } 530 | 531 | 532 | //MARK: UITextViewDelegate 533 | extension KeyboardTextField : UITextViewDelegate { 534 | 535 | public func textViewDidChange(_ textView: UITextView) { 536 | 537 | if (textView.text.isEmpty) { 538 | placeholderLabel.alpha = 1 539 | } 540 | else { 541 | placeholderLabel.alpha = 0 542 | } 543 | 544 | delegate?.keyboardTextField?(self, didChangeText: textView.text) 545 | } 546 | 547 | public func textViewShouldBeginEditing(_ textView: UITextView) -> Bool { 548 | if isEditing == false { 549 | if !translatesAutoresizingMaskIntoConstraints { 550 | isAutoLayout = true 551 | translatesAutoresizingMaskIntoConstraints = true 552 | } 553 | isShowing = true 554 | delegate?.keyboardTextFieldWillBeginEditing?(self) 555 | } 556 | isEditing = true 557 | setTapButtonHidden(false) 558 | return true 559 | } 560 | 561 | public func textViewShouldEndEditing(_ textView: UITextView) -> Bool { 562 | return true 563 | } 564 | 565 | public func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool { 566 | if isSending { return false } 567 | if text == "\n" { 568 | if isSending == false { 569 | done() 570 | } 571 | return false 572 | } 573 | return true 574 | } 575 | } 576 | 577 | public final class KeyboardTextView : UITextView { 578 | 579 | private var hasDragging : Bool = false 580 | 581 | override open func layoutSubviews() { 582 | super.layoutSubviews() 583 | if isDragging == false { 584 | if hasDragging { 585 | let delayTime = DispatchTime.now() + Double(Int64(1 * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC) 586 | DispatchQueue.main.asyncAfter(deadline: delayTime) { 587 | self.hasDragging = false 588 | } 589 | }else { 590 | if selectedRange.location == text.count { 591 | contentOffset = CGPoint(x: contentOffset.x, y: (contentSize.height + 2) - bounds.size.height) 592 | } 593 | } 594 | }else { 595 | hasDragging = true 596 | } 597 | } 598 | 599 | } 600 | 601 | //MARK: UITextView extension 602 | extension UITextView { 603 | 604 | fileprivate func ktf_numberOfLines() -> Int { 605 | let text = self.text as NSString 606 | let textAttributes = [NSAttributedStringKey.font: font!] 607 | var width: CGFloat = UIEdgeInsetsInsetRect(frame, textContainerInset).width 608 | width -= 2.0 * textContainer.lineFragmentPadding 609 | let boundingRect: CGRect = text.boundingRect(with: CGSize(width:width,height:9999), options: [NSStringDrawingOptions.usesLineFragmentOrigin , NSStringDrawingOptions.usesFontLeading], attributes: textAttributes, context: nil) 610 | let line = boundingRect.height / font!.lineHeight 611 | if line < 1.0 { return 1 } 612 | return abs(Int(line)) 613 | } 614 | } 615 | 616 | extension UIView { 617 | /** 618 | 将视图移动到父视图的底端 619 | - parameter offset: 可进行微调 大于0 则 小于0 则 620 | */ 621 | fileprivate func ktf_toBottom(offset : CGFloat = 0.0) { 622 | if let superView = superview { 623 | frame.origin.y = superView.bounds.size.height - offset - frame.size.height; 624 | } 625 | } 626 | 627 | public func moveToTop() { 628 | superview?.bringSubview(toFront: self) 629 | } 630 | 631 | public func moveToBottom() { 632 | superview?.sendSubview(toBack: self) 633 | } 634 | } 635 | --------------------------------------------------------------------------------