I have an NSTextField that repopulates itself based on the outcome of a result; I would like to automatically perform an IBAction (which is normally controlled by clicking a NSButton in the app). How could I perform the click, or IBAction automatically based upon the result?
appDelegate.h
#interface main : NSView <NSApplicationDelegate>
{
NSView *main;
IBOutlet NSTextField *textValue;
IBOutlet NSWindow* window1;
IBOutlet NSWindow* window2;
}
- (void)textUpdated;
- (IBAction)switchWindow:(id)sender;
Which I would like to trigger or bind to the (IBAction)switchWindow:
appDelegate.m
- (void)textUpdated
{
[textValue setStringValue:[NSString stringWithFormat:#"%#, Switching Window", stringValue]];
[self switchWindow:sender];
}
- (IBAction)switchWindow:(id)sender {
[self performSelector:[NSApp keyWindow]==window?#selector(openWindow):#selector(closeWindow) withObject:nil afterDelay:0.0];
}
- (void)openWindow {
[window1 showWindow:window2 open:YES];
}
- (void)closeWindow {
[window2 showWindow:window1 open:NO];
}
Switching to real code improves the question, but it's still hard to understand exactly what you're asking. As Josh Caswell pointed out, an action is just a method and you're free to call it yourself. You seem to be doing that already in -textUpdated, but your assertion that "it still doesn't do anything" doesn't give us much to go on. So:
Does -textUpdated get called? If no, there's your problem.
Does -switchWindow: get called? Stick a breakpoint in that method and see if you stop there. (Hint: If you answered 'yes' to the first question, you'll stop there.)
What do you expect to happen that isn't happening?
Are the values of the various variables (ivars and local variables) what you expect?
Do -openWindow and -closeWindow get called appropriately?
What does [NSApp keyWindow] return? Does it ever return a pointer equivalent to window? (And by the way, it wouldn't hurt to tell us what window means here.)
Did you perhaps intend to write something like (note the '1' after 'window'):
[self performSelector:[NSApp keyWindow] == window1 ? #selector(openWindow) : #selector(closeWindow) withObject:nil afterDelay:0.0];
Your code might be easier to understand if you just do one thing at a time. Yes, it's more lines, but it's a lot easier to debug:
NSWindow *keyWindow = [NSApp keyWindow];
if (keyWindow == window1) {
[self openWindow];
}
else if (keyWindow == window2) {
[self closeWindow];
}
Related
I have my NSButton layer backed because I wanted to use a custom image, but this seems like it's inhibiting the use of the setFont: method when I need to programmatically change the font, as when I comment out the code for wantsUpdateLayer: and updateLayer:, setFont: works, but when the layer methods are in the code, it does nothing.
#implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
self.fontChangeButton = [[CustomButton alloc]initWithFrame:NSMakeRect(82, 60, 190, 113)];
[self.window.contentView addSubview:self.fontChangeButton];
}
- (IBAction)changeFont:(id)sender {
[self.fontChangeButton fontChange];
}
#end
#implementation CustomButton
- (void)fontChange{
[self setFont:[NSFont fontWithName:#"Dosis Bold" size:40]];
}
//when these are commented out, setFont: works, but I need them in for the custom button images
- (BOOL)wantsUpdateLayer{
return YES;
}
- (void)updateLayer{
if (self.state == NSOnState) {
self.layer.contents = [NSImage imageNamed:#"buttonPressed.png"];
}else
self.layer.contents = [NSImage imageNamed:#"buttonUnpressed.png"];
}
This thread offers a workaround, but I'd much rather understand why this is happening and fix it: Can't Change NSButton Font
By overriding -wantsUpdateLayer to return YES you're bypassing calls to -drawRect:. This facility was introduced in 10.8 and exists for efficiency purposes.
There are two things I think should be clarified:
1 - You don't need to override -wantsUpdateLayer to be layer-backed. Just send -setWantsLayer:YES to your button to be layer-backed.
2 - In your example, creating a custom NSButtonCell class might be a better approach to what you're trying to do. Have a look at Apple's documentation on subclassing NSControl and this how to to get started.
I just implemented following method that suppose to take some action after the value of a NSTextField is changed in my NSOutlineView
-(BOOL)control:(NSControl *)control textShouldEndEditing:(NSText *)fieldEditor
{
NSLog(#"end editing");
NSTextField* tf = (NSTextField*)control;
if(selectedItem && [selectedItem isKindOfClass:[HSCategoryClass class]])
{
HSCategoryClass* c = selectedItem;
c.name = tf.stringValue;
// request the update from DB
[[NSNotificationCenter defaultCenter] postNotificationName:#"updatingCategoryName"
object:c
userInfo:#{#"sender":self}];
}
return YES;
}
However, when I'm done editing and hit enter key or navigate anywhere outside of the text field this method is getting called twice instead of just once.
Does anyone know why is this?!
Any kind of help is highly appreciated!
That routine does not signify that editing has ended. Instead, it's called to find out if it should end (hence the name of the method). It can be called by the framework any number of times, and you shouldn't be relying on it for this purpose.
Instead override the NSOutlineView's textDidEndEditing: method.
Be sure to call super.
So you'd subclass the NSOutlineView and in your subclass:
- (void)textDidEndEditing:(NSNotification *)aNotification
{
// do your stuff
[super textDidEndEditing:aNotification];
}
Below is my typical WindowController module for presenting a modal dialog (could be settings, asking username/password, etc) loaded from a XIB. It seems a bit too complex for something like this. Any ideas how this can be done better/with less code?
Never mind that it's asking for a password, it could be anything. What frustrates me most is that I repeat the same pattern in each and every of my XIB-based modal window modules. Which of course means I could define a custom window controller class, but before doing that I need to make sure this is really the best way of doing things.
#import "MyPasswordWindowController.h"
static MyPasswordWindowController* windowController;
#interface MyPasswordWindowController ()
#property (weak) IBOutlet NSSecureTextField *passwordField;
#end
#implementation MyPasswordWindowController
{
NSInteger _dialogCode;
}
- (id)init
{
return [super initWithWindowNibName:#"MyPassword"];
}
- (void)awakeFromNib
{
[super awakeFromNib];
[self.window center];
}
- (void)windowWillClose:(NSNotification*)notification
{
[NSApp stopModalWithCode:_dialogCode];
_dialogCode = 0;
}
- (IBAction)okButtonAction:(NSButton *)sender
{
_dialogCode = 1;
[self.window close];
}
- (IBAction)cancelButtonAction:(NSButton *)sender
{
[self.window close];
}
+ (NSString*)run
{
if (!windowController)
windowController = [MyPasswordWindowController new];
[windowController loadWindow];
windowController.passwordField.stringValue = #"";
if ([NSApp runModalForWindow:windowController.window])
return windowController.passwordField.stringValue;
return nil;
}
The application calls [MyPasswordWindowController run], so from the point of view of the user of this module it looks simple, but not so much when you look inside.
Set tags on your buttons to distinguish them. Have them both target the same action method:
- (IBAction) buttonAction:(NSButton*)sender
{
[NSApp stopModalWithCode:[sender tag]];
[self.window close];
}
Get rid of your _dialogCode instance variable and -windowWillClose: method.
-[NSApplication runModalForWindow:] will already center the window, so you can get rid of your -awakeFromNib method.
Get rid of the invocation of -[NSWindowController loadWindow]. That's an override point. You're not supposed to call it. The documentation is clear on that point. It will be called automatically when you request the window controller's -window.
Get rid of the static instance of MyPasswordWindowController. Just allocate a new one each time. There's no point in keeping the old one around and it can be troublesome to reuse windows.
I have a NSWindow which consist a NSView which appear only on some specific occasion this NSView consist a NSSearchField I want if there is data on clipboard it will appear in searchField otherwise it would be empty.I am able to do this what I want is if there is Data in SearchField it should be in focus I tried it this way:
In NSViewController class there is a function which returns NSSearchField which is a class variable
-(NSSearchField*)getSearchField
{
return searchField;
}
In NSWindowController class I am making it first responder where pSearchContact is instance variable of NSViewController class
[[self window] makefirstResponder:[pSearchContact getSearchField]];
It is running smoothly but I don't know why searchField is not getting focus
Is their something like searchField will become first responder only if it is a part of NSWindow because in my case searchField is in NSView which is in NSWindow.
Thanks in Advance
Your question is a little confusing but I think what you're asking to boils down to "why can't I ever make my search field the focus?".
That one line of code:
[[self window] makefirstResponder:[pSearchContact getSearchField]];
has a little too much going on with it for my comfort (i.e. I wouldn't embed so much functionality - any pieces of which could go wrong or haywire - into one line of code).
How about doing something like this:
NSWindow * myWindow = [self window];
if(myWindow)
{
if(pSearchContact)
{
NSResponder * ourSearchField = [pSearchContact getSearchField];
if(ourSearchField)
{
[myWindow makeFirstResponder: ourSearchField];
} else {
NSLog( #"ourSearchfield is nil; why?" );
}
} else {
NSLog( #"pSearchContact is nil; why?" );
}
} else {
NSLog( #"myWindow is nil; why?" );
}
This might also allow you to narrow down on why the focus setting isn't working for you.
A late response, but I was having a similar issue which was fixed by unchecking refusesFirstResponder in Xcode/Interface Builder.
I am not sure I am doing things the right way, or if I have it all hacked up.
I have a really simple test application (not document-based) I created for learning how to work with application-modal dialogs in Cocoa applications.
With the application project "TestModalDialog", I have a simple MainMenu.xib with the default view and a button, "Show Dialog", I added. I created a second XIB called TheDialog.xib that has "Cancel" and "OK" buttons. That xib has as its owner a class derived from NSWindowController called "TheDialogController"; the window outlet and delegate are connected to the controller.
Selection of "Show Dialog" on the main view will launch the dialog. Selection of "Cancel" or "OK" will dismiss the dialog. Here is the pretty simple code:
// TestModalDialogAppDelegate.h
// TestModalDialog
#import <Cocoa/Cocoa.h>
#class TheDialogController;
#interface TestModalDialogAppDelegate : NSObject <NSApplicationDelegate>
{
NSWindow *window;
TheDialogController* theDialogController;
}
#property (assign) IBOutlet NSWindow *window;
- (IBAction)showDialog:(id)sender;
#end
// TestModalDialogAppDelegate.m
// TestModalDialog
#import "TestModalDialogAppDelegate.h"
#import "TheDialogController.h"
#implementation TestModalDialogAppDelegate
#synthesize window;
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
theDialogController= [[TheDialogController alloc] init];
}
- (void)dealloc
{
if(nil != theDialogController)
[theDialogController release];
[super dealloc];
}
- (IBAction)showDialog:(id)sender
{
if(nil == theDialogController)
{
NSAlert* alert= [NSAlert alertWithMessageText:#"Dialog Error" defaultButton:nil alternateButton:nil otherButton:nil informativeTextWithFormat:#"The dialog controller was not allocated."];
[alert runModal];
return;
}
NSInteger result= [NSApp runModalForWindow:[theDialogController window]];
// Do something with result....
}
#end
// TheDialogController.h
// TestModalDialog
#import <Cocoa/Cocoa.h>
#interface TheDialogController : NSWindowController
{
// BOOL userClickedCloseOrOk; // Removed based on answer.
// Should declare a common define - just being lazy.
NSInteger userClickedOk; // Added based on answer.
UInt16 timesShown;
}
- (IBAction)showWindow:(id)sender;
- (IBAction)closeDialog:(id)sender;
- (IBAction)okDialog:(id)sender;
//- (BOOL)windowShouldClose:(id)sender; // Removed based on answer.
- (void)windowWillClose:(NSNotification*)notification; // Added based on answer.
- (void)windowDidBecomeKey:(NSNotification*)notification; // To set title when modal.
#end
// TheDialogController.m
// TestModalDialog
#import "TheDialogController.h"
#implementation TheDialogController
- (id)init
{
self = [super initWithWindowNibName:#"TheDialog"];
userClickedOk= 0; // Added based on answer.
// userClickedCloseOrOk= FALSE; // Removed based on answer.
return self;
}
-(void)dealloc
{
// Do member cleanup if needed.
[super dealloc];
}
- (void)windowDidLoad
{
[super windowDidLoad];
// Initialize as needed....
[[self window] center]; // Center the window.
}
// Does not show with runModalForWindow.
- (IBAction)showWindow:(id)sender
{
// Just playing with the window title....
++timesShown;
NSString* newTitle= [NSString stringWithFormat:#"Shown %d Times", timesShown];
[[self window] setTitle:newTitle];
return [super showWindow:sender];
}
// This method no longer used for this solution based on the answer.
//- (BOOL)windowShouldClose:(id)sender
//{
// if(!userClickedCloseOrOk) // The user did not click one of our buttons.
// [NSApp abortModal];
// else
// userClickedCloseOrOk= FALSE; // Clear for next time.
//
// return TRUE;
//}
// Added based on answer.
- (void)windowWillClose:(NSNotification*)notification
{
[NSApp stopModalWithCode:userClickedOk];
userClickedOk= 0; // Reset for next time.
}
// Note - the title will update every time the window becomes key. To do the
// update only once per modal session, a flag can be added. There might be a better
// notification to catch.
- (void)windowDidBecomeKey:(NSNotification*)notification
{
++timesShown;
NSString* newTitle= [NSString stringWithFormat:#"Shown %d Times", timesShown];
[[self window] setTitle:newTitle];
}
- (IBAction)closeDialog:(id)sender
{
//userClickedCloseOrOk= TRUE; // Removed based on answer.
//[NSApp abortModal]; // Removed based on answer.
//[[self window] performClose:self]; // Removed based on answer.
[[self window] close]; // Know we want to close - based on answer.
}
- (IBAction)okDialog:(id)sender
{
userClickedOk= 1; // Added based on answer.
//userClickedCloseOrOk= TRUE; // Removed based on answer.
//[NSApp stopModal]; // Removed based on answer.
//[[self window] performClose:self]; // Removed based on answer.
[[self window] close]; // Know we want to close - based on answer.
}
#end
I had trouble with the modality - before I put in userClickedCloseOrOk and tests, if the user hit the close button (upper-left red dot), the dialog would close but the modal session was still running.
I realize I could just leave the close-button off the dialog to start with, but with it there, is what I have demonstrated a good way to catch that scenario, or is there a better way? Or, am I doing something wrong to start with, which creates that problem for me?
Any advice would be appreciated.
NOTE - Code from the original example commented out and replaced with code based on the answer. Also added a new notification handler.
In the okDialog: and closeDialog: methods call close instead of performClose: to avoid the window calling the windowShouldClose: delegate method. That way you don't need userClickedCloseOrOk.
Also I think you want to be calling stopModalWithCode: instead of stopModal since in the app delegate you seem to be interested in the result. And you can call stopModal or stopModalWithCode: instead of abortModal because you are always in the runloop when you call it (abort is for when you are outside of the modal runloop, like in another thread or timer's runloop).
In windowShouldClose: you are doing an action (abortModal) when you should only be answering the question "should this window close". The windowWillClose: delegate method is where you should do actions if you need to.
Sheets are useful when there is one window and it tells the user they can't do anything with it until they complete whatever is in the sheet. Application-modal windows are useful when you have multiple windows that the user can't interact with until they complete whatever is in the modal window or where there is an error that involves the whole application but is not tied to the content of one window. In their HIG Apple suggests avoiding the use of Application-modal windows whenever possible.
I have actually just been struggling with the same problem and found this link :
Stopping modal when window is closed (Cocoa)