is it possible to deselect a UITextfield programmatically?
I have several textfields with a pickerView as input instead of the keyboard. But when I click on a cancel button I want to deselect the current textfield.
func resignFirstResponder() -> Bool
textField.resignFirstResponder()
Notifies the receiver that it has been asked to relinquish its status
as first responder in its window.
Discussion The default implementation returns YES, resigning first
responder status. Subclasses can override this method to update state
or perform some action such as unhighlighting the selection, or to
return NO, refusing to relinquish first responder status. If you
override this method, you must call super (the superclass
implementation) at some point in your code.
Availability Available in iOS 2.0 and later.
func becomeFirstResponder() -> Bool
textField.becomeFirstResponder()
Notifies the receiver that it is about to become first responder in
its window.
Return Value YES if the receiver accepts first-responder status or NO
if it refuses this status. The default implementation returns YES,
accepting first responder status.
Discussion Subclasses can override this method to update state or
perform some action such as highlighting the selection.
A responder object only becomes the first responder if the current
responder can resign first-responder status (canResignFirstResponder)
and the new responder can become first responder.
You may call this method to make a responder object such as a view the
first responder. However, you should only call it on that view if it
is part of a view hierarchy. If the view’s window property holds a
UIWindow object, it has been installed in a view hierarchy; if it
returns nil, the view is detached from any hierarchy.
Availability Available in iOS 2.0 and later.
Related
In my macOS application I have menu items, which are replicated also in main UI. Application is consisted of main window with its delegate and single view along with its view controller. In app delegate I capture menu item click action, then I need to send this event to my view controller in order to take appropriate actions and also update main UI.
Question is how to access my view controller (NSViewController) from app delegate?
If you have window as an IBOutlet you can do
var rootViewController: MyViewController? {
return window!.contentViewController as? MyViewController
}
func sendPressed(_ sender: Any?) {
rootViewController?.sendPressed()
}
If you don‘t have a window variable you can get it through
NSApplication.shared.orderedWindows.first!
Actually you don't need a reference to the controller. There is First Responder
Declare the IBActions
In Interface Builder connect the menu items to the First Responder (red cube) of the target object and choose the appropriate action.
The action will be executed in the first object of the responder chain which implements the action.
I have a (subclassed) NSCollectionView open, containing multiple text views. Each of the text views is mapped to a (subclassed) NSDocument object. (The idea is to use the document architecture's save functions but not its windowing functions, because I need multiple documents in the same window and the traditional document architecture doesn't allow that.)
Now, there's a function I'd like the user to be able to call from the main menu that will affect their currently selected document. That is: the document is currently visible in a text view with current focus, and the menu command should make an alteration to that document. But the sender of the menu command is just the menu. When the window controller handles the command from the menu, how can I tell it what the currently selected document is?
This is what the responder chain is for.
Since you're using NSCollectionView, you probably already have a subclass of NSCollectionViewItem. If not, create one. Implement your action method in this subclass. Example:
class DocumentItem: NSCollectionViewItem {
var document: MyDocument? {
return representedObject as? MyDocument
}
#IBAction func doThatThing(sender: AnyObject?) {
Swift.print("This is where I do that thing to \(document)")
}
// #IBOutlets and whatnot here...
}
You may need to set this as the custom class of your NSCollectionViewItem in your xib or storyboard.
Next, if your cell view (the view owned by your NSCollectionViewItem) isn't a custom subclass of NSView already, you should make it a custom subclass. You must override acceptsFirstResponder to return true:
class DocumentCellView: NSView {
override var acceptsFirstResponder: Bool { return true }
// #IBOutlets and whatnot here...
}
Make sure you set this as the custom class of your cell view in your storyboard or xib.
Finally, connect the action of your menu item to doThatThing: on First Responder:
Here's how it works:
Because the cell view now returns true for acceptsFirstResponder, when the user clicks a cell view in the collection view, the system will make it the first responder (the start of the responder chain).
When a view has a view controller, it makes that view controller the next responder after itself in the responder chain (if you are on OS X 10.10 Yosemite or later). Your cell view has a view controller: the item object you return from outlineView:itemForRepresentedObjectAtIndexPath:. (NSCollectionViewItem is a subclass of NSViewController, so your custom item is a view controller.)
When the user clicks the menu item, the menu item asks NSApplication to send its action along the responder chain, starting with the first responder. The first responder is the cell view, but it doesn't respond to the doThatThing: message. So NSApplication asks the view for its nextResponder, which is an instance of your NSCollectionViewItem subclass. That object does respond to doThatThing:, so NSApplication sends doThatThing: to your item object (with the NSMenuItem object as the sender argument) and doesn't check the rest of the responder chain.
I am using the UIMenuController on a custom UIView subclass. This means it can become first responder, and claims it canPerformAction on the "delete" action.
I would also like this view's superview (also a custom UIView) to be able to use the menu controller, so on that superview, I have marked it as being able to be first responder, and implemented canPerformAction for different actions ("copy" and "cut" in this case).
Here's the thing-- when I make the menu visible from the (first) subview, it puts all three actions in the menu: delete, copy, and cut. In the debugger, I see canBecomeFirstResponder and canPerformAction being invoked on both views before the menu appears.
What's going on here? Why isn't the menu controller restricted to the view that's become first responder? Or am I not diagnosing this correctly?
Thanks.
What code are you using?
In the documentation for canPerformAction:withSender:,
This default implementation of this method returns YES if the responder class implements the requested action and calls the next responder if it does not. ... Note that if your class returns NO for a command, another responder further up the responder chain may still return YES, enabling the command.
It seems to be contradictory, saying the default implementation recurses up the responder chain, but then also that UIMenuController recurses up the responder chain if you return NO.
The easiest fudge is probably to override -nextResponder to return nil, but that might have other side-effects (for one, actions with a "nil" target go up the responder chain by default!).
Context:
I have added my custom menu item to the UIMenuController. Everything works just fine, the canPerformAction is called as expected when the custom item is tapped.
Here is my problem:
within the view where the menu is displayed I have a few textview.
I want to be able to grab the current selected text from the current textview
when the custom menu is tapped.
I can’t because the sender is not the object that is hosting/showing the menu but the menucontroller itself.
How do I find the sender/UI_control (parent?) where the menu has been shown?
The way is supposed to work is:
You add a custom menu in the usual fashion.
In the canPerformAction you filter which action you want to enable/show
When the action occurs you check the current responder and there you apply your logic.
The canPerformAction withSender never returns you the current control that is visually hosting the custom menu.
You can find out who is the first responder looping all the views and check the isFirstResponder property. Or if you have just a couple if you can do a quick check for those two.
// called by canPerformAction
if ([myTextBox isFirstResponder]) {
NSLog(#"Found it!", nil);
}
I have three textfields in interface builder being used to accept input. When a button is pushed the data in the 3 fields are processed. Currently, when one of the textfields sends out a textFieldShouldReturn (or similar) message to its delegate, I assign the "sender" parameter (using the tag property) to the delegate's respective UITextField instance variable.
I run into a problem if the button is pushed, but the user has taken no action inside the textfield, essentially leaving the delegate's UITextFields as nil. A simple work around is to just check for nil and assume that nil means an empty textfield, but I'm sure there is a more appropriate pattern within this context to achieve the same goal.
Essentially, how do I assign IB textfields to the delegate UITextField properties without the user taking some action to invoke the delegate?
If your delegate object is represented in the xib file (it's the owner or something), you could use IBOutlets to connect the textfields to delegate variables. See the Interface Builder User Guide.
If your delegate is not there, there's probably a place in your code that assigns the delegate to the textfield and hence know both of them. You could set the textfield on the delegate at the same time.