Ok, I have used interface builder and added tooltips to all controls.
I would like to offer the user a menu item "disable tooltips".
How do I disable all tooltips globally on a cocoa application?
Instead of setting the text for the tooltips in directly in Interface Builder, make NSString properties for them in your view controller (or other bindable object). Use a Boolean property to control whether or not the tooltips will be shown.
#interface YourViewController : NSViewController
#property (readonly) NSString *thisTooltip;
#property (readonly) NSString *thatTooltip;
#property BOOL showTooltips;
#end
#implementation YourViewController
- (NSString *)thisTooltip {
if (showTooltips) {
return #"This is a tooltip";
}
else return #"";
}
- (NSString *)thatTooltip {
if (showTooltips) {
return #"That is a tooltip";
}
else return #"";
}
#end
Use the Bindings Inspector in IB to bind the Tooltip to the Property:
As you can see, this strategy makes it possible to customize your tooltips dynamically, while your application is running.
Related
I have a document-based app with a tool bar containing several NSButton which I need to validate. Base on other code here, I have subclassed NSToolbar:
#interface CustomToolbar : NSToolbar
#end
#implementation CustomToolbar
-(void)validateVisibleItems
{
for (NSToolbarItem *toolbarItem in self.visibleItems)
{
NSResponder *responder = toolbarItem.view;
while ((responder = [responder nextResponder]))
{
if ([responder respondsToSelector:toolbarItem.action])
{
[responder performSelector:#selector(validateToolbarItem:) withObject:toolbarItem];
}
}
}
}
#end
MyDocument (the File's owner) is set as the delegate of the toolbar. However
-(BOOL)validateToolbarItem:(NSToolbarItem *)toolbarItem
is never called. The buttons have an action set on them, so not sure why [responder respondsToSelector:toolbarItem.action] is always false.
I have tried subclassing the NSButton items:
#interface DocumentToolbarActionItem : NSToolbarItem
#implementation DocumentToolbarActionItem
-(void)validate
{
Document* document = [[self toolbar] delegate];
[self setEnabled:[document validateUserInterfaceItem:self]];
}
#end
But this results in an endless loop.
The document's validateUserInterfaceItem: method works for all other items in the app and I need to have my toolbar button call it to determine if they should be enabled or not.
My guess is that you're not calling through [super validateVisibleItems] and, so, losing the superclass behaviour of validation through the responder chain.
I am trying to create a simple set of buttons which show/hide a text field.
I created a BOOL in .h;
BOOL showBool;
#property BOOL showBool;
Then have buttons linked to actions in .h:
- (IBAction) showText:(id)sender;
- (IBAction) hideText:(id)sender;
Then in .m, I have the actions, which should be setting the bool;
- (IBAction) showText:(id)sender;
{
showBool = YES;
}
- (IBAction) hideText:(id)sender;
{
showBool = NO;
}
I have a text field key value bound via App Delegate as 'hidden' to showBool. But it does not change (hide/show) during run...
Am I setting the booleans incorrectly ??
Bindings rely on key value observing. Use the accessor self.showBool = YES; to trigger the change for the binding, rather than directly giving the ivar a value. This assumes you did the #synthesize in your implementation.
I created a View-Based NSTableView with a single column. This column is populated with a standard NSTableCellView from Interface Builder (I chose the version with image and textfield).
Now I want to make the textfield in the column editable.
My first attempt was to modify the NSTextField from Interface builder and set its behaviour as Editable. It works, indeed when I select a row and I push the enter key the field becomes editable and I can change its value. I thought I would be able to intercept this change thanks to some NSTableViewDataSource method like tableView:setObjectValue:forTableColumn:row: but this method never gets called in response of a textfield edit action.
Which is the right way to deal with editable field in a view-based NSTableView system? I suppose that the NSTableViewDataSource has something to do with it but I don't know how to get its methods called.
Create a subclass of NSTableCellView. (The appropriate .h and .m files) Make the class respond to the NSTextFieldDelegate protocol. Implement the control:textShouldEndEditing: method. Make this subclass the delegate of your label control.
Here is some example code.
CategoryListCell.h
#interface CategoryListCell : NSTableCellView
#end
CategoryListCell.m
#interface CategoryListCell()<NSTextFieldDelegate>
#property (weak) IBOutlet NSTextField *categoryLabel;
#property (assign) BOOL editing;
#property (copy) NSString* category;
#end
#implementation CategoryListCell
- (BOOL)control:(NSControl*)control textShouldBeginEditing:(NSText *)fieldEditor {
self.editing = YES;
return YES;
}
- (BOOL)control:(NSControl *)control textShouldEndEditing:(NSText *)fieldEditor; {
if (self.editing) {
self.editing = NO;
[self mergeFromSource:self.category toDestination:self.categoryLabel.stringValue];
}
return YES;
}
- (void)mergeFromSource:(NSString*)source toDestination:(NSString*) destination {
// your work here
}
#end
Sounds like you need to subclass the NSView that's in the NSTableView cell and make the subclassed view a delegate of the textfield. Your view will then get text change notifications via the NSTextField delegate method:
- (void)textDidChange:(NSNotification *)notification;
My application contains a PLAY/PAUSE button that is set to type Toggle in Interface Builder. I use it - as the name reveals - to play back my assets or to pause them.
Further, I am listening to the SPACE key to enable the same functionality via the keyboard shortcut. Therefore, I use keyDown: from NSResponderin my application. This is done in another subview. The button itself is not visible at this time.
I store the current state of playback in a Singleton.
How would you update the title/alternative title for the toogle button while taking into account that its state could have been altered by the keyboard shortcut? Can I use bindings?
I managed to implement the continuous update of the button title as follows. I added a programmatic binding for the state (in the example buttonTitle). Notice, that the IBAction toggleButtonTitle: does not directly change the button title! Instead the updateButtonTitle method is responsible for this task. Since self.setButtonTitle is called the aforementioned binding gets updated immediately.
The following example shows what I tried to describe.
// BindThisAppDelegate.h
#import <Cocoa/Cocoa.h>
#interface BindThisAppDelegate : NSObject<NSApplicationDelegate> {
NSWindow* m_window;
NSButton* m_button;
NSString* m_buttonTitle;
NSUInteger m_hitCount;
}
#property (readwrite, assign) IBOutlet NSWindow* window;
#property (readwrite, assign) IBOutlet NSButton* button;
#property (readwrite, assign) NSString* buttonTitle;
- (IBAction)toggleButtonTitle:(id)sender;
#end
And the implementation file:
// BindThisAppDelegate.m
#import "BindThisAppDelegate.h"
#interface BindThisAppDelegate()
- (void)updateButtonTitle;
#end
#implementation BindThisAppDelegate
- (id)init {
self = [super init];
if (self) {
m_hitCount = 0;
[self updateButtonTitle];
}
return self;
}
#synthesize window = m_window;
#synthesize button = m_button;
#synthesize buttonTitle = m_buttonTitle;
- (void)applicationDidFinishLaunching:(NSNotification*)notification {
[self.button bind:#"title" toObject:self withKeyPath:#"buttonTitle" options:nil];
}
- (IBAction)toggleButtonTitle:(id)sender {
m_hitCount++;
[self updateButtonTitle];
}
- (void)updateButtonTitle {
self.buttonTitle = (m_hitCount % 2 == 0) ? #"Even" : #"Uneven";
}
#end
If you store your state in an enum or integer a custom NSValueTransformer will help you to translate a state into its button title equivalent. You can add the NSValueTransformer to the binding options.
NSDictionary* options = [NSDictionary dictionaryWithObject:[[CustomValueTransformer alloc] init] forKey:NSValueTransformerBindingOption];
[self.button bind:#"title" toObject:self withKeyPath:#"buttonTitle" options:options];
I'm pretty new to mac development (coming from a web and iOS background) and I can't work out how I could get a notification every time the value of an NSTextView changes. Any ideas?
Ups I just saw that you want a callback from NSTextView and not NSTextField
Just add in the header of the object which should be the delegate the protocol
#interface delegateAppDelegate : NSObject <NSApplicationDelegate, NSTextViewDelegate> {
NSWindow *window;
}
After that you add a method like
-(void)textDidChange:(NSNotification *)notification {
NSLog(#"Ok");
}
Make sure you connected the delegate property of the NSTextView (not NSScrollView) with the object which should receive the delegate
Here's the solution:
NSTextView *textView = ...;
#interface MyClass : NSObject<NSTextStorageDelegate>
#property NSTextView *textView;
#end
MyClass *myClass = [[MyClass alloc] init];
myClass.textView = textView;
textView.textStorage.delegate = myClass;
#implementation MyClass
- (void)textStorageDidProcessEditing:(NSNotification *)aNotification
{
// self.textView.string will be the current value of the NSTextView
// and this will get invoked whenever the textView's value changes,
// BOTH from user changes (like typing) or programmatic changes,
// like textView.string = #"Foo";
}
#end
set the nstextfield's delegate. in the .h file of the delegate you add the delegate protocol
In the .m file you add a method like -(void)controlTextDidChange:(NSNotification *)obj {
NSLog(#"ok");
}
I hope that helps
Set the delegate and then use
- (void) controlTextDidChange: (NSNotification *) notification
{
}