Swift 4.0 iOS 11.x
I have created a simple text field class, that uses the UITextFieldDelegate. I wanted to add to it an additional protocol that I could use to pass on the fact that the text entry to said field completed. A Delegate chain, since once I have picked up the fact that text entry has exited in the custom class I cannot pass it down to the VC in which the UITextField class is within it seems.
import UIKit
protocol ExitedFieldDelegate {
func exited(info: String)
}
class IDText: UITextField, UITextFieldDelegate {
internal var zeus: ExitedFieldDelegate? = nil
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)!
delegate = self
}
required override init(frame: CGRect) {
super.init(frame: frame)
delegate = self
}
func textFieldDidBeginEditing(_ textField: UITextField) {
self.textColor = UIColor.black
}
func textFieldDidEndEditing(_ textField: UITextField, reason: UITextFieldDidEndEditingReason) {
if (delegate != nil) {
let info = self.text
zeus?.exited(info: info!)
}
}
}
I added this code to the viewController I wanted to use my custom class within.
class ConfigViewController: UIViewController, ExitedFieldDelegate
And of course the method required by the protocol
func exited(info: String) {
print("The brain has left the room")
}
And I made it a delegate of said protocol so I got this in effect
var blah = IDText()
blah.delegate = self
But well it doesn't work. Am I attempting the impossible here, should I simply use default notifications instead? or indeed something else?
By setting:
blah.delegate = self
You are overwriting setting the delegate to self in the initializers.
What you want is to rewrite:
internal var zeus: ExitedFieldDelegate? = nil
to:
weak var zeus: ExitedFieldDelegate?
To be able to use weak (you want that to prevent retain cycle), update protocol definition to:
protocol ExitedFieldDelegate: class {
func exited(info: String)
}
And then change this:
var blah = IDText()
blah.delegate = self
to:
var blah = IDText()
// you want to set zeus instead of the delegate field
blah.zeus = self
Related
I want all of TextField to have small letters.and if I need to change to a larger one I could write inside the creation.But my code doesn't work, what am I doing wrong?
class ViewController: UIViewController
private let TextField: CustomTextField = { //LETTERS (no work)
let textfield = CustomTextField()
textfield.autocapitalizationType = .allCharacters
return textfield
}()
private let TextField2: CustomTextField = { //letters (work)
let textfield = CustomTextField()
return textfield
}()
class CustomTextField: UITextField
override func layoutSubviews() {
super.layoutSubviews()
self.autocapitalizationType = .none
}
layoutSubviews() function is called after the initialization of your textFields. If you debug your code, you will see that TextField.autocapitalizationType will be set to .allCharacters. But after initialization of your object, layoutSubviews() will be called and autocapitalizationType will be set to .none. So you need to define the self.autocapitalizationType = .none inside init of your CustomTextField.
First, remove the override of layoutSubviews() - as mentioned by Muhammed's answer, setting the autocapitalizationType here will overwrite your custom value as soon as layoutSubviews() is called.
Then, if you give your CustomTextField a custom initializer with a parameter for the capitalization type like this:
init(autocapitalizationType: UITextAutocapitalizationType = .none) {
super.init(frame: .zero)
self.autocapitalizationType = autocapitalizationType
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
Then you can just initialize it all in one step like this, no closure required:
private let textField = CustomTextField(autocapitalizationType: .allCharacters)
If you don't want to set a type then you can leave the CustomTextField() parentheses empty since the initializer adds .none as a default.
So this might be basic Swift knowledge but I'm struggling to find this info anywhere online. I'm trying to create a custom text field glass with global styles that utilizes UITextFieldDelegate methods. In particular I'm trying to use the function textFieldDidBeginEditing(). In my example below I'm just trying to print "hello" when a text field is being edited, however nothing is being printed to the console when I begin typing in my custom text field.
If I'm doing anything incorrectly please let me know, as I don't work with Swift too much. Thank you!
class LoFMTextField: UITextField {
override init(frame: CGRect) {
super.init(frame: frame)
styleTextField()
}
required init?(coder LoFMDecoder: NSCoder) {
super.init(coder: LoFMDecoder)
styleTextField()
}
func styleTextField() {
layer.cornerRadius = 5.0
layer.borderWidth = 1.0
layer.borderColor = UIColor.white.cgColor
backgroundColor = UIColor.white
}
}
extension UITextFieldDelegate {
func textFieldDidBeginEditing(textField: UITextField!) {
print("hello")
}
}
Add this line inside styleTextField
self.delegate = self
Then
extension LoFMTextField : UITextFieldDelegate {
func textFieldDidBeginEditing(_ textField: UITextField) {
print("hello")
}
}
Note: it's (_ textField: UITextField) not (textField: UITextField!)
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)
}
}
How to create a Custom delegate with a delegate method for a custom View with its(custom View's) object as arguement in that delegate method just like tableView's cellForRowAtIndexPath: method?
Elaboration of the question with the exact problem:
Till now I have always used the delegate methods of different views provided in UIKit to construct views in my application. But this time I need to make a custom View but with the custom delegate and data source. I already made it. But a question always lifts. That is, whenever I need multiple same custom Views in my ViewController, then how will I recognise them seperately in my delegate methods. Definitely I need to pass the custom View's object in my delegate method as arguement. For eg.
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath;
In above delegate method, tableView object is an argument using which we can recognise the tableView if there are multiple tableViews on one ViewController and this View Controller is set as the delegate of all these tableViews.
This is the real scenario:
//
// ViewController.swift
// DemoCustomViewDelegate
//
// Created by Shubham Ojha on 4/3/16.
// Copyright © 2016 Shubham Ojha. All rights reserved.
//
import UIKit
protocol MyCustomViewDelegate {
func myCustomView(myCustomView: MyCustomView, didSomethingWithAString aString: String)
}
class MyCustomView: UIView {
var delegate: AnyObject?
init(frame: CGRect, andViewId viewId: String, andDelegate delegate: AnyObject) {
super.init(frame: frame)
self.delegate = delegate
self.doSomethingWithAString(viewId)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)!
}
func doSomethingWithAString(aString: String) {
// do something with a string
delegate!.myCustomView(self, didSomethingWithAString: aString)
}
}
class ViewController: UIViewController {
var myView: MyCustomView!
override func viewDidLoad() {
super.viewDidLoad()
myView = MyCustomView.init(frame: CGRectMake(0, 0, 50, 50), andViewId: "100", andDelegate: self)
//set the delegate
myView.delegate = self
}
func myCustomView(myCustomView: MyCustomView, didSomethingWithAString aString: String) {
//this delegate method is called but the object of myCustomView I need could not get
if(myCustomView.isEqual(myView)){
print("Got the object")//**Does not prints, this is what I actually need to print...**
}
}
}
You can copy-paste the code in your ViewController and evaluate the problem.
Answers in swift are also welcome!
is this what you mean?
protocol MyCustomViewDelegate {
func myCustomView(myCustomView: MyCustomView, didSomethingWithAString aString: String)
}
class MyCustomView: UIView {
var delegate: MyCustomViewDelegate?
func doSomethingWithAString(aString: String) {
// do something with a string
// inform the delegate
delegate?.myCustomView(self, didSomethingWithAString: aString)
}
}
The code you posted is correct.
When in your ViewController you do something like
let myView = MyCustomView()
myView.delegate = self
You're telling to the MyCustomView object to call self as delegate.
So, when MyCustomView executes this code
delegate!.myCustomView(self, didSomethingWithAString: aString)
It calls this in your code
func myCustomView(myCustomView: MyCustomView, didSomethingWithAString aString: String) {
//this delegate method is called but the object of myCustomView I need could not get
if(myCustomView.isEqual(myView)){
print("Got the object")//**Does not prints, this is what I actually need to print...**
}
}
where myCustomView is equal to myView. May you should compare them by using the == operator instead of isEqual.
You can look up extensions like:
extension ViewController: UITableViewDelegate {
// You extension to the UITableViewDelegate class
}
protocol MyCustomViewDelegate {
func myCustomView(myCustomView: MyCustomView, didSomethingWithAString aString: String)
}
class MyCustomView: UIView {
var delegate: MyCustomViewDelegate?
func doSomethingWithAString(aString: String) {
// do something with a string
if let delegate = delegate {
delegate.myCustomView(self, didSomethingWithAString: aString)
}
}
}
class ViewController: UIVIewController {
var myView: MyCustomView!
override func viewDidLoad() {
super.viewDidLoad()
myView = MyCustomView()
//set the delegate
myView.delegate = self
}
}
//implement the delegate
extension ViewController: MyCustomViewDelegate {
func myCustomView(myCustomView: MyCustomView, didSomethingWithAString aString: String) {
//get the string from delegation
}
}
So I'm trying to separate out my delegate into a separate class instead of housing it in my cell class. Here's how I declare and initialize the delegate:
class RegistrationTableViewController: BaseTableViewController, UITextFieldDelegate {
var textFieldDelegate: UITextFieldDelegate
//MARK: - Initialization
required init?(coder aDecoder: NSCoder) {
textFieldDelegate = RegistrationTextFieldDelegate()
super.init(coder: aDecoder)
}
Then I set it in my general cell setup method:
func setupBaseCellWithType(type: Constants.Registration.CellType) -> BaseCell {
var cell: BaseCell? = nil
if let newCell = tableView.dequeueReusableCellWithIdentifier(Constants.Registration.profileBaseRegistrationCell) as? BaseProfileCell {
newCell.cellType = type
newCell.setupCell()
newCell.setTextFieldDelegate(textFieldDelegate)
cell = newCell
}
return cell!
}
The problem is, it never hits any of the delegate methods. It was working great when I made the cells my delegate, any idea whats going wrong here?
Edit: setTextFieldDelegate calls this method:
private func setTextFieldDelegates(delegate: UITextFieldDelegate) -> Void {
for textField in textFieldArray {
textField.delegate = delegate
}
}
That was how I set the delegate on all my textFields in the cell previously, so I know it's correctly setting them there, but something is going wrong in me trying to move it to another class.
Edit 2: I'm an idiot who's running on low sleep. I was only changing the BaseCell delegate, not my PhoneNumberCellDelegate... Feel free to call me an idiot if you'd like.