How to add target to UITextField in a class other than ViewController - ios

I am trying to write a class that has a method which observe text changes on UITextField objects.
When in ViewController, code below works as intended:
class ViewController: UIViewController {
#IBOutlet weak var password: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
textField.addTarget(view, action: #selector(textFieldDidChange(_:)), for: .editingChanged)
}
#objc func textFieldDidChange(_ textField: UITextField) {
print(textField.text!)
}
}
So i wrote a class and put methods in it as below:
internal class ListenerModule: NSObject, UITextFieldDelegate {
internal func textWatcher(textField: UITextField!, view: UIViewController!) {
textField.delegate = self
textField.addTarget(self, action: #selector(self.textFieldDidChange(_:)), for: .editingChanged)
}
#objc internal func textFieldDidChange(_ textField: UITextField) {
print(textField.text!)
}
}
//And in ViewController,
...
ListenerModule().textWatcher(textField: password, view: self)
...
But it does not work.
How can i add target to a TextField in a class or a library?

I think it could be because you are not persisting with your ListenerModule object.
I believe you are doing this ListenerModule().textWatcher(textField: password, view: self) in some function so the scope of the object created is limited to that function.
You could do the following:
// Globally in your UIViewController subclass
var listenerModule: ListenerModule?
// Some set up function after text field is initialized
private func setup()
{
listenerModule = ListenerModule()
// Then call the text watcher, not sure why you pass the view,
// doesn't seem like you use it
listenerModule?.textWatcher(textField: password, view: self)
}
Give this a try and see if this solves your issue

Related

Using #objc delegate methods with multiple arguments

I want to be able to pass multiple arguments to a #selector() method other than only the sender itself.
Say, I have a UITextField which has a UITapGestureRecognizer, and I want some other class to be the delegate of this UITapGestureRecognizer. I write a delegate protocol for it called SomeDelegateProcotol. However, I also want to pass the instance of the UITextField to the delegate upon tap. I figured things might look something like this:
// The delegate
class Delegate: SomeDelegateProcotol {
private let textField = TextField()
func handleTapFromView(_ sender: UITapGestureRecognizer, textField: UITextField) {
print("Hey! I should handle the tap from the user.")
}
init() {
textField.delegate = self
}
}
// The protocol
#objc protocol SomeDelegateProtocol {
#objc func handletapFromView(_ sender: UITapGestureRecognizer, textField: UITextField)
}
class TextField: UITextField {
weak var delegate: SomeDelegateProtocol?
override init(frame: CGSize) {
super.init(frame: frame)
...
let gestureRecognizer = UITapGestureRecognizer(target: delegate!,
action: #selector(delegate!.handleTapFromView(_:, textField:
self)))
}
}
However, this is not the right syntax, as handleTapFromView(_:, textField: self) is invalid. This raises the following questions to which I haven't found a solution yet:
What exactly means this syntax? (_:). I assume it's passing itself, but the UITapGestureRecognizer is not created yet?
How do I successfully pass the TextField instance to the delegate alongside the sender?
I would suggest keeping things as simple as this,
protocol SomeDelegateProtocol: class {
func handletapFromView(_ sender: UITapGestureRecognizer, textField: UITextField)
}
class TextField: UITextField {
weak var someDelegate: SomeDelegateProtocol?
override init(frame: CGRect) {
super.init(frame: frame)
let tap = UITapGestureRecognizer.init(target: self, action: #selector(tap(_:)))
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
#objc private func tap(_ sender: UITapGestureRecognizer) {
self.someDelegate?.handletapFromView(sender, textField: self)
}
}

Hiding the Keyboard triggered by textField via resignFirstResponder

Environment: Xcode Version 8.2 (8C38)/Swift 3.0
A textFiled object in the View is wire up to a method named textFieldReturn in the controller via IBAction. The related codes are presented as follow
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func textFieldReturn(_ sender: Any) {
_ = (sender as AnyObject).resignFirstResponder()
}
}
What I expect:
When I hit the Return key of the virtual keyboard the function textFieldReturn(_:) will be called and the keyboard will be hidden
Issue Observer:
The function is not called after I tapped the return key, the keyboard is still there
Resource:
This code spinet come from the example of the Chapter 16 of the book iOS 10 App Development Essentials by Neil Symth (pp-114)
The only difference between this code and original code is the type of the function argument (Sender). It is AnyObject in the original book while I've got Any by default, therefore I've cast to AnyObject inside the function body
Question:
Its seems to be a decent book, but the sample code doesn't work for me. How can I call the resignFirstResponder() method when I hit the return key
Alternative try out:
Instead of using IBAction, I turn to the idea of delegate, I've set the VeiwController as the delegate of the textField
class ViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var tempText: UITextField! //reference the TextField as the variable **tempText** in the controller
override func viewDidLoad() {
super.viewDidLoad()
self.tempText.delegate = self //set up the delegation
}
func textFieldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
return(true)
}
}
Problem
The alternative solution still not working.
Thanks for your time and help
Why are you not using the original delegate function of UITextField?
I think the default function will work as you want:
class ViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var tempText: UITextField! //reference the TextField as the variable **tempText** in the controller
override func viewDidLoad() {
super.viewDidLoad()
self.tempText.delegate = self //set up the delegation
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool{
textField.resignFirstResponder()
return false
}
}
Replace your textFieldReturn method with this and it should work just fine.
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
Make sure to keep the following in your viewDidLoad()
self.tempText.delegate = self

Swift: recognize when UITextField is tapped

I am new to swift and have been stuck on this for hours. I am trying to recognize when a UITextField is tapped by the user, and call some function. For some reason I keep getting "unrecognized selector sent to instance".
Here is my attempt at a solution
and
Here is the error thrown
Any help is greatly appreciated!
Your selector is not pointing to the method! Try this instead:
textField.addTarget(self, action: #selector(ViewController.tapped(_:)), for: UIControlEvents.touchDown)
Also, instead of this approach you could also set ViewController as the delegate for textField and implement this:
extension ViewController: UITextFieldDelegate {
func textFieldDidBeginEditing(_ textField: UITextField) {
print("tapped")
textField.backgroundColor = UIColor.blue
}
}
Use: action: #selector(ViewController.tapped())
Also, below that, add this: self.textField.delegate = self
you can simply perform you action in UITexFieldDelegate,
by making
myTextField.delegate = self
and use the method
optional public func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool{
//perform you action here
return true
}
instead of finding a way to get tap event you can use textFieldDidBeginEditing function:
class ViewController: UIViewController, UITextFieldDelegate
{
#IBOutlet weak var textField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
textField.delegate = self;
// Do any additional setup after loading the view, typically from a nib.
}
func textFieldDidBeginEditing(textField: UITextField) {
print("TextField did begin editing method called")
}
func textFieldDidEndEditing(textField: UITextField) {
print("TextField did end editing method called")
}
}

iOS/Swift: how to detect touch action on a UITextField

I would like to detect the touch action on a UITextField.
It seems the "Touch Up Inside" action is not fired by touching inside the textfield.
It seems "Touch Up Inside" is not enabled for UITextField, but "Touch Down" works.
So the solution is as follows:
Swift 4.x
myTextField.addTarget(self, action: #selector(myTargetFunction), for: .touchDown)
#objc func myTargetFunction(textField: UITextField) {
print("myTargetFunction")
}
Swift 3.x
myTextField.addTarget(self, action: #selector(myTargetFunction), for: UIControlEvents.touchDown)
#objc func myTargetFunction(textField: UITextField) {
print("myTargetFunction")
}
Swift 4 and higher:
class ViewController: UIViewController, UITextFieldDelegate {
func textFieldDidBeginEditing(_ textField: UITextField) {
if textField == myTextField {
print("You edit myTextField")
}
}
}
This is a delegate function.
here's Swfit:
and you don't need to use the "touchUpInside" just use the delegate methods like so:
Make your View controller a delegate:
class ViewController: UIViewController, UITextFieldDelegate{
func textFieldDidBeginEditing(textField: UITextField) -> Bool {
if textField == myTextField {
return true // myTextField was touched
}
}
Here's the other delegate methods:
protocol UITextFieldDelegate : NSObjectProtocol {
optional func textFieldShouldBeginEditing(textField: UITextField) -> Bool // return NO to disallow editing.
optional func textFieldDidBeginEditing(textField: UITextField) // became first responder
optional func textFieldShouldEndEditing(textField: UITextField) -> Bool // return YES to allow editing to stop and to resign first responder status. NO to disallow the editing session to end
optional func textFieldDidEndEditing(textField: UITextField) // may be called if forced even if shouldEndEditing returns NO (e.g. view removed from window) or endEditing:YES called
optional func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool // return NO to not change text
optional func textFieldShouldClear(textField: UITextField) -> Bool // called when clear button pressed. return NO to ignore (no notifications)
optional func textFieldShouldReturn(textField: UITextField) -> Bool // called when 'return' key pressed. return NO to ignore.
}
from swift docs:
struct UIControlEvents : RawOptionSetType {
init(_ rawValue: UInt)
init(rawValue: UInt)
static var TouchDown: UIControlEvents { get } // on all touch downs
static var TouchDownRepeat: UIControlEvents { get } // on multiple touchdowns (tap count > 1)
static var TouchDragInside: UIControlEvents { get }
static var TouchDragOutside: UIControlEvents { get }
static var TouchDragEnter: UIControlEvents { get }
static var TouchDragExit: UIControlEvents { get }
static var TouchUpInside: UIControlEvents { get }
static var TouchUpOutside: UIControlEvents { get }
static var TouchCancel: UIControlEvents { get }
static var ValueChanged: UIControlEvents { get } // sliders, etc.
static var EditingDidBegin: UIControlEvents { get } // UITextField
static var EditingChanged: UIControlEvents { get }
static var EditingDidEnd: UIControlEvents { get }
static var EditingDidEndOnExit: UIControlEvents { get } // 'return key' ending editing
static var AllTouchEvents: UIControlEvents { get } // for touch events
static var AllEditingEvents: UIControlEvents { get } // for UITextField
static var ApplicationReserved: UIControlEvents { get } // range available for application use
static var SystemReserved: UIControlEvents { get } // range reserved for internal framework use
static var AllEvents: UIControlEvents { get }
}
UITextField doesn't respond to "touchUpInside" see to the right side, you'll find it's acceptable control events
Update For Swift 3
Here is the code for Swift 3:
myTextField.addTarget(self, action: #selector(myTargetFunction), for: .touchDown)
This is the function:
func myTargetFunction() {
print("It works!")
}
I referred to the UIControlEvents documentation from Apple and came up with the following:
First add UITextFieldDelegate to your class then,
textBox.delegate = self
textBox.addTarget(self, action: #selector(TextBoxOn(_:)),for: .editingDidBegin)
textBox.addTarget(self, action: #selector(TextBoxOff(_:)),for: .editingDidEnd)
with the following functions:
func TextBoxOff(_ textField: UITextField) {
code
}
}
func TextBox(_ textField: UITextField) {
code
}
}
For Swift 3.1:
1) Create a gesture recognizer:
let textViewRecognizer = UITapGestureRecognizer()
2) Add a handler to the recognizer:
textViewRecognizer.addTarget(self, action: #selector(tappedTextView(_:)))
3) Add the recognizer to your text view:
textView.addGestureRecognizer(textViewRecognizer)
4) Add the handler to your class:
func tappedTextView(_ sender: UITapGestureRecognizer) {
print("detected tap!")
}
To make this a little clearer these things need to be in place. I used this to make it so if a user entered something in an app of my own's credit textField anything in the debit textField is deleted.
UITextFieldDelegate needs to be declared in the View controller i.e class SecondViewController:
The detector functions func myDebitDetector func myCreditDetector need to be in the ViewController class.
put debit.addTarget and credit.addtarget inside view will appear.
#IBOutlet weak var debit: UITextField! and #IBOutlet weak var credit: UITextField! are textfields on the storyboard and connected to the viewController.
class SecondViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource, UITextFieldDelegate {
#IBOutlet weak var credit: UITextField!
#IBOutlet weak var debit: UITextField!
func myDebitDetector(textfield: UITextField ){
print("using debit")
credit.text = ""
}
func myCreditDetector(textfield: UITextField) {
print("using cedit")
debit.text = ""
}
override func viewWillAppear(animated: Bool) {
debit.addTarget(self, action: "myDebitDetector:", forControlEvents: UIControlEvents.TouchDown)
credit.addTarget(self, action: "myCreditDetector:", forControlEvents: UIControlEvents.TouchDown)
....
}
}
Set the UITextField delegate to your view controller
Obj-C
textField.delegate = self;
Swift
textField.delegate = self
Implement the delegate method
Obj-c
-(void)textField:(UITextField*)textField didBeginEditing {
// User touched textField
}
Swift
func textFieldDidBeginEditing(textField: UITextField!) { //delegate method
}
Swift 3.0 Version:
textFieldClientName.addTarget(self, action: Selector(("myTargetFunction:")), for: UIControlEvents.touchDown)
Swift 4.2.
Try .editingDidEnd instead of .touchDown and delegate.
myTextField.addTarget(self, action: #selector(myTargetFunction), for: .editingDidEnd)
#objc func myTargetFunction(textField: UITextField) {
print("textfield pressed")
}
On xamarin.iOS i easy to use:
YourTextField.WeakDelegate = this;
[...]
[Export("textFieldDidBeginEditing:")]
public void TextViewChanged(UITextField textField)
{
if (YourTextField.Equals(textField))
IsYourTextFileFocused = true;
}
** Swift 4.0 + **
Use custom action like this
YourTextField.addTarget(self, action: #selector(myTargetFunction(_:)), for: .allEditingEvents)
#IBAction private func myTargetFunction(_ sender: Any) {
if let textField = sender as? UITextField {
// get textField here
}
}

Swift: how to use UISwitch to control UITextField to be enabled or disabled with a delegate

In XCode 6.3.2, I have a UITextField:
#IBOutlet weak var uiswitchControlledTextField: UITextField!
I am now using a UISwitch (named mySwitch) to control its enabled or disabled state in the following way:
#IBOutlet weak var mySwitch: UISwitch!
mySwitch.addTarget(self, action: Selector("stateChanged:"), forControlEvents: UIControlEvents.ValueChanged)
//callback below:
func stateChanged(switchState: UISwitch) {
uiswitchControlledTextField.enabled = switchState.on
}
The above works well, however, I am looking to try if it would be possible to create a UITextFieldDelegate to control the above UITextField in the same way. So far, I have the following by implementing textFieldShouldBeginEditing, in which I wish to return false to disable the UITextField, but I don't know how to let the UISwitch dynamically return true or false from textFieldShouldBeginEditing
import Foundation
import UIKit
class SwitchControlledTextFieldDelegate: NSObject, UITextFieldDelegate {
func textFieldShouldBeginEditing(textField: UITextField) -> Bool {
return false; //do not show keyboard or cursor
}
}
In ViewController, I try to set
self.uiswitchControlledTextField.delegate = SwitchControlledTextFieldDelegate()
but it does not work as I wished. Any help would be appreciated.
self.uiswitchControlledTextField.delegate = SwitchControlledTextFieldDelegate()
The problem is that that line merely creates an instance of your SwitchControlledTextFieldDelegate class, which then immediately goes right back out of existence.
You need to use, as your text field delegate, some instance which already exists and which will persist - like, perhaps, your view controller!
(Xcode 7)
Use this:
override func viewDidLoad() {
super.viewDidLoad()
// Setting the delegate
self.textField3.delegate = self
self.editingSwitch.setOn(false, animated: false)
}
// Text Field Delegate Methods
func textFieldShouldBeginEditing(textField: UITextField) -> Bool {
return self.editingSwitch.on
}
#IBAction func toggleTheTextEditor(sender: AnyObject) {
if !(sender as! UISwitch).on {
self.textField3.resignFirstResponder()
}
}

Resources