Detect if custom UITextField has a text value - swift

I created a custom text field, a class that extends a UITextField, how can I know if that UITextField has a value, I already tried the shouldChangeCharactersIn but nothing happened.
This is my code for my custom UITextField Class
#IBDesignable class CustomTextField: UITextField {
override init(frame: CGRect) {
super.init(frame: frame)
loadContent()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
loadContent()
}
...
}
I want to print "the textfield is blank" if the UITextField is blank or if the user removed the value in it, and "the textfield has content" if there's a value in it or if the user add a value in it.

You just need to add a target to your text field for UIControl.Event .editingChanged
override func willMove(toSuperview newSuperview: UIView?) {
addTarget(self, action: #selector(editingChanged), for: .editingChanged)
}
And use UIKeyInput method hasText to check if your field is empty or not:
#objc func editingChanged(_ textField: UITextField) {
print("the textfield " + (hasText ? "has content" : "is blank"))
}

Related

How to detect which NSTextField is clicked in Cocoa?

I have two NSTextField objects which I want to highlight when user clicks on it.
The initial text field is already highlighted on NSWindow load. I am able to get mouse down event for text field click, but unable to distinguish which textfield did the user tapped.
I tried using hitTest on the text field using the NSPoint obtained from the NSEvent object, but the NSView returned is nil. The view it returns is that of the window's view and not that text field.
class SettingsViewController: NSViewController {
private var sview: SettingsView?
override func viewDidLoad() {
initEvents()
}
override func loadView() {
if let settingsView = SettingsView.createFromNib() {
self.view = settingsView
self.sview = settingsView as? SettingsView
}
}
func initEvents() {
self.sview!.emailTextField.delegate = self
}
}
extension SettingsViewController: NSTextFieldDelegate, NSTextDelegate {
override func mouseDown(with event: NSEvent) {
self.log.debug("mouse down: \(event.buttonNumber), \(event.eventNumber), \(event.locationInWindow)")
// How to know which text field triggered this?
}
func control(_ control: NSControl, textView: NSTextView, doCommandBy commandSelector: Selector) -> Bool {
self.log.debug("control delegate")
return false
}
func textField(_ textField: NSTextField, textView: NSTextView, shouldSelectCandidateAt index: Int) -> Bool {
self.log.debug("text field should select")
return true
}
func textShouldBeginEditing(_ textObject: NSText) -> Bool {
self.log.debug("text field should being editing")
return true
}
}
class SettingsView: NSView {
private let log = Logger()
private static var topLevelObjects: NSArray?
#IBOutlet weak var emailTextField: ASTextField!
#IBOutlet weak var passwordTextField: NSSecureTextField!
// ...
}
I am adding delegate to only one text field.
self.sview!.emailTextField.delegate = self
But when I click on the passwordTextField, I am getting the mouse click event as well. Why is this happening?
How to distinguish NSTextField mouse click and highlight the text field?
I tried subclassing NSTextField and adding click handler, but it is not working.
class ASTextField: NSTextField {
private let log = Logger()
required init?(coder: NSCoder) {
super.init(coder: coder)
bootstrap()
}
override init(frame frameRect: NSRect) {
super.init(frame: frameRect)
bootstrap()
}
override func awakeFromNib() {
super.awakeFromNib()
bootstrap()
}
func bootstrap() {
self.delegate = self
}
}
extension ASTextField: NSTextFieldDelegate {
override func mouseDown(with event: NSEvent) {
// This is not working
self.log.debug("mouse down")
super.mouseDown(with: event)
}
}
If what you're looking for is to be able to select the text when you click (focus) the text field, you can override the class to simplify your task and you won't have to worry about locating the clicked field from the delegate.
For an NSView object, when it gets focus (ie. clicking or tabbing) it will call becomeFirstResponder so we can hook in there.
When an NSTextField becomes editable (or selectable) it grabs a reusable 'field editor' and overlays it on top of your text field during the editing. If your NSTextField has focus, you can grab this field editor using the currentEditor() call on the view.
So, once you have the field editor, you can perform selectAll on the editor to select the text.
Example class :-
class AutoselectOnFocusTextField: NSTextField {
override func becomeFirstResponder() -> Bool {
guard super.becomeFirstResponder() else {
return false
}
if let editor = self.currentEditor() {
editor.perform(#selector(selectAll(_:)), with: self, afterDelay: 0)
}
return true
}
}
Hope this helps!
I updated the ASTextField as below.
class ASTextField: NSTextField {
// ...
override func mouseDown(with event: NSEvent) {
self.sendAction(#selector(didClick(_:)), to: self)
super.mouseDown(with: event)
}
#objc func didClick(_ event: NSEvent) {
self.log.debug("did click")
}
}
In the SettingsView, I missed calling super.layout(), without which the click won't work, nor the other text field will get focus when clicked.
class SettingsView: NSView {
// ...
override func layout() {
self.log.debug("layout method")
super.layout() // This is important
}
}
NSTextField delegate methods are not required.
The method you overwrote, mouseDown(with), isn't a member of the NSTextFieldDelegate or NSTextDelegate protocols. You overwrote NSViewController.mouseDown(with).
Whenever that method is called, the thing that was clicked is your SettingsViewController's view.
To react to your textfield being selected, you use NSTextFieldDelegate .textField(_:textView:shouldSelectCandidateAt:), which you already have. The value of the textView parameter is the text view that was selected.

How to Observer UITexFiled startEditing and EndEditing in its SubClass?

How to observer in UITexFiled sub class ,if current textField is started Ending or Ended Editing in same class .
if I'll write self.delegate = self then my ViewController UITextField method will not called .
I want to make a UITexFiled SubClass when every editing is started then I'll scale up the textFiled ,and when editing is done then UITexField will be back to normal size . but I want to handle from my subclass not to write logic every UITexFeildDelegate in all ViewController Please help me .
You can add targets for the specific events
class MyTextField: UITextField {
override init(frame: CGRect) {
super.init(frame:frame)
commonInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
func commonInit() {
self.addTarget(self, action: #selector(didBegin), for: .editingDidBegin)
self.addTarget(self, action: #selector(didEnd), for: .editingDidEnd)
}
#objc func didBegin() {
}
#objc func didEnd() {
}
}

How to disable user interaction of NSTextView?

In Cocoa Touch, you can disable user interaction of a text view like this:
let textView = UITextView()
textView.isUserInteractionEnabled = false
But in Cocoa, NSTextView has no such easy API to use.
So what's the proper way to disable NSTextView user interaction?
You can disable its' editable by:
textView.isEditable = false
And its' selectable by:
textView.isSelectable = false
However, when you disable both of them. This textView still will intercept Mouse-down event, which is different from isUserInteractionEnabled .
So, for now, to enable click-through, my solution is subclassing a NSTextView and override its' hitTest:
class TextView: NSTextView {
override func hitTest(_ point: NSPoint) -> NSView? {
return nil
}
}
An encapsulated way is here
class StaticTextView: NSTextView {
override init(frame frameRect: NSRect) {
super.init(frame: frameRect)
}
override init(frame frameRect: NSRect, textContainer container: NSTextContainer?) {
super.init(frame: frameRect, textContainer: container)
}
required init?(coder: NSCoder) {
super.init(coder: coder)
}
override func hitTest(_ point: NSPoint) -> NSView? {
return nil
}
}

Swift: NSPopUpButtonCell in NSTableHeaderCell

I cannot for the life of my figure out how to get an NSPopUpButtonCell to work in a NSTableHeaderCell. I've tried to implement the solution here: Getting duplicate header button cell in NSTableView when using NSPopUpButtonCell but it seems some of the methods there aren't available in Swift, specifically performClickWithFrame on the headerCell.
I've managed to get the popup button to draw in the header like this:
class DropDownHeaderCell: NSTableHeaderCell {
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override init(textCell aString: String) {
super.init(textCell: aString)
}
override func drawWithFrame(cellFrame: NSRect, inView controlView: NSView) {
let buttonCell = NSPopUpButtonCell(textCell: "", pullsDown: true)
buttonCell.addItemsWithTitles(["Item1", "Item2", "Item3"])
buttonCell.drawWithFrame(cellFrame, inView: controlView)
}
}
Whenever I try to click on the button it selects the header cell instead. How do I get it so that my click register on the button and not the header?

How to capture the text from a UITextField swift 2.0

I am trying to simply capture the string typed into a UITextField in a GameController class that supports a MainViewController. My code below doesn't capture it into the wordAttempt string. Maybe its something to do with the textfield delegate which I am not sure how to set... Any help very much appreciated!
Class MainViewController: UIViewController {
required init?(coder aDecoder: NSCoder) {
controller = GameController()
super.init(coder: aDecoder)
}
override func viewDidLoad() {
super.viewDidLoad()
let gameView = UIView(frame: CGRectMake(0, 0, ScreenWidth, ScreenHeight))
self.view.addSubview(gameView)
controller.gameView = gameView
}
and then
class GameController: NSObject, UITextFieldDelegate {
var gameView: UIView!
var writeText: UITextField!
self.writeText = UITextField(frame: CGRectMake(100,100,200,50))
writeText.delegate = self
writeText.becomeFirstResponder()
gameView.addSubView(writeText)
textFieldShouldReturn(writeText)
textFieldDidEndEditing(writeText)
}
func textFieldShouldReturn(textField: UITextField) -> Bool {
writeText.resignFirstResponder()
textFieldDidEndEditing(writeText)
return true
}
func textFieldDidEndEditing(textField: UITextField) {
self.wordAttempt = writeText.text ?? ""
writeText.resignFirstResponder()
}
If you want get informed anytime user enter some characters to your UITextField you should subscribe to it changes.
// inside your GameController
self.writeText = UITextField(frame: CGRectMake(100,100,200,50))
writeText.addTarget(self, action: "textFieldDidChange:", forControlEvents: UIControlEvents.EditingChanged)
writeText.becomeFirstResponder()
...
func textFieldDidChange(textField: UITextField) {
let inputText = textField.text ?? ""
}
And you will get text that user type in intpuText variable. Because of optional type you should unwrap it before use. And ?? "" means "if it nil put empty string to intputText, otherwise - text". This is why you see Optional("") when you output text.
If you need to know only when user stop typing consider textFieldDidEndEditing(_:) method of UITextFieldDelegate.
class GameController: NSObject, UITextFieldDeleage {
...
self.writeText = UITextField(frame: CGRectMake(100,100,200,50))
writeText.delegate = self
writeText.becomeFirstResponder()
...
func textFieldDidEndEditing(textField: UITextField) {
let inputText = textField.text ?? ""
}
You need to have it detect that the text has changed. You could do this say with a separate button press. Or you could use the textField delegate function textFieldDidEndEditing and textFieldDidBeginEditing to detect when the text has changed. Note that you would have to set the textfield's delegate.