I would like to disable the Application "menu highlight" that happens when you press a shortcut key assigned to an NSMenuItem that belongs to the specific menu in question.
The issue is that in the application you use the keyboard quite a bit and having the menus becoming highlighted all the time becomes a bit annoying but I still want to have the menus (including the shortcuts) there as it shows the user which actions that can be used.
Declare a custom NSMenuItem subclass and start using that custom class instead of NSMenuItem.
In this class you should override this method:
- (BOOL)isHighlighted
{
return NO;
}
This way you will not have the menu item highlighted.
EDIT
Try this:
[item setOnStateImage: item.offStateImage];
FFR: Look up the following methods in the docs:
- (BOOL)validateMenuItem:(NSMenuItem *)menuItem
Will work for both selecting the menu item and the associated command key.
Within your NSDocument provide a body for validateMenuItem
such as,
- (BOOL)validateMenuItem:(NSMenuItem *)menuItem
{
SEL theAction = [menuItem action];
if (theAction == #selector(openPreferencesPanel:)) {
return !_isCurrentlyModal; //A BOOL in MyDocument
}
return [super validateMenuItem:menuItem]; // Keep this for proper cut, paste, etc validation
}
In your case, the above selector might be highlight:. Check the nib/xib and inspect it. It might be attached to the First Responder. Copy the method name.
Also have a gander at for more general items (buttons, etc) and also includes menu items.
- (BOOL)validateUserInterfaceItem:(id <NSValidatedUserInterfaceItem>)anItem
Related
I have an NSWindow with a main "OK" button. This button has as "key equivalent" property in interface builder, the key ENTER i.e ↵.
It works good, but now I have a new NSComboBox, which is supposed to invoke a method when the user selects a list item, or he preses Enter / ↵.
However, when I press Enter, the main Button receive the notification and the window close. How to prevent this?
This is the normal behavior what you are getting, but you can hack a bit, by removing and adding the key-equivalent.
Add following delegates of NSComboBox:
- (void)comboBoxWillPopUp:(NSNotification *)notification;{
[self.closeButton setKeyEquivalent:#""];
}
- (void)comboBoxWillDismiss:(NSNotification *)notification;{
[self.closeButton setKeyEquivalent:#"\r"];
}
One way you can workaround for prevent enter notification is like that below:-
//Connect this action method to your combobbox and inside that set one BOOL flag to yes
- (IBAction)comBoxItm:(id)sender
{
self.isEnterCalled=YES;
}
//Now check this flag to your some method where close window is called
-(void)someMethod
{
//Check the flag value if it is yes then just ignore it
if (!self.isEnterCalled)
{
//Close window logic
}
self.isEnterCalled=NO;
}
Ran into the same problem. Had "hot key" which I'd like to switch off while editing some text fields. I found solution for myself. There's no need in override lots of NSTextField base methods.
Firstly, I removed all the "key equivalents". I used to detect Enter key down with the + (void)addLocalMonitorForEventsMatchingMask:(NSEventMask)mask handler:(NSEvent *(^)(NSEvent *))block class method of NSEvent. You pass block as a parameter, where you can check for some conditions. The first parameter is the event mask. For your task it would be NSKeyDownMask, look for other masks at the NSEvent Reference Page
The parameter block will perform each time the user pushes the button. You should check if it is right button pushed, and - generally - if the current window first responder isn't some editable control. For that purposes we need NSWindow category class just not to implement this code each time we deal with NSKeyDownMasked local monitors.
NSWindow+Responders class listing:
#interface NSWindow (Responders)
- (BOOL)isEditableFirstResponder;
#end
#implementation NSWindow (Responders)
- (BOOL)isEditableFirstResponder
{
if (!self.firstResponder)
return NO; // no first responder at all
if ([self.firstResponder isKindOfClass:[NSTextField class]]) // NSComboBox is NSTextField subclass
{
NSTextField *field=(NSTextField *)self.firstResponder;
return field.isEditable;
}
if ([self.firstResponder isKindOfClass:[NSButton class]]) // yep, buttons may be responders
return YES;
return NO; // the first responder is not NSTextField or NSButton subclass - not editable
}
#end
Don't know if there's another way to check if we are now editing some text field or combo box. So, there's at least the part you add the local monitor somewhere in your class (NSWindow, NSView, some controller etc.).
- (void)someMethod
{
id monitor=[NSEvent addLocalMonitorForEventsMatchingMask:NSKeyDownMask handler:(NSEvent *)^(NSEvent *theEvent){
if (theEvent.keyCode==/*Enter key code*/ && ![self.window.isEditableFirstResponder]) // you should check the key modifiers too
{
// your code here
}
return theEvent; // you may return the event to pass the key to the receiver
}];
}
Local monitors is safe remedy about the Apple rules. It works only inside your application. For global key down events you may use addGlobalMonitor but Apple may reject your app from the AppStore.
And don't forget to remove the monitor when there's no need in it.
- (void)viewControllerShutdownMethod
{
[NSEvent removeMonitor:monitor];
}
Good luck.
Now i am creating dictionary application in iOS.
In my apps, I used textDidChange Event of UISearchBar with UISearchDisplayController for Auto Complete searching.
So when i type a word in SearchBar,it's automatically search in database.
However i have a function in Preferences to turn off AutoComplete searching.
After i turn off AutoComplete function in Preferences, It's still auto complete searching with TextDidChange Event.
I don't want TextDidChange event this time.I want to search after i enter complete word in search bar and then click Search button from Keyboard.
So how can i turn off TextDidChange event from UISearchBar?
Please let me know if you ok.
Best Regards,
In your situtation you don't want to suppress the textDidChange event. Instead you want your UISearchDisplayDelegate's shouldReloadTableForSearchString: method to return NO unless the Search button has been clicked.
You could create a boolean ivar to keep track of this. Set the ivar to NO when textDidChange is called, and set it to YES when searchBarSearchButtonClicked: is called. Then in your shouldReloadTableForSearchString: you can do the test.
EDIT: Clarified by expanding code example. (In this example I am assuming you have set up a ivar named isSearchButtonClicked).
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
{
isSearchButtonClicked = NO;
}
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar
{
isSearchButtonClicked = YES;
}
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString
{
// returns YES if search button was clicked, otherwise return NO
return isSearchButtonClicked;
}
you can always create a bool variable and check if auto complete is on or not... based upon that you can search or not.
- (void)filterContentForSearchText:(NSString*)searchText
scope:(NSString*)scope
{
//create a bool variable and check is Auto Complete searching is Yes or No
if YES then pass searchText
if no then Don't pass any thing unit user press the search button
after he press search button pass searchText
}
I have a CoreData app presenting data with a TableView, textfields, buttons... It deals with people situations and one of the button is a toggle-style button with title "Close". When we consider the user's case closed, we press and it changes the state of a boolean-type attribute in the entity, representing the closed/open state of the case, using a simple binding to the attribute value. The button title also becomes "Reopen" as the case may be reopened in the future.
Then additional things had to be done with the data on pressing the button, so I had to create an IBAction method instead of simply use the former binding. Problem: when button is pressed, the action is done, but the button title is not toggled. It makes sense since nothing tells it to toggle anymore.
I decided to remove the action on the boolean from the IBAction and use again the value binding, so the boolean change is performed by the binding and the other operations are performed by the IBAction. Problem: it modifies the data unexpectedly, sometimes working fine, sometimes not doing all things in a coherent way as expected.
So I'm back with all changes handled by the IBAction and this time, I'm using the Title/Alternate title bindings instead of the value binding. Now the button title toggles, but instead of displaying the word "Close" and "Reopen", it displays the boolean values "0" and "1".
I should perhaps handle the button title change in the IBAction as well, using "setTitle", but then I see a new problem coming. On app start-up, how will it pick the appropriate entity record for reference? And what if the table is in a "No Selection" situation? Looks like a quite extensive piece of code to handle such a small issue...
Any advice on a short, more direct way of handling this is welcome. Thanks.
Sounds like you probably have a couple of different options.
This first option is a bit more involved than the others, but is still good to know for the record. This option basically would not use bindings for the close/re-open button, but instead, set the title, etc. programmatically. The basic game plan would be as follows:
The close/re-open button's initial title when the document opens is set in the nib file (e.g. Close).
Optionally, when the document opens, you could disable the button in -awakeFromNib, since the table view will have no initial selection.
The only actions that would necessitate the button's title or enabled state to be changed is 1) when the tableview's selection is changed, and 2) when you've clicked the close/re-open button to toggle the case's state.
To achieve the desired result, you'd create an IBOutlet to the close/re-open button if you don't already have one. You'd then connect up the tableview's delegate outlet to your controller class, which will let your controller class know when the tableview's selection has been changed (via the - (void)tableViewSelectionDidChange:(NSNotification *)notification <NSTableViewDelegate> protocol method). You would also need to update the close/reopen IBAction method to make sure it switches the title on the button after being clicked (since the tableview selection wouldn't change during that operation). The controller class's code might look something like this:
// add this declaration to avoid compiler warnings:
#interface LHController (LHPrivate)
- (void)updateCloseReopenButtonTitle;
#end
- (void)awakeFromNib {
[self updateCloseReopenButtonTitle];
}
- (void)tableViewSelectionDidChange:(NSNotification *)notification {
[self updateCloseReopenButtonTitle];
}
- (IBAction)toggleCloseReopenButton:(id)sender {
// do your existing code here and then add:
[self updateCloseReopenButtonTitle];
}
- (void)updateCloseReopenButtonTitle {
// assuming 'casesController' is an IBOutlet to your NSArrayController
NSArray *selectedObjects = [casesController selectedObjects];
// loop through the selected cases to determine whether
// they're all closed, all open, or mixed to set
// the title of the button appropriately
BOOL allClosed = YES;
for (LHCase *case in selectedCases) {
if ([case isOpen]) {
allClosed = NO;
break;
}
}
[closeReopenButton setTitle:(allClosed ? #"Reopen" : #"Close")];
[closeReopenButton setEnabled:[selectedObjects count] > 0];
}
Before bindings came along, this is how we used to have to do things.
Another option might be reconsidering your user interface: maybe rather than a push/toggle button whose title you need to toggle, you could instead just have a checkbox titled Closed, which would signify whether the selected cases were closed or not. You could use bindings for the checkbox's state, like shown in the image below:
You could then have an IBAction method that handles the extra stuff that needs processing. You can ask the casesController for the -selectedObjects and then loop through them. As long as you make sure the checkbox Allows Mixed states, it has the added advantage of better representing mixed-case scenarios, in case of a selection of cases with mixed open/closed states (the checkbox a dash instead of a full check).
Another option if you want to stick with a toggle button is to create and specify a custom NSValueTransformer for the title and alternate title bindings. This value transformer would take in the boolean closed/open state of the case and turn it into a string more fitting than just 0 or 1 (which is what's being displayed now). It might look something like this:
+ (Class)transformedValueClass {
return [NSString class];
}
+ (BOOL)allowsReverseTransformation {
return NO;
}
- (id)transformedValue:(id)value {
BOOL isClosed;
if (value == nil) return nil;
// Attempt to get a reasonable value from the
// value object.
if ([value respondsToSelector: #selector(boolValue)]) {
isClosed = [value boolValue];
} else {
[NSException raise: NSInternalInconsistencyException
format: #"Value (%#) does not respond to -boolValue.",
[value class]];
}
return (isClosed ? #"Reopen" : #"Close");
}
Using Cocoa with latest SDK on OSX 10.6.6
I have an NSToolbar with custom toolbar items and also the built in fonts and colors NSToolbarItem items (NSToolbarShowFontsItem and NSToolbarShowColorsItem identifiers).
I need to be able to enable/disable those in various situations. Problem is validateToolbarItem: is never called for these items (it is being called for my other toolbar items).
The documentation is not very clear about this:
The toolbar automatically takes care
of darkening an image item when it is
clicked and fading it when it is
disabled. All your code has to do is
validate the item. If an image item
has a valid target/action pair, then
the toolbar will call
NSToolbarItemValidation’s
validateToolbarItem: on target if the
target implements it; otherwise the
item is enabled by default.
I don't explicitly set target/action for these two toolbar items, I want to use their default behavior. Does it mean I can't validate these items? Or is there any other way I can do this?
Thanks.
After some trial and error, I think I was able to figure this out and find a reasonable workaround. I will post a quick answer here for future reference for others facing the same problem.
This is just one more of Cocoa's design flaws. NSToolbar has a hardcoded behavior to set the target/action for NSToolbarShowFontsItem and NSToolbarShowColorsItem to NSApplication so as the documentation hints it will never invoke validateToolbarItem: for these NSToolbarItem items.
If you need those toolbar items validated, the trivial thing to do is not to use the default fonts/colors toolbar items but to roll your own, calling the same NSApplication actions (see below).
If using the default ones, it is possible to redirect the target/action of them to your object and then invoke the original actions
- (void) toolbarWillAddItem:(NSNotification *)notification {
NSToolbarItem *addedItem = [[notification userInfo] objectForKey: #"item"];
if([[addedItem itemIdentifier] isEqual: NSToolbarShowFontsItemIdentifier]) {
[addedItem setTarget:self];
[addedItem setAction:#selector(toolbarOpenFontPanel:)];
} else if ([[addedItem itemIdentifier] isEqual: NSToolbarShowColorsItemIdentifier]) {
[addedItem setTarget:self];
[addedItem setAction:#selector(toolbarOpenColorPanel:)];
}
}
Now validateToolbarItem: will be called:
- (BOOL)validateToolbarItem:(NSToolbarItem *)theItem {
//validate item here
}
And here are the actions that will be invoked:
-(IBAction)toolbarOpenFontPanel:(id)sender {
[NSApp orderFrontFontPanel:sender];
}
-(IBAction)toolbarOpenColorPanel:(id)sender {
[NSApp orderFrontColorPanel:sender];
}
I guess the engineers who designed this never thought one would want to validate fonts/colors toolbar items. Go figure.
how should i take NSButton text value , e.g if i use 2 buttons with text Click and Cancel, i want to check which button is clicked and then show a message with NSRunAlertPanel(...) which button i have clicked..what code should i write for it when the button is clicked.
In you action method you get an argument, usually named 'sender', which is the button. So you could do something like:
- (IBAction)buttonClicked:(id)sender
{
if ([[sender title] isEqualToString:#"Click"]) {
NSLog(#"Click clicked.");
} else if ([[sender title] isEqualToString:#"Cancel"]) {
NSLog(#"Cancel clicked.");
}
}
It's better not to use the title for checking the button, since the title could change in different localizations. You could specify the tag instead, which is simply an int and which can be used to identify different senders.
The way this is typically implemented is that each button would call a different action, thus there would be no need to check the text of the button. See The Target-Action Mechanism.
In general it is almost always a bad idea to use the user visible text to control program logic because that makes localization harder.
You might also want to describe your situation further. Are you using Interface Builder to create your interface? Are these buttons in a modal dialog or a document window?
You could give the button a name in the class info tab of the inspector window in Interface Builder, then declare it as an IBOutlet in your app delegate.
AppDelegate.h:
IBOutlet NSButton *ClickButton;
IBOutlet NSButton *CancelButton;
Then hook up the outlet in Interface Builder, and just check to see which button is the sender in your method:
- (IBAction)buttonClicked:(id)sender
{
if (sender == ClickButton) {
NSLog(#"Click clicked.");
}
else {
NSLog(#"Cancel clicked.");
}
}