Keyboard not being shown correctly - swift

I have got the following functions that make the keyboard not to cover the TextView, but the keyboard is not showing correctly. Instead, there appears a kind of all black "keyboard" with no keyboard keys.
func textViewDidBeginEditing(_ textView: UITextView) {
moveTextView(textView, moveDistance: -250, up: true)
}
func textViewDidEndEditing(_ textView: UITextView) {
moveTextView(textView, moveDistance: -250, up: false)
}
func textViewShouldReturn(_ textView: UITextView) -> Bool {
textView.resignFirstResponder()
return true
}
func moveTextView(_ textView: UITextView, moveDistance: Int, up: Bool) {
let moveDuration = 0.3
let movement: CGFloat = CGFloat(up ? moveDistance : -moveDistance)
UIView.beginAnimations("animateTextView", context: nil)
UIView.setAnimationBeginsFromCurrentState(true)
UIView.setAnimationDuration(moveDuration)
self.view.frame = self.view.frame.offsetBy(dx: 0, dy: movement)
UIView.commitAnimations()
}
Do somebody have an idea of why and how to fix it?
Thank you for your time.

Try to do it another way. Add UITextViewDelegate to your viewController. Add smth like this in viewDidLoad():
self.yourTextView1.delegate = self
self.yourTextView2.delegate = self
//For scrolling the view if keyboard on
NotificationCenter.default.addObserver(self, selector: #selector(YourViewController.keyboardWillShow), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(YourViewController.keyboardWillHide), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
And add this to your ViewController:
var keyBoardAlreadyShowed = false //using this to not let app to scroll view
//if we tapped UITextField and then another UITextField
func keyboardWillShow(notification: NSNotification) {
if !keyBoardAlreadyShowed {
self.view.frame.origin.y -= 50 // we will scroll on it
keyBoardAlreadyShowed = true
}
}
func keyboardWillHide(notification: NSNotification) {
self.view.frame.origin.y += 50
keyBoardAlreadyShowed = false
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
self.view.endEditing(true)
}
Hope it helps

Try this:
iOS Simulator > Restore Content and Settings.
Clean the Project, and restart Xcode.

Related

Move view using Scroll View to prevent keyboard from obscuring text field or text view

I am implementing an app where tapping a button will pop up a location entry screen which is implemented with Modally presented Segue and Visual Effect with Blur to make the entry window appear hovering above the main screen.
The pop up screen has a text field for location name and a multi line text view for remarks.
Problem is the multi line text view is partially obscured by the keyboard on smaller devices and in landscape view.
Landscape View
Portrait View
Modal Segue View
I tried to utiize the solution described here, which is supposed to utilize scroll view to shift the view when keyboard appears, but it doesn't seem to work for my project: Move a view up only when the keyboard covers an input field
This question doesn't answer my problem as my text field and view are inside another view to make it appear like a popup to the main screen: Move a view up only when the keyboard covers an input field
In order to implement the pop up view (in a modal segue) I placed the text field and view inside a View which is placed inside a Scroll View which is located inside the view of the Visual Effects with Blur View.
Another problem I'm experiencing is I impleneted the following code to dismiss keyboard when tapping outside the text field/view, but for some reason it doesn't seem to work
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?)
{
self.view.endEditing(true)
}
Here is the code for the View Controller of the pop up view in Xcode 9.2
Archive of the project: Xcode Project Archive
//
// LocationEntryScrollViewController.swift
//
import UIKit
class LocationEntryScrollViewController: UIViewController, UITextViewDelegate, UITextFieldDelegate {
#IBOutlet weak var ScrollView: UIScrollView!
#IBOutlet weak var LocationEntryView: UIView!
#IBOutlet weak var LocationTextField: UITextField!
#IBOutlet weak var RemarksTextView: UITextView!
var activeTextField: UITextField?
var activeTextView: UITextView?
override func viewDidLoad()
{
super.viewDidLoad()
registerForKeyboardNotifications()
LocationEntryView.layer.cornerRadius = 10
LocationEntryView.layer.masksToBounds = true
LocationTextField.delegate = self // dismiss keyboard when tapped outside keyboard
RemarksTextView.delegate = self // dismiss keyboard when tapped outside keyboard
}
override func viewDidAppear(_ animated: Bool)
{
super.viewDidAppear(animated)
LocationTextField.becomeFirstResponder()
RemarksTextView.becomeFirstResponder()
}
/**
* Called when the user click on the view (outside the UITextField).
*/
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?)
{
self.view.endEditing(true)
}
override func viewDidDisappear(_ animated: Bool)
{
deregisterFromKeyboardNotifications()
}
override func didReceiveMemoryWarning()
{
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func DoneButtonTapped(_ sender: Any)
{
dismiss(animated: true, completion: nil)
}
#IBAction func CancelButtonTapped(_ sender: Any)
{
}
#objc func keyboardWasShown(notification: NSNotification)
{
//Need to calculate keyboard exact size due to Apple suggestions
self.ScrollView.isScrollEnabled = true
var info = notification.userInfo!
let keyboardSize = (info[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue.size
let contentInsets : UIEdgeInsets = UIEdgeInsetsMake(0.0, 0.0, keyboardSize!.height, 0.0)
self.ScrollView.contentInset = contentInsets
self.ScrollView.scrollIndicatorInsets = contentInsets
var aRect : CGRect = self.view.frame
aRect.size.height -= keyboardSize!.height
if let activeTextField = self.activeTextField
{
if (!aRect.contains(activeTextField.frame.origin))
{
self.ScrollView.scrollRectToVisible(activeTextField.frame, animated: true)
}
}
if let activeTextView = self.activeTextView
{
if (!aRect.contains(activeTextView.frame.origin))
{
self.ScrollView.scrollRectToVisible(activeTextView.frame, animated: true)
}
}
}
#objc func keyboardWillBeHidden(notification: NSNotification)
{
//Once keyboard disappears, restore original positions
let info : NSDictionary = notification.userInfo! as NSDictionary
let keyboardSize = (info[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue.size
let contentInsets : UIEdgeInsets = UIEdgeInsetsMake(0.0, 0.0, -keyboardSize!.height, 0.0)
self.ScrollView.contentInset = contentInsets
self.ScrollView.scrollIndicatorInsets = contentInsets
self.view.endEditing(true)
self.ScrollView.isScrollEnabled = false
}
func textFieldDidBeginEditing(_ textField: UITextField)
{
activeTextField = textField
}
func textFieldDidEndEditing(_ textField: UITextField)
{
activeTextField = nil
}
func textViewDidBeginEditing(_ textView: UITextView)
{
activeTextView = textView
}
func textViewDidEndEditing(_ textView: UITextView) {
activeTextView = nil
}
func registerForKeyboardNotifications()
{
//Adding notifies on keyboard appearing
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWasShown(notification:)), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillBeHidden(notification:)), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}
func deregisterFromKeyboardNotifications()
{
//Removing notifies on keyboard appearing
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillShow, object: nil)
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}
}

Does keyboard textfield depends from textfield constraint?

I'm having a problem with keyboard textfield. I'm working on swift 4 in iOS 9.2
I have a login screen which does have a lot of constraints (to be adaptable for every screen). In my login screen, I have a username and password textfield.
The problem is that when I launch the application on my IPad, the keyboard appears in center of the associated textField. Moreover, the observer won't work : When I use debugger, I never get inside the observers
But when I launch on simulator, everything works perfectly.
#My object life cycle
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillHide(_:)), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillShow(_:)), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillHide, object: nil)
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillShow, object: nil)
}
# My textfield delegates and observers
extension LoginViewController {
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
#objc func keyboardWillShow(_ notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
if self.view.frame.origin.y == 0{
self.view.frame.origin.y -= keyboardSize.height
}
}
}
#objc func keyboardWillHide(_ notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
if self.view.frame.origin.y != 0{
self.view.frame.origin.y += keyboardSize.height
}
}
}
}
And for my textfield storyboard configuration, here is it
So what didn't I understood ?
Is the keyboard also dependants to textfield constraints ?
Why the observers aren't working on iPad but works in simulation ?
how can I move the keyboard programatically ?
Make sure your iPad/iPhone have the keyboard dock..
https://discussions.apple.com/thread/5430957
You're not inheriting from UITextFieldDelegate in your extension. Also, make sure you set your textfields as self delegate in viewDidLoad method.
#IBOutlet weak var yourViewName: UIView!
override func viewDidLoad() {
loginTextField.delegate = self
passwordTextField.delegate = self
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector:#selector(LoginViewController.keyboardWillShow(notification:)), name: NSNotification.Name.UIKeyboardDidShow, object: nil)
NotificationCenter.default.addObserver(self, selector:#selector(LoginViewController.keyboardWillHide(notification:)), name: NSNotification.Name.UIKeyboardDidHide, object: nil)
}
extension LoginViewController : UITextFieldDelegate {
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
#objc func keyboardWillShow(notification: NSNotification) {
UIView.animate(withDuration: 0.3) {
self.usernameField.frame.origin.y -= 43
self.usernameLine.frame.origin.y -= 43
self.passwordField.frame.origin.y -= 43
self.passwordLine.frame.origin.y -= 43
self.loginBtn.frame.origin.y -= 65
}
}
#objc func keyboardWillHide(notification: NSNotification) {
UIView.animate(withDuration: 0.3) {
self.usernameField.frame.origin.y += 33
self.usernameLine.frame.origin.y += 33
self.passwordField.frame.origin.y += 33
self.passwordLine.frame.origin.y += 33
self.loginBtn.frame.origin.y += 55
}
}
easies way , you first take one scroll view and add top , bottom , leading , trailing contains of all 4 with value 0 and than take one view and add 4 constrain for view to top,bottom,leading,traling to scrollview and put your textfield inside that view and add constrains as per your design concept relative to view which you added in this scrollview ,
now , whenever your keyboard will open your view will move toward upside and textfield and keyboard both will be look properly

Move button when keyboard appears swift

In a UIViewController I have several text fields and a button is on the bottom of the UIViewController.
For the button, I have set a bottom constraint with a constant of 0.
Then I made an outlet from the bottom constraint to the UIViewController.
When I run my code, the button does not move upwards. I have seen suggestions on stackoverflow that I should add UIScrollView, but that means, I would have to delete all the objects on the UIViewController, put the UIScrollView and then put my objects on the UIVIewController again.
#IBOutlet weak var bottomConstraint: NSLayoutConstraint!
// When tapping outside of the keyboard, close the keyboard down
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
self.view.endEditing(true)
}
// Stop Editing on Return Key Tap. textField parameter refers to any textfield within the view
func textFieldShouldReturn(textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
// When keyboard is about to show assign the height of the keyboard to bottomConstraint.constant of our button so that it will move up
func keyboardWillShow(notification: NSNotification) {
if let userInfo = notification.userInfo {
if let keyboardSize: CGRect = (userInfo[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
bottomConstraint.constant = keyboardSize.size.height
view.setNeedsLayout()
}
}
}
// When keyboard is hidden, move the button to the bottom of the view
func keyboardWillHide(notification: NSNotification) {
bottomConstraint.constant = 0.0
view.setNeedsLayout()
}
The typical way to address this would be to move the keyboard with code like this:
in ViewController class:
func keyboardWillShow(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
if view.frame.origin.y == 0{
let height = keyboardSize.height
self.view.frame.origin.y += height
}
}
}
func keyboardWillHide(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
if view.frame.origin.y != 0 {
let height = keyboardSize.height
self.view.frame.origin.y -= height
}
}
}
in ViewDidLoad method:
NotificationCenter.default.addObserver(self, selector: Selector("keyboardWillShow:"), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self, selector: Selector("keyboardWillHide:"), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
Please Read This:
The way you are trying to solve your problem is not allowed. In the code above, if you change view to your button variable name, the button will shoot up and then fall back down. This is because Auto Layout and Programmatic layout do not work together, it is one or the other. The way you fix this is by programmatically creating that button (with CGRect), then using the code above to move only that button on keyboard press. (Do that by changing view to your button variable name.
func keyboardWillShow(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
if view.frame.origin.y == 0{
let height = keyboardSize.height
self.yourBtn.frame.origin.y += height
}
}
}
func keyboardWillHide(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
if view.frame.origin.y != 0 {
let height = keyboardSize.height
self.yourBtn.frame.origin.y -= height
}
}
}
To programmatically create the button you would use code similar to this:
myButton.frame = CGRect(...)
Complimentary to Ryan's answer above, this can be done all with auto-layout and no need for frames and CGRect.
Swift 5
In your view, constrain your button as you normally would but add a reference to the constraint for modification when the keyboard hides/shows:
var bottomButtonConstraint = NSLayoutConstraint()
bottomButtonConstraint = yourButton.bottomAnchor.constraint(equalTo: safeAreaLayoutGuide.bottomAnchor, constant: -12)
bottomButtonConstraint.isActive = true
In your ViewController's viewDidLoad():
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(notification:)), name: UIResponder.keyboardWillShowNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(notification:)), name: UIResponder.keyboardWillHideNotification, object: nil)
Also in your ViewController:
#objc private func keyboardWillShow(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
self.yourCustomView.bottomButtonConstraint.constant -= keyboardSize.height
}
}
#objc private func keyboardWillHide(notification: NSNotification) {
self.yourCustomView.bottomButtonConstraint.constant = -12
}
You need add(viewDidLoad) observers to call your functions:
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(keyboardWillShow), name: UIKeyboardDidShowNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(keyboardWillHide), name: UIKeyboardDidHideNotification, object: nil)
Consider using this pod: https://cocoapods.org/pods/IQKeyboardManager
In AppDelegate.swift, just import IQKeyboardManagerSwift framework and enable IQKeyboardManager.
import IQKeyboardManagerSwift
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
IQKeyboardManager.shared.enable = true
return true
}
}

MouseMoved function is never called

I'm trying to override mouseMoved function in NSViewController.
import Cocoa
class MainViewController: NSViewController {
override var acceptsFirstResponder: Bool {get {return true} }
#IBOutlet var background: RandomNumberBackground!
override func viewDidLoad() {
super.viewDidLoad()
}
override func awakeFromNib() {
NSTimer.scheduledTimerWithTimeInterval(0.04, target: background, selector: "updateNumbers", userInfo: nil, repeats: true)
}
#IBAction func btnLevelClicked(sender: AnyObject) {
self.presentViewControllerAsSheet(LevelScrollController())
}
override func mouseMoved(theEvent: NSEvent) {
Swift.print("MOVED!")
}
}
I've overrided acceptsFirstResponder but mouseMoved is never called. Why? Where I go wrong?
You need to set acceptsMouseMovedEvents in the windows property.
Add the following code to applicationDidFinishLaunching
func applicationDidFinishLaunching(aNotification: NSNotification) {
window.acceptsMouseMovedEvents = true
}
No need to set anything on the window.
A tracking area is the proper way to handle this.
Just add this in the VC's vieWDidLoad() method.
let ta = NSTrackingArea(rect: CGRect.zero, options: [.activeAlways, .inVisibleRect, .mouseMoved], owner: self, userInfo: nil)
self.view.addTrackingArea(ta)
This is the code I add when I need to track mouse moves in a view.
It is longer than previous answers, but this is what it takes to be done properly :)
class MyView: NSView {
var trackingArea: NSTrackingArea?
// MARK: - Tracking area management
/// Will install tracking area on the view if a window is set
override func viewDidMoveToSuperview() {
super.viewDidMoveToSuperview()
installTrackingArea()
}
/// Install tracking area if window is set, remove previous one if needed.
func installTrackingArea() {
guard let window = window else { return }
window.acceptsMouseMovedEvents = true
if trackingArea != nil { removeTrackingArea(trackingArea!) }
let trackingOptions = [.activeAlways, .mouseEnteredAndExited, .mouseMoved]
trackingArea = NSTrackingArea(rect: bounds,
options: trackingOptions,
owner: self, userInfo: nil)
self.addTrackingArea(trackingArea!)
}
/// Called when layout is modified
override func updateTrackingAreas() {
super.updateTrackingAreas()
installTrackingArea()
}
//MARK: - Mouse Events handling
override func mouseExited(with event: NSEvent) {
print("Good bye mouse")
}
override func mouseEntered(with event: NSEvent) {
let point = self.convert(event.locationInWindow, from: nil)
print("Hello mouse, welcome at \(point)")
}
override func mouseMoved(with event: NSEvent) {
let point = self.convert(event.locationInWindow, from: nil)
print("Mouse moved \(point)")
}
}
You need to set the acceptsMouseMovedEvents property on the window the view belongs to. See
https://developer.apple.com/library/mac/documentation/Cocoa/Reference/ApplicationKit/Classes/NSWindow_Class/index.html#//apple_ref/occ/instp/NSWindow/acceptsMouseMovedEvents
In swift 3 you can put this in your viewDidLoad()
self.view!.window?.acceptsMouseMovedEvents = true;

dismiss keyboard with a uiTextView

I am sure this is not that difficult, but I am having trouble finding info on how to dismiss a keyboard with the return/done key using a textview, not a textfield. here is what I have tried so far(which works with a textfield.)
Thanks very much in advance for any help!
// PostTravelQuestion.swift
class PostTravelQuestion: UIViewController, UITextViewDelegate {
#IBAction func closepostpage(sender: AnyObject) {
dismissViewControllerAnimated(true, completion: nil)
}
#IBOutlet var postquestion: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
postquestion.delegate = self
}
self addDoneToolBarToKeyboard:self.textView
/*func textViewShouldEndEditing(textView: UITextView) -> Bool {
textView.resignFirstResponder()
return true
}*/
/*override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
postquestion.resignFirstResponder()
self.view.endEditing(true)
}*/
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func textViewShouldReturn(textView: UITextView!) -> Bool {
self.view.endEditing(true);
return true;
}
}
This works for me:
import UIKit
class ViewController: UIViewController, UITextViewDelegate {
#IBOutlet weak var textView: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
textView.delegate = self
}
/* Updated for Swift 4 */
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
if(text == "\n") {
textView.resignFirstResponder()
return false
}
return true
}
/* Older versions of Swift */
func textView(textView: UITextView, shouldChangeTextInRange range: NSRange, replacementText text: String) -> Bool {
if(text == "\n") {
textView.resignFirstResponder()
return false
}
return true
}
}
Add UITextViewDelegate to your class and then set your delegate for your textView or your textField in viewDidLoad. Should look something like this:
// in viewDidLoad
textField.delegate = self
textView.delegate = self
Swift 3
// hides text views
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
if (text == "\n") {
textView.resignFirstResponder()
return false
}
return true
}
// hides text fields
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if (string == "\n") {
textField.resignFirstResponder()
return false
}
return true
}
Swift 2.0
The below syntax has been tested for Swift 1.2 & Swift 2.0
func textView(textView: UITextView, shouldChangeTextInRange range: NSRange, replacementText text: String) -> Bool {
if(text == "\n") {
textView.resignFirstResponder()
return false
}
return true
}
Below code will dismissing the keyboard when click return/done key on UITextView.
In Swift 3.0
import UIKit
class ViewController: UIViewController, UITextViewDelegate {
#IBOutlet var textView: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
textView.delegate = self
}
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool
{
if(text == "\n")
{
view.endEditing(true)
return false
}
else
{
return true
}
}
In Swift 2.2
func textView(textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool
{
if text == "\n"
{
view.endEditing(true)
return false
}
else
{
return true
}
}
Easiest and best way to do this using UITextView Extension.
Credit: http://www.swiftdevcenter.com/uitextview-dismiss-keyboard-swift/
Your ViewController Class
class ViewController: UIViewController {
#IBOutlet weak var myTextView: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
// 1
self.myTextView.addDoneButton(title: "Done", target: self, selector: #selector(tapDone(sender:)))
}
// 2
#objc func tapDone(sender: Any) {
self.view.endEditing(true)
}
}
Add UITextView Extension
extension UITextView {
func addDoneButton(title: String, target: Any, selector: Selector) {
let toolBar = UIToolbar(frame: CGRect(x: 0.0,
y: 0.0,
width: UIScreen.main.bounds.size.width,
height: 44.0))//1
let flexible = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)//2
let barButton = UIBarButtonItem(title: title, style: .plain, target: target, action: selector)//3
toolBar.setItems([flexible, barButton], animated: false)//4
self.inputAccessoryView = toolBar//5
}
}
For more detail: visit full documentation
to hide the keyboard touch on any part outside the textbox or textviews in swift 4 use this peace of code in the ViewController class:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
view.endEditing(true)
super.touchesBegan(touches, with event: event)
}
Regards
Building on the answers of others (kudos!), here is my minimalistic take on it:
import UIKit
extension UITextView {
func withDoneButton(toolBarHeight: CGFloat = 44) {
guard UIDevice.current.userInterfaceIdiom == .phone else {
print("Adding Done button to the keyboard makes sense only on iPhones")
return
}
let toolBar = UIToolbar(frame: CGRect(x: 0.0, y: 0.0, width: UIScreen.main.bounds.width, height: toolBarHeight))
let flexibleSpace = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
let doneButton = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(endEditing))
toolBar.setItems([flexibleSpace, doneButton], animated: false)
inputAccessoryView = toolBar
}
}
Usage:
override func viewDidLoad() {
super.viewDidLoad()
textView.withDoneButton()
}