This code works and fires:
class ViewControllerDisplay: UIViewController, UITextViewDelegate {
//.....
let textView = MyUITextView(frame: CGRect(x:10, y:20, width:270, height:65))
textView.params["dataname"] = _dataname
textView.delegate = self
view.addSubview(textView)
func textViewDidChange(_ textView: UITextView) {
print("YAAAA" + textView.text);
}
however this does not fire
func textViewDidChange(_ textView: MyUITextView) {
print("YAAAA" + textView.text);
}
here is the MyUITextView code
import UIKit
class MyUITextView: UITextView{
var params: Dictionary<String, Any>
required init?(coder aDecoder: NSCoder) {
self.params = [:]
super.init(coder: aDecoder)
}
init(frame: CGRect) {
self.params = [:]
super.init(frame: frame, textContainer: nil)
}
}
so how do I extend the delegate to include MyUITextView in the delegate 'onChange'
The method textViewDidChange (textView: MyUITextView) was not called because in the protocol UITextViewDelegate that method is specified like func textViewDidChange ( textView: UITextView)
If you need to call specifically from the custom class MyUITextView, you need to write a protocol for this class, something like:
protocol MyUITextViewDelegate {
func myTextViewDidChange(_ textView: MyUITextView)
}
Add a field with type MyUITextViewDelegate? in class MyUITextView:
var myDelegate: MyUITextViewDelegate?
Initialize it for example in the viewDidLoad class of the ViewControllerDisplay class:
override func viewDidLoad() {
super.viewDidLoad()
...
textView.myDelegate = self
...
}
The class MyUITextView need to subscribe to the UITextViewDelegate protocol and implement the textViewDidChange (_ textView: UITextView) method in which our protocol is called from myDelegate if it exists:
class MyUITextView: UITextView, UITextViewDelegate {
...
func someInitMethod() {
delegate = self
}
...
func textViewDidChange(_ textView: UITextView) {
myDelegate?.myTextViewDidChange(self)
}
}
And finally, by signing the class ViewControllerDisplay on the protocol MyUITextViewDelegate and implement the necessary method:
class ViewControllerDisplay: UIViewController, MyUITextViewDelegate {
...
func textViewDidChange(_ textView: MyUITextView) {
print("YAAAA" + textView.text);
}
}
ok I figured it out by casting
func textViewDidChange(_ textView: UITextView) {
if let sender = textView as? MyUITextView {
let dataname = sender.params["dataname"] as? String ?? ""
print("MyUITextView: dataname" + dataname + " = " + sender.text!)
}
}
Related
I am new to using xib files. So I'm not very clear on how they interact with their parent.
I have a custom view (LoginView) which is created from a xib file. This view also defines a protocol (LoginDelegate). The sole purpose of the delegate is to pass the username and password back to the caller.
I also have a ViewController (LoginVC) which implements this protocol. I am adding the LoginView to this VC.
I verified that I properly set the delegate in VC.viewDidLoad(). The problem is when I try to use the delegate to invoke the protocol method: the delegate is always nil. Somehow it is getting cleared. Here is the UIView:
// MARK:- Login Delegate method
// provide a means to pass the user credentials back to the parent
protocol LoginDelegate: AnyObject {
func getUsernameAndPassword(user: String, pwd: String)
}
class LoginView: UIView, UITextViewDelegate {
#IBOutlet weak var user: UITextView!
#IBOutlet weak var password: UITextView!
#IBOutlet weak var btnLogin: UIButton!
var userPlaceholderLabel : UILabel!
var pwdPlaceholderLabel : UILabel!
var view: UIView!
weak var loginDelegate: LoginDelegate?
// MARK:- internal use only
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override init(frame: CGRect) {
super.init(frame: frame)
}
class func getInstance() -> LoginView {
let nib = UINib(nibName:"LoginView", bundle:nil)
let view = nib.instantiate(withOwner: self, options: nil).first as! LoginView
return view
}
#IBAction func onLoginButtonPress(_ sender: Any) {
print ("\(#function): Username: \(user.text ?? ""), Password: \(password.text ?? "")")
self.loginDelegate?.getUsernameAndPassword(user: user.text, pwd: password.text )
}
// MARK:- TextView Delegate methods
func textViewDidChange(_ textView: UITextView) {...}
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {...}
}
And the View Controller:
class LoginVC: UIViewController, LoginDelegate {
var isBleScan = true
#IBOutlet weak var btnToggleBleScan: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
let loginSubview = LoginView.getInstance()
loginSubview.frame = self.view.bounds
loginSubview.loginDelegate = self
view.addSubview(loginSubview)
}
#IBAction func onToggleScanPressed(_ sender: Any) {
isBleScan = !isBleScan
if (isBleScan) {
btnToggleBleScan.setTitle("Stop Scan", for: UIControl.State.normal)
} else {
btnToggleBleScan.setTitle("Start Scan", for: UIControl.State.normal)
}
}
// MARK:- LoginDelegate methods
// METHOD IS NEVER CALLED - DELEGATE IS nil IN THE CALLER
func getUsernameAndPassword(user: String, pwd: String) {
let _user = user
let _pwd = pwd
print ("\(#function):Username: \(_user), Password: \(_pwd)")
}
}
The Connections for the Main Storyboard and Child xib, respectively:
I suspect I am not wiring things properly in IB, but I'm unsure. I have found many answers regarding this problem. I have tried many proposed solutions, but nothing works. I appreciate any input!
Create
class func getInstance() -> LoginView {
let nib = UINib(nibName:"LoginView", bundle:nil)
let view = nib.instantiate(withOwner: self, options: nil).first as! LoginView
return view
}
then
let loginSubview = LoginView.getInstance()
loginSubview.frame = self.view.bounds
loginSubview.loginDelegate = self // I verified this sets the delegate to an instance of LoginVC
view.addSubview(loginSubview)
Then remove this function loadViewFromNib
current problem when you do LoginView() it creates an instance without it's layout and set the delegate for it but from your loadViewFromNib you create another instance with correct layout and add it to that view but this misses delegate assignment hence the top subview of the view you create in the vc has a nil delegate
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)
}
}
I am a newbie and want to make a custom class for a UITextView where I will be able to get the text by tapping on it within the UITextView. How to create Custom UITextView class?
Try this, I have created custom textview class and protocol that you need to confirm in your class and implement that method to get text of textview every time when you click textview.
class CustomTextView: UITextView {
weak var delegateCustomTV: CustomTextViewDelegate?
override func awakeFromNib() {
super.awakeFromNib()
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.textViewTapped))
self.addGestureRecognizer(tapGestureRecognizer)
}
#objc func textViewTapped() {
delegateCustomTV?.preparedText(text: self.text ?? "")
}
}
protocol CustomTextViewDelegate: class {
func preparedText(text: String)
}
use like i have used below,
class yourViewController: UIViewController, CustomTextViewDelegate {
#IBOutlet weak var textView: CustomTextView!
override func viewDidLoad() {
super.viewDidLoad()
textView.delegateCustomTV = self
}
func preparedText(text: String) {
// You will get your text here when you click on textview
print(text)
}
}
Try this
override func viewDidLoad() {
super.viewDidLoad()
self.textView.delegate = self
}
extension YourViewController: UITextViewDelegate
{
//MARK:- TextView Editing begins Function
func textViewDidBeginEditing(_ textView: UITextView)
{
//TextView Editing Begin Function
}
// MARK:- TextView text replaced Function
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool
{
//Character changed in textView
return true
}
// MARK:- TextView End Editing function
func textViewDidEndEditing(_ textView: UITextView)
{
//End editing of textView
}
}
if you want to get text every time you tap textview you have to add tap gesture
override func viewDidLoad() {
super.viewDidLoad()
self.textView.delegate = self
let tap = UITapGestureRecognizer(target: self, action: #selector(self.textViewTapped(_:)))
self.textView.addGestureRecognizer(tap)
}
#objc func textViewTapped(_ sender: UITapGestureRecognizer){
print(textView.text)
}
I want to write a custom text view by inheriting default UITextView. My implementation uses some of the methods from the delegate of the original component:
class CustomTextView: UITextView {
fileprivate func applyStyles() {
self.layer.cornerRadius = 5
self.layer.borderColor = .black
self.layer.borderWidth = 5
self.clipsToBounds = true
}
override func awakeFromNib() {
super.awakeFromNib()
applyStyles()
delegate = self
}
}
extension CustomTextView: UITextViewDelegate {
func textViewDidBeginEditing(_ textView: UITextView) {
print("aaaa")
}
func textViewDidEndEditing(_ textView: UITextView) {
print("bbbb")
}
}
Now I want to use this text view everywhere instead of the standard one, but in the current implementation, I lose the ability to set another delegate to the component without losing the delegate functions of my own.
I mean if I will create a component in other class like this:
let customView = CustomTextView()
customView.delegate = self
textViewDidBeginEditing and textViewDidEndEditing functions in CustomTextView will not be called. How can I get around this limitation? Thanks.
The trick is to override the delegate property, so that you can capture any value that is assigned and then call that delegate after your code is done. For this to work, your subclass will need to implement all of the UITextViewDelegate functions in order to pass the invocation on to the "real" delegate:
class CustomTextView: UITextView {
private weak var externalDelegate: UITextViewDelegate?
override var delegate: UITextViewDelegate? {
set {
self.externalDelegate = newValue
}
get {
return self.externalDelegate
}
}
fileprivate func applyStyles() {
self.layer.cornerRadius = 5
self.layer.borderColor = UIColor.black.cgColor
self.layer.borderWidth = 5
self.clipsToBounds = true
}
override init(frame: CGRect, textContainer: NSTextContainer?) {
super.init(frame: frame, textContainer: textContainer)
super.delegate = self
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
super.delegate = self
}
}
extension CustomTextView: UITextViewDelegate {
func textViewDidBeginEditing(_ textView: UITextView) {
print("aaaa")
self.externalDelegate?.textViewDidBeginEditing?(textView)
}
func textViewDidEndEditing(_ textView: UITextView) {
print("bbbb")
self.externalDelegate?.textViewDidEndEditing?(textView)
}
func textViewShouldBeginEditing(_ textView: UITextView) -> Bool {
return self.externalDelegate?.textViewShouldEndEditing?(textView) ?? true
}
func textViewShouldEndEditing(_ textView: UITextView) -> Bool {
return self.externalDelegate?.textViewShouldEndEditing?(textView) ?? true
}
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
return (self.externalDelegate?.textView?(textView, shouldChangeTextIn: range, replacementText: text)) ?? true
}
func textViewDidChange(_ textView: UITextView) {
self.externalDelegate?.textViewDidChange?(textView)
}
func textViewDidChangeSelection(_ textView: UITextView) {
self.externalDelegate?.textViewDidChangeSelection?(textView)
}
func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool {
return self.externalDelegate?.textView?(textView, shouldInteractWith: URL, in: characterRange, interaction: interaction) ?? true
}
func textView(_ textView: UITextView, shouldInteractWith textAttachment: NSTextAttachment, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool {
return self.externalDelegate?.textView?(textView, shouldInteractWith: textAttachment, in: characterRange, interaction: interaction) ?? true
}
}
Despite the reason may be unclear behind the fact that you want the callback to be triggered both inside the custom textview class and the parentVc , but you can do
class CustomTextView: UITextView , UITextViewDelegate {
weak var parentVC:VCName?
func textViewDidBeginEditing(_ textView: UITextView) {
print("aaaa")
parentVC?.textViewDidBeginEditing(self)
}
let customView = CustomTextView()
customView.parentVC = self
or do the reverse which is to make the parentVc as the delegate and call the methods of textView subclass from it
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