Mac NSTextField won't resign firstResponder - nstextfield

I have a window with some NSTextFields. When I click in one and edit the value and press return, I want the focus to go back to what it was before. I don't want the blue ring around the text field and I don't want further keystrokes going to that text field. I would have thought this would happen automatically.
I tried these, and none of them work
sender.resignFirstResponder()
sender.window?.makeFirstResponder(nil)
InspectorWindowController.window?.makeFirstResponder(nil)
AnotherWindowController.window?.becomeFirstResponder()
I'm doing these at the end of my IBAction associated with the text field. Maybe I have to do it from somewhere else?
Thanks

I figured this out. I guess the sent action is happening on another thread. So you have to call makeFirstResponder using Dispatch async.
DispatchQueue.main.async { //omg
sender.window?.makeFirstResponder(nil)
}

I needed to dismiss first responder in my SwiftUI macOS app and here what I found working in a way I need:
func controlTextDidEndEditing(_ obj: Notification) {
DispatchQueue.main.async {
guard let window = self.textField.window else {
return
}
// https://stackoverflow.com/questions/5999148/how-to-determine-whether-an-nssearchfield-nstextfield-has-input-focus
// We need to make sure that our text field is still first responder.
guard let textView = window.firstResponder as? NSTextView,
textView.delegate === self.textField else {
return
}
window.makeFirstResponder(nil)
}
}

Related

How to create a action for UIReturnKeyType.Done in UITextView in xamarin iOS

I am trying to change the UITextView keyboard return button in to Done button.
I need to close the keyboard when I press the Done button.
I have create a UITextView and change the return button in to Done button.
This is my code
PhotoTitle = new UITextView
{
TranslatesAutoresizingMaskIntoConstraints = false, Editable = true, AccessibilityIdentifier = "PhotoTitle",
ReturnKeyType = UIReturnKeyType.Done
}
The keyboard showing Done button successfully. if I press it just behave like return button.
And I can not find a particuler event for fire when tap on Done button.
First of all you have to keep remember that, UITexiView is a scrollable UI widget. So, when you change the keyboard's default Return key in to Done button, you are hiding the opportunity of go to new line inside your UITextView. Therefore my suggestion is, if you are using a UITextView and you need a Done button by keeping the default keyboard, you should add a UIToolBar on top of the keyboard where you can place the Done button.
Since I'm not aware of your real need, which may do not need go to a new line and just need to exit the keyboard when user tap on the Return/Done button, here how you need to do it.
public override void ViewDidLoad()
{
base.ViewDidLoad();
TextView.WeakDelegate = this;
}
[Export("textView:shouldChangeTextInRange:replacementText:")]
public bool ShouldChangeText(UITextView textView, NSRange range, string text)
{
if (text.Equals("\n"))
{
TextView.ResignFirstResponder();
return false;
}
return true;
}
That is quite easy actually subscribe to the ShouldReturn event of the UITextField with a delegate or anonymous method that will call ResignFirstResponder on the field.
You can check this example project that shows how to do it
this.txtDefault.ShouldReturn += (textField) => {
textField.ResignFirstResponder();
return true;
};

Dismissing keyboard in UITextField with RAC(5)?

Newbie to ReactiveCocoa and ReactiveSwfit here... Sorry if the answer is obvious.
I am trying to adapt the Start Developing iOS Apps with Swift sample to ReactiveSwift / ReactiveCocoa, and I am running into an issue with "translating" the UITextField's Delegate method -- which gets rid of the keyboard and essentially ends the editing (so I can capture the text field in the mealNameLabel) :
func textFieldShouldReturn(_ textField: UITextField) -> Bool
I am using
nameTextField.reactive.textValues.observeValues { value in
viewModel.mealName.swap(value ?? "")
}
// Setup bindings to update the view's meal label
// based on data from the View Model
mealNameLabel.reactive.text <~ viewModel.mealLabel
to get the value from the text field into the view model and percolate the view model's label back to the UILabel (convoluted...)
That works fine, as long as I maintain the viewController as the UITextField's delegate and I still implement the method depicted in the tutorial and mentioned above. Essentially :
override func viewDidLoad() {
super.viewDidLoad()
nameTextField.delegate = self
// view controller logic
...
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
// Hide the keyboard.
textField.resignFirstResponder()
return true
}
I tried using
nameTextField.reactive.controlEvents
but that failed miserably due to my lack of understanding of controlEvents (docs anywhere ?).
So what do I need to do to make the keyboard disappear when the user is done editing, the "reactive way" ?
Thanks !!!
(Of course right after I post my question...)
It looks like this might actually do the trick :
nameTextField.reactive.controlEvents(UIControlEvents.primaryActionTriggered)
.observeValues { textField in
textField.resignFirstResponder()
}
After fiddling with the different event types, it looks like .primaryActionTriggered is what gets triggered when the "Done" button is pressed.
Any better way to do this ?

How to close window (NSWindowController) by hitting the ESC key?

Issue
I would like the user being able to close a window by hitting the ESC key but I can't get it to work in this specific case, hitting ESC triggers an error sound (the "no you can't do that" macOS bloop) and nothing happens.
Context
I'm making a subclass of NSWindowController which itself creates an instance of a subclass of NSViewController and sets it in a view. Both controllers have their own xib file.
NSWindowController:
final class MyWindowController: NSWindowController, NSWindowDelegate {
#IBOutlet weak var targetView: MainView!
let myVC: MyViewController!
var params: SomeParams!
override func windowDidLoad() {
super.windowDidLoad()
myVC = MyViewController(someParams: params)
myVC.view.setFrameSize(targetView.frame.size)
myVC.view.setBoundsSize(targetView.bounds.size)
targetView.addSubview(myVC.view)
}
override var windowNibName: String! {
return "MyWindowController"
}
convenience init(someParams params: SomeType) {
self.init(window: nil)
self.params = params
}
}
NSViewController:
final class MyViewController: NSViewController {
convenience init(someParams params: SomeType) {
// do stuff with the params
}
override func viewDidLoad() {
super.viewDidLoad()
// configure stuff for the window
}
}
What I've tried
I suppose that my issue is that the MyWindowController NSWindow is the .initialFirstResponder when I would want the content of the targetView (an NSTableView) to be the first responder - this way I could use keyDown, I guess, and send the close command to the window from there. This doesn't seem optimal, though.
I've tried forcing the view controller views into being the first responder by using window?.makeFirstResponder(theView) in the windowDidLoad of MyWindowController but nothing ever changes.
I've also tried adding this to MyWindowController:
override func cancelOperation(_ sender: Any?) {
print("yeah, let's close!")
}
But this only works if the user clicks first on the background of the window then hits ESC, and it still emits the error sound anyway. Which is actually what made me think that the issue was about the first responder being on the window.
Question
How would you achieve that? Of course, I know that the user can already close the window with CMD+W, but I'd really like to sort out this issue nonetheless.
Note that the code example is in Swift but I can also accept explanations using Objective-C.
The documentation of cancelOperation explains how cancelOperation should work:
This method is bound to the Escape and Command-. (period) keys. The key window first searches the view hierarchy for a view whose key equivalent is Escape or Command-., whichever was entered. If none of these views handles the key equivalent, the window sends a default action message of cancelOperation: to the first responder and from there the message travels up the responder chain.
If no responder in the responder chain implements cancelOperation:, the key window searches the view hierarchy for a view whose key equivalent is Escape (note that this may be redundant if the original key equivalent was Escape). If no such responder is found, then a cancel: action message is sent to the first responder in the responder chain that implements it.
NSResponder declares but does not implement this method.
NSWindow implements cancelOperation: and the next responder, the window controller, isn't checked for an implementation of cancelOperation:. The cancel: message does arrive at the window controller. Implementing
- (void)cancel:(id)sender
{
NSLog(#"cancel");
}
will work. The cancel: message isn't inherited from a superclass so autocompletion doesn't suggest it.
This worked for me in Xcode 10 and Swift 4.2:
#objc func cancel(_ sender: Any?) {
close()
}
I tried it before but without the #objc part and it didn't work. So don't omit it.
When I needed such behavior I implemented it by overriding keyDown: of the NSWindow object.
I.e. something like the following:
- (void)keyDown:(NSEvent *)theEvent
{
int k = [theEvent keyCode];
if (k == kVK_Escape)
{
[self close];
return;
}
[super keyDown:theEvent];
}

How do I access the undoManager for my Mac OS app in swift?

I am simply trying to get undo working for the actions a user performs in my app. By default, any text editing the user does has the benefit of undo, but any actions that are done otherwise (from my code) does not.
I can see the documentation explains that I need to get an instance of NSUndoManager and call registerUndoWithTarget, but I am stumped with the first step: getting the undoManager from within my ViewController. Since ViewController is a UIResponder, I tried this:
if let undoManager = self.undoManager {
undoManager.registerUndoWithTarget(self, selector: Selector("removeLatestEntry:"), object: "test")
}
Since that binding returns nil, I thought maybe the ViewController doesn't have the undoManager, so I looked for it in the window:
if let window = NSApplication.sharedApplication().mainWindow {
if let undoManager = window.undoManager {
undoManager.registerUndoWithTarget(self, selector: Selector("removeLatestEntry:"), object: "test")
}
}
Alas, the window binding also returns nil. Sorry, I am very new to this. Can anyone point me in the right direction? Am I supposed to implement my own undoManager or something? There is clearly an undoManager somewhere because anything a user does manually in my textField is getting undo behavior. It seems like this would be a singleton that I could access easily from a ViewController.
--
Edit: BTW, the code above was placed in viewDidLoad and removeLatestEntry is just a function in my ViewController that takes a string and prints it at this point.
To use undoManager, the ViewController needs to be first responder. So:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
becomeFirstResponder()
}
Then, from wherever your action is defined that needs to be reversed, you register your undo and pass it whatever it needs to undo that action. So in my case:
func addEntry(activity: String) {
// some other stuff…
undoManager!.registerUndoWithTarget(self, selector: Selector("removeLatestEntry:"), object: activity)
}

I need assistance with xcode buttons

So I'm trying to make an event happen with buttons in xcode, and it's working fine, and executing the way I want it to, but now I want a different section of code to run if the button is not being pushed. I don't know how to test for if a button is NOT being pushed. Any ideas?
While a NSButton is pressed its state is non-zero.
if (myButton.state) {
// My button is pushed!
}
Here's something that can help you. Define a var ,notPushed whose value can be checked to see if button is called or not.
var notPushed:Bool=true
#IBAction func buttonPressed(sender:UIButton)
{
//....
notPushed=false
}
To check wether the button is pressed :
if notPushed==true{
//...
} else {
//...
}
Link your button to the buttonPressed.

Resources