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()
}
Related
I have a UITextView and NSMutableAttributedString which has a clickable link. I need this to show another storyboard but not quite sure how I could do this.
What I have so far works for external links but not a storyboard. Any help would be appreciated.
Not sure if the below code is the best way to approach it or not?
class HomeViewController: UIViewController {
#IBOutlet weak var textView: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
let attributedString = NSMutableAttributedString(string: "Already have an account? Log in")
attributedString.addAttribute(.link, value: "", range: NSRange(location: 25, length: 6))
textView.attributedText = attributedString
}
func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool {
UIApplication.shared.open(URL)
return false
}
}
EDIT
After NikR answer I've updated my code to the following but still no success. It's still loading Google.
class HomeViewController: UIViewController, UITextViewDelegate {
#IBOutlet weak var textView: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
let attributedString = NSMutableAttributedString(string: "Already have an account? Log in")
attributedString.addAttribute(.link, value: "https://google.com", range: NSRange(location: 25, length: 6))
textView.attributedText = attributedString
textView.isSelectable = true
textView.isEditable = false
textView.delegate = self
}
func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool {
let loginViewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "loginViewController")
show(loginViewController, sender: self)
return true
}
}
Yes it's the right way.
But not call:
UIApplication.shared.open(URL)
Simply initiate your VC and show/present it:
let vc = UIStoryboard(name: "StoryboardName", bundle: nil).instantiateInitialViewController()
show( vc, sender: self )
And don't forget to:
textView.delegate = self
you can set to your textView:
textView.dataDetectorTypes = .link
You should enable isSelectable and disable isEditable for your text view.
textView.isSelectable = true
textView.isEditable = false
Don't forget to do this
textView.delegate = self
This Delegate method will use to handle click
func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool {
}
I have a UITextView in my ViewController, When user press on that textView, the keyboard shows up. This is not the problem. My problem is: How to hide the keyboard when user press return button in the keyboard? I have tried some functions but apparently they worked only with UITextFields.
import UIKit
class NewNoteVC: UIViewController, UITextViewDelegate {
#IBOutlet weak var newText: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
newText.text = "Start typing"
newText.textColor = UIColor.lightGray
newText.tintColor = UIColor(red: 251/255, green: 140/255, blue: 139/255, alpha: 1)
}
/// setting placeholdere
func textViewDidBeginEditing(_ textView: UITextView) {
if newText.textColor == UIColor.lightGray {
newText.text = nil
newText.textColor = UIColor.black
}
}
func textViewDidEndEditing(_ textView: UITextView) {
if newText.text.isEmpty {
newText.text = "Placeholder"
newText.textColor = UIColor.lightGray
}
}
/// limit characters
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
let currentText = newText.text ?? ""
guard let stringRange = Range(range, in: currentText) else { return false }
let changedText = currentText.replacingCharacters(in: stringRange, with: text)
return changedText.count <= 1000
}
/// hide keyboard when user touch outside screen
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
self.view.endEditing(true)
}
/// hide keyboard when user press return button
#IBAction func backToMainPage(_ sender: Any) {
dismiss(animated: true, completion: nil)
}
}
Whatever the reason might be to dismiss keyboard on return of a UITextView the simplest solution would be:
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
if text == "\n" {
textView.resignFirstResponder()
}
{...}
}
I am working in a form in swift 3, my problem is when I tried to hide the keyboard in textview because the keyboard reuse to hide and is still visible in the previous VC.
My question is what I missing to fix this and when tap outside of textview the keyboard hide automatically?
Here the code:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
self.view.endEditing(true)
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
return(true)
}
func textFieldDidBeginEditing(_ textField: UITextField) {
if(textField == names){
scrooView.setContentOffset(CGPoint(x:0, y:250), animated: true)
}
else if(textField == email){
scrooView.setContentOffset(CGPoint(x:0, y:250), animated: true)
}
else if (textField == tel){
scrooView.setContentOffset(CGPoint(x:0, y:250), animated: true)
}
else if (textField == message){
scrooView.setContentOffset(CGPoint(x:0, y:450), animated: true)
}
}
func textFieldDidEndEditing(_ textField: UITextField, reason:
UITextFieldDidEndEditingReason) {
scrooView.setContentOffset(CGPoint(x:0, y:0), animated: true)
}
extension UIViewController{
func hideKeyboard(){
let tap: UITapGestureRecognizer = UITapGestureRecognizer(
target: self,
action: #selector(UIViewController.dismissKeyboard))
view.addGestureRecognizer(tap)
}
#objc func dismissKeyboard(){
view.endEditing(true)
}
}
Here a link with the issue!
You are using a scrollView, try to add the GestureRecognizer in them, like this:
class ViewController: UIViewController {
#IBOutlet weak var scrooView: UIScrollView!
#IBOutlet weak var tfTest: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
scrooView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard)))
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#objc func dismissKeyboard(){
tfTest.endEditing(true)
}
})
If you add your Gesture in your View, it could never actived 'cause you are touching the scrollview that is over the view.
Is there an easy way to deselect an NSTextField after pressing enter?
First you will need to make your view controller the delegate of your text field. Then you override NSControl instance method controlTextDidEndEditing(_:), get your textfield current editor selected range
and from the main thread set it back to your textfield:
import Cocoa
class ViewController: NSViewController, NSTextFieldDelegate {
#IBOutlet weak var textField: NSTextField!
override func viewDidLoad() {
super.viewDidLoad()
textField.delegate = self
}
override func controlTextDidEndEditing(_ obj: Notification) {
if let selectedRange = textField.currentEditor()?.selectedRange {
DispatchQueue.main.async {
self.textField.currentEditor()?.selectedRange = selectedRange
}
}
}
}
Here is one way I did it.
By disabling it with isSelectable and isEditable and then setting a timer to re-enable it after 0.5s
#IBAction func timeCodeChanged(_: NSTextField) {
timecodeLabel.isSelectable = false
timecodeLabel.isEditable = false
Timer.scheduledTimer(timeInterval: 0.5, target: self, selector: #selector(reEnableLabel), userInfo: nil, repeats: false)
}
#objc func reEnableLabel() {
timecodeLabel.isSelectable = true
timecodeLabel.isEditable = true
}
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.