Text change notification for an NSTextField - cocoa

I would like to use the code from the answer to this question: How to observe the value of an NSTextField on an NSTextField in order to observe changes on the string stored in the NSTextField.
[[NSNotificationCenter defaultCenter]
addObserverForName:NSTextViewDidChangeSelectionNotification
object:self.textView
queue:[NSOperationQueue mainQueue]
usingBlock:^(NSNotification *note){
NSLog(#"Text: %#", self.textView.textStorage.string);
}];
The class used here is an NSTextView. I can't find a notification in NSTextField to use instead of NSTextViewDidChangeSelectionNotification.
Is there a notification available in NSTextField that can be used in this case ?

If you just want to detect when the value of a text field has changed, you can use the controlTextDidChange: delegate method that NSTextField inherits from NSControl.
Just connect the delegate outlet of the NSTextField in the nib file to your controller class, and implement something like this:
- (void)controlTextDidChange:(NSNotification *)notification {
NSTextField *textField = [notification object];
NSLog(#"controlTextDidChange: stringValue == %#", [textField stringValue]);
}
If you're creating the NSTextField programmatically, you can use NSTextField's setDelegate: method after creation to specify the delegate:
NSTextField *textField = [[[NSTextField alloc] initWithFrame:someRect] autorelease];
[textField setDelegate:self]; // or whatever object you want
Delegation is one of the fundamental design patterns used throughout Cocoa. Briefly, it allows you to easily customize the behavior of standard objects (in this case, user interface objects) without the complexity involved in having to subclass the object to add that additional behavior. For example, another lower-level way to detect when the text in a textfield has changed might be to create your own custom NSTextField subclass in which you override the keyDown: method that NSTextField inherits from NSResponder. However, subclassing like that is difficult because it can require that you have an intimate knowledge of the object's inheritance hierarchy. For more info, definitely check out the following:
Cocoa Fundamentals Guide: Delegates and Data Sources
Regarding what id <NSTextFieldDelegate> means: it means a generic object (id) that declares itself as conforming to the <NSTextFieldDelegate> protocol. For more info on protocols, see The Objective-C Programming Language: Protocols.
Sample GitHub project at: https://github.com/NSGod/MDControlTextDidChange

Xcode 9.2. with Swift 4.0.3.
The NSTextField must be connected via interface builder for this implementation to work.
import Cocoa
#objc public class MyWindowController: NSWindowController, NSTextFieldDelegate {
#IBOutlet weak var myTextField: NSTextField!
// MARK: - ViewController lifecycle -
override public func windowDidLoad() {
super.windowDidLoad()
myTextField.delegate = self
}
// MARK: - NSTextFieldDelegate -
public override func controlTextDidChange(_ obj: Notification) {
// check the identifier to be sure you have the correct textfield if more are used
if let textField = obj.object as? NSTextField, self.myTextField.identifier == textField.identifier {
print("\n\nMy own textField = \(self.myTextField)\nNotification textfield = \(textField)")
print("\nChanged text = \(textField.stringValue)\n")
}
}
}
Console output:
My own textField = Optional(<myApp.NSTextField 0x103f1e720>)
Notification textfield = <myApp.NSTextField: 0x103f1e720>
Changed text = asdasdasddsada

You should use NSTextFieldDelegate and implement controlTextDidChange. Test in macOS 10.14 and Swift 4.2
import Cocoa
class ViewController: NSViewController, NSTextFieldDelegate {
#IBOutlet weak var textField: NSTextField!
override func viewDidLoad() {
super.viewDidLoad()
textField.delegate = self
}
func controlTextDidChange(_ obj: Notification) {
let textField = obj.object as! NSTextField
print(textField.stringValue)
}
}

I believe you want to read up on the field editor which is essentially a (hidden) NSTextView that handles the text input to all the NSTextFields in a given window. The section on "Using Delegation and Notification With the Field Editor" should point you in the right direction.

In Swift it's
public override func controlTextDidChange(_ obj: Notification) {
}

Related

How to capture escape key in an NSWindow?

I've got a storyboard with an NSWindowController (the storyboard entry point), whose content is an NSViewController. When the user selects a menuitem in my application, I load the storyboard and display it.
Now I want the "escape" key to close the window. I've seen several questions about this, but none that matches the behavior I'm seeing.
I subclassed NSWindowController and NSViewController, and set the controllers in the storyboard to those. I've tried every method I could find or think of, including:
override func cancelOperation(_ sender: Any?)
func cancel(_ sender: Any?)
override func keyDown(with event: NSEvent)
in the window controller, the view controller, and even a custom NSWindow subclass, but none of these methods get called. I've confirmed that viewDidLoad() and windowDidLoad() do get called, so my classes are getting used. They're just not getting events.
To verify my sanity, I tried inspecting the responder chain. As expected, both my custom window controller and view controller classes are in it, as is the NSWindow subclass.
Why would an NSResponder object in the responder chain not receive events?
// monitor ESC key
NSEvent* (^handler)(NSEvent*) = ^(NSEvent *event) {
if(event.keyCode == 53) {
[self keyDown:event];
}
return event;
};
eventMonitor = [NSEvent addLocalMonitorForEventsMatchingMask:NSKeyDownMask handler:handler];
Put these code into a NSWindowController or AppDelegate class.

Drag - connect an UISwitch to an IBOutlet on a delegate class

My goal is to enable/disable the editing of a UITextField with a UISwitch using delegates. This is the delegate class:
import Foundation
import UIKit
class SwitchedTextFieldDelegate : NSObject, UITextFieldDelegate{
#IBOutlet weak var switchText : UISwitch!
func textFieldShouldBeginEditing(textField: UITextField) -> Bool {
//Here I intended to read the UISwitch state
print("Can't touch this")
return false
}
}
I've tried to drag-connect the IBOutlet to the storyboard, but it is not possible. I can do it on the main view controller, which inherits UIViewController. I've already learned that multiple inheritance is not possible in Swift. How would you solve this? I'll try using an IBAction instead.
In your main view controller drag IBOutlet of a UITextField and UISwitch. Then confirm the UITextField Delegate. Now implement the UITextField Delegate method Like this:
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
if switchText.on{
return true
}else{
return false
}
}
For better understanding I have shared my ViewController screen shot.Here FirstViewController is just like your MainViewController. Hope this will help you.

Detecting when a button is touched in SKScene?

I created a storyboard and added a scene with two buttons. I cannot figure out how to know when a button is pressed on my GameScene.swift class.
How can this be done?
You can you touchesBegan for that.
Here is example code for you:
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
for touch: AnyObject in touches{
let location = touch.locationInNode(self)
if self.nodeAtPoint(location) == self.playButton{
//your code
}
}
}
You appear to be mixing UIKit and SpriteKit here. I would personally advise against using UIButtons in conjunction with Sprite Kit. Is there a specific reason for doing so?
There are two ways you can implement button behavior within a Sprite Kit scene:
have the SKScene object handle the touches
have the button itself handle the touches
Dharmesh's answer uses method (1), where he implements the -touchesBegan method.
In my current project, I am using an SKNode subclass as a button (2). I am unfamiliar with Swift syntax so I have posted Objective-C code from my project instead. The method calls are similar though and should help illustrate the point.
If you want an SKNode to receive touches, set userInteractionEnabled to YES. Otherwise, the closest ancestor with userInteractionEnabled = YES (which typically is the containing SKScene) will receive a -touchesBegan/-touchesMoved/-touchesEnded message.
#interface VTObject : SKNode
#end
...
#implementation VTObject
- (instancetype)init {
if (self = [super init]) {
self.userInteractionEnabled = YES;
}
return self;
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
NSLog(#"button touched!");
}
#end
You should add the UIButton programatically, instead of in IB, to the SKScene's SKView (in didMoveToView for example). You can then set the target for the button with button.addTarget:action:forControlEvents:. Just remember to call button.removeFromSuperview() in willMoveFromView otherwise you'll see the buttons in your next scene.

Intercept Keydown Actions in an NSTextFieldCell

I have a cell-based NSOutlineView which displays NSTextFieldCell objects.
I'd like to respond to keydown or keyup events so as to make the text contained in the NSTextFieldCell bold when the text contains certain preset keywords. What is the most elegant way to achieve this - should I:
Subclass NSOutlineView and override the keydown method
Subclass NSTextFieldCell
Utilize a delegate of some kind
Utilize some other approach
Thanks very much in advance to all for any info!
Found it.
In awakeFromNib:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(actionToTakeOnKeyPress:) name:NSControlTextDidChangeNotification object:theNSOutlineViewThatContainsTheNSTextFieldCell];
Then add a method like this:
- (void) actionToTakeOnKeyPress: (id) sender
{
//will be called whenever contents of NSTextFieldCell change
}
To intercept key presses in a way that they can still be filtered out, various NSResponder messages may be overwritten, such as keyDown: or interpretKeyEvents:.
To be able to do that, a subclass of a NSTextView needs to be used as the field editor. For that, one subclasses NSTextFieldCell and overrides fieldEditorForView:, returning the subclass (see Custom field editor for NSTextFieldCell in an NSTableView).
Here's the relevant code excerpts:
In a subclassed NSTextFieldCell (which then has to be assigned in Interface Builder for the editable column, or returned by the NSTableViewDelegate's dataCellForTableColumn message):
- (NSTextView *)fieldEditorForView:(NSView *)aControlView
{
if (!self.myFieldEditor) {
self.myFieldEditor = [[MyTextView alloc] init];
self.myFieldEditor.fieldEditor = YES;
}
return self.myFieldEditor;
}
It also requires the declaration of a property in the #interface section:
#property (strong) MyTextView *myFieldEditor;
And then in MyTextView, which is a subclass of NSTextView:
-(void)keyDown:(NSEvent *)theEvent
{
NSLog(#"MyTextView keyDown: %#", theEvent.characters);
static bool b = true;
if (b) { // this silly example only lets every other keypress through.
[super keyDown:theEvent];
}
b = !b;
}

NSTextView end editing action like NSTextField

How can I detect an end editing action on a NSTextView, just like on NSTextField ? I can not see it as an action or in its delegate.
You can register for notifications, such as NSTextDidEndEditingNotification.
If you want to use the delegate pattern, then you should check the NSTextDelegate protocol. Docs here. The method sent on end editing is textDidEndEditing:.
NSTextView is a subclass of NSText, so it is a good idea to check the docs for that class, too.
Example
NSTextView has a NSTextViewDelegate property you can use to be notified about changes. The delegate methods are mere convenience methods to obtain the "end editing" notification, unlike control:textShouldEndEditing you may know from NSTextField, for example.
class SomeViewController: NSViewController, NSTextViewDelegate {
var textView: NSTextView!
func loadView() {
super.loadView()
textView.delegate = self
}
func textDidBeginEditing(notification: NSNotification) {
guard let editor = notification.object as? NSTextView else { return }
// ...
}
func textDidEndEditing(notification: NSNotification) {
guard let editor = notification.object as? NSTextView else { return }
// ...
}
}

Resources