I used this source http://www.cats.rwth-aachen.de/library/programming/cocoa
to create my custom sheet.
I created a NSPanel object in existing .xib file and connected with IBOutlet
My source code:
.h
#interface MainDreamer : NSWindow <NSWindowDelegate>
{
...
NSPanel *newPanel;
}
...
#property (assign) IBOutlet NSPanel *newPanel;
.m
#dynamic newPanel;
...
//this method is wired with button on main window and calls a sheet
- (IBAction)callPanel:(id)sender
{
[NSApp beginSheet:newPanel
modalForWindow:[self window] modalDelegate:self
didEndSelector:#selector(myPanelDidEnd:returnCode:contextInfo:)
contextInfo: nil]; //(__bridge void *)[NSNumber numberWithFloat: 0]
}
//this method is wired with cancel and ok buttons on the panel
- (IBAction)endWorkPanel:(id)sender
{
[newPanel orderOut:self];
[NSApp endSheet:newPanel returnCode:([sender tag] == 9) ? NSOKButton : NSCancelButton];
}
//closing a sheet
- (void)myPanelDidEnd:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
{
if (returnCode == NSCancelButton) return;
else{
return;
}
}
So callPanel works fine, sheet appears but I can't interact with controls on the sheet (with buttons). They don't react on a click (even visually).
Where the problem lies?
Heh, I forgot about
[newDreamPanel close];
in applicationDidFinishLaunching method. I wrote it because I wanted the panel to not appear when main window launches.
In fact, the Visible At Launch panel's property should be activated in IB. The close method works too, but side effect is that all controls become unable on the panel.
Related
I am trying to implement a pop-up menu (something that can be seen when in Chrome I press right mouse button when the cursor is over the left arrow).
I have a class derived from NSToolBarItem and I have another class derived from NSToolBar. In the toolbar I call setAllowsUserCustomization. So my right click anywhere on the toolbar brings up the customization menu for the toolbar.
Thank you for any pointer you can give.
You don't need to subclass NSToolbarItem. Just give one toolbar item its own view (in code or in IB). In that view, you can use a standard control like NSPopUpButton, or a custom view with whatever event handling logic you like.
If you want your NSToolbarItem custom views to receive mouseDown events, you can follow this pattern: use a custom NSWindow subclass (or swizzle the -[NSWindow hitTest:] method) and forward events to your views yourself.
// MyWindow.h
#interface MyWindow : NSWindow
#end
#interface NSView (MyWindow)
- (BOOL)interceptsToolbarRightMouseDownEvents;
#end
// MyWindow.m
#implementation NSView (MyWindow)
- (BOOL)interceptsToolbarRightMouseDownEvents { // overload in your custom toolbar item view return YES
return NO;
}
#end
#interface NSToolbarView : NSView /* this class is hidden in AppKit */ #end
#implementation MyWindow
- (void)sendEvent:(NSEvent*)event {
if (event.type == NSRightMouseDown) {
NSView* frameView = [self.contentView superview];
NSView* view = [frameView hitTest:[frameView convertPoint:event.locationInWindow fromView:nil]];
if ([view isKindOfClass:NSToolbarView.class])
for (NSView* subview in view.subviews) {
NSView* view = [subview hitTest:[subview convertPoint:event.locationInWindow fromView:nil]];
if (view.interceptsToolbarRightMouseDownEvents)
return [view rightMouseDown:event];
}
}
[super sendEvent:event];
}
#end
I'm trying to get my program to recognize a double click with an NSCollectionView. I've tried following this guide: http://www.springenwerk.com/2009/12/double-click-and-nscollectionview.html but when I do it, nothing happens because the delegate in IconViewBox is null:
The h file:
#interface IconViewBox : NSBox
{
IBOutlet id delegate;
}
#end
The m file:
#implementation IconViewBox
-(void)mouseDown:(NSEvent *)theEvent {
[super mouseDown:theEvent];
// check for click count above one, which we assume means it's a double click
if([theEvent clickCount] > 1) {
NSLog(#"double click!");
if(delegate && [delegate respondsToSelector:#selector(doubleClick:)]) {
NSLog(#"Runs through here");
[delegate performSelector:#selector(doubleClick:) withObject:self];
}
}
}
The second NSLog never gets printed because delegate is null. I've connected everything in my nib files and followed the instructions. Does anyone know why or an alternate why to do this?
You can capture multiple-clicks within your collection view item by subclassing the collection item's view.
Subclass NSView and add a mouseDown: method to detect multiple-clicks
Change the NSCollectionItem's view in the nib from NSView to MyCollectionView
Implement collectionItemViewDoubleClick: in the associated NSWindowController
This works by having the NSView subclass detect the double-click and it pass up the responder chain. The first object in the responder chain to implement collectionItemViewDoubleClick: is called.
Typically, you should implement collectionItemViewDoubleClick: in the associated NSWindowController, but it can be in any object within the responder chain.
#interface MyCollectionView : NSView
/** Capture double-clicks and pass up responder chain */
-(void)mouseDown:(NSEvent *)theEvent;
#end
#implementation MyCollectionView
-(void)mouseDown:(NSEvent *)theEvent
{
[super mouseDown:theEvent];
if (theEvent.clickCount > 1)
{
[NSApplication.sharedApplication sendAction:#selector(collectionItemViewDoubleClick:) to:nil from:self];
}
}
#end
Another option is to override the NSCollectionViewItem and add an NSClickGestureRecognizer like such:
- (void)viewDidLoad
{
NSClickGestureRecognizer *doubleClickGesture =
[[NSClickGestureRecognizer alloc] initWithTarget:self
action:#selector(onDoubleClick:)];
[doubleClickGesture setNumberOfClicksRequired:2];
// this should be the default, but without setting it, single clicks were delayed until the double click timed-out
[doubleClickGesture setDelaysPrimaryMouseButtonEvents:FALSE];
[self.view addGestureRecognizer:doubleClickGesture];
}
- (void)onDoubleClick:(NSGestureRecognizer *)sender
{
// by sending the action to nil, it is passed through the first responder chain
// to the first object that implements collectionItemViewDoubleClick:
[NSApp sendAction:#selector(collectionItemViewDoubleClick:) to:nil from:self];
}
What you said notwithstanding, you need to be sure you followed step four in the tutorial:
4. Open IconViewPrototype.xib in IB and connect the View's delegate outlet with "File's Owner":
That should do ya, provided you did follow the rest of the steps.
I'm making an app which lives in status bar. When status item is clicked, NSPopover pops up.
It looks like this:
Here's the problem: I want it to be "transient", that is if I click anywhere outside of the popover, it will close. And while NSPopoverBehaviorTransient works fine when popover is in a window, it doesn't work when it's in status bar.
How can I implement such behavior myself?
It turned out to be easy:
- (IBAction)openPopover:(id)sender
{
// (open popover)
if(popoverTransiencyMonitor == nil)
{
popoverTransiencyMonitor = [NSEvent addGlobalMonitorForEventsMatchingMask:NSLeftMouseDownMask|NSRightMouseDownMask handler:^(NSEvent* event)
{
[self closePopover:sender];
}];
}
}
- (IBAction)closePopover:(id)sender
{
if(popoverTransiencyMonitor)
{
[NSEvent removeMonitor:popoverTransiencyMonitor];
popoverTransiencyMonitor = nil;
}
// (close popover)
}
What wasn't easy, though, is that there are nasty issues with having a popover pop out of NSStatusItem (it didn't behave as desired when Mission Control was invoked or space switched to a full-screen window). I had to implement a custom window that always floats above the NSStatusItem and deals with switching to a full-screen window etc. It seemed easy, but clearly status items weren't designed for something like that ;)
The approach that I use is similar to the above answer except I have everything combined into one method instead of using two separate IBActions.
First, I declare the following properties
#property (strong, nonatomic) NSStatusItem *statusItem;
#property (strong, nonatomic) NSEvent *popoverTransiencyMonitor;
#property (weak, nonatomic) IBOutlet NSPopover *popover;
#property (weak, nonatomic) IBOutlet NSView *popoverView;
then in awakeFromNib I set up the status bar item
- (void)awakeFromNib {
self.statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength];
self.statusItem.title = #"Title";
self.statusItem.highlightMode = YES;
self.statusItem.action = #selector(itemClicked:);
}
followed by the method that is called when the status bar item is clicked
- (void)itemClicked:(id)sender {
[[self popover] showRelativeToRect:[sender bounds] ofView:sender preferredEdge:NSMinYEdge];
if (self.popoverTransiencyMonitor == nil) {
self.popoverTransiencyMonitor = [NSEvent addGlobalMonitorForEventsMatchingMask:(NSLeftMouseDownMask | NSRightMouseDownMask | NSKeyUpMask) handler:^(NSEvent* event) {
[NSEvent removeMonitor:self.popoverTransiencyMonitor];
self.popoverTransiencyMonitor = nil;
[self.popover close];
}];
}
}
which makes the popover appear and also close when the user clicks outside the view.
Note that in Interface Builder you must set the behavior of the popover to Transient so the popover will close when the user clicks the status item.
i am developing a small application. On the first window, i have an option to create a new account. I use a button "Continue" for this. When this button is clicked, another window for creating a new account opens. I want that once this window is opened, no other instance of this nib file should load again. Even if the user clicks on "Continue" again, already opened instance of the nib file (the one for creating a new account) should come to front.
Is there any API which will help to check if one instance of nib is already loaded?
Or may be something that gives a list of all the nibs loaded in the memory?
Thanks in Advance...
UPDATE:
#interface WelcomePageController : NSObject {
IBOutlet NSTextField * userNameField;
IBOutlet NSPopUpButton * actionList;
IBOutlet NSWindow * welcomePage;
CreateNewAccountWindowController * createNewAccountWindowController;
}
-(IBAction) changePasswordButton:(id)sender;
-(IBAction) logOutButton:(id)sender;
-(IBAction) continueButton:(id)sender;
#end
#implementation WelcomePageController
-(void)windowDidUpdate:(id)sender{
UserInfo * user=[UserInfo uInfoObject];
[userNameField setStringValue:[user.firstName stringByAppendingFormat:#" %#!", user.lastName]];
if ([user.userType isEqual:#"Standard"]) {
[actionList setAutoenablesItems:NO];
[[actionList itemAtIndex:2]setEnabled:NO];
[[actionList itemAtIndex:3]setEnabled:NO];
}
else {
[actionList setAutoenablesItems:YES];
}
}
-(IBAction) changePasswordButton:(id)sender{
[NSBundle loadNibNamed:#"ChangePassword" owner:self];
}
-(IBAction) continueButton:(id)sender{
if ([actionList indexOfSelectedItem]==0) {
[NSBundle loadNibNamed:#"ViewAvailableItemsWindow" owner:self];
}
else if([actionList indexOfSelectedItem]==1){
[NSBundle loadNibNamed:#"NewOrderPage" owner:self];
}
else if([actionList indexOfSelectedItem]==2){
[NSBundle loadNibNamed:#"ManageItemList" owner:self];
}
else {
if(!createNewAccountWindowController){
createNewAccountWindowController=[[CreateNewAccountWindowController alloc]init];
}
[createNewAccountWindowController showWindow:self];
//[NSBundle loadNibNamed:#"NewAccount" owner:self];
}
}
-(IBAction) logOutButton:(id)sender{
[NSBundle loadNibNamed:#"LoginPage" owner:self];
[[sender window]close];
}
#end
This is the complete code that i am using....The code in question is the method continueButton..The else condition(last one)..
I have tried this. I open the NewAccountWindow once i click on the Continue button. I close the window and click on the continue button again. However this time the "NewAccountWindow" does not open again(even the already existing instance does not show up).
The standard approach for this is to have a subclass of NSWindowController (potentially holding outlets to the window widgets) responsible for loading the nib file. For instance,
#interface CreateAccountWindowController : NSWindowController {
// …
}
// …
#end
#implementation CreateAccountWindowController
- (id)init {
self = [super initWithWindowNibName:#"CreateAccount"];
return self;
}
// …
#end
When the user clicks the Continue button, you have an action method that handles that click. In the class that contains the action method, declare an instance variable for the corresponding window controller:
CreateAccountWindowController *createAccountWindowController;
and, in the action method that handles clicks of the Continue button, create an instance of CreateAccountWindowController if and only if none exists yet. This will make sure at most one instance of that window controller exists at any given time, hence the corresponding nib file is loaded at most once:
- (IBAction)showCreateAccountWindow:(id)sender {
if (! createAccountWindowController) {
createAccountWindowController = [[CreateAccountWindowController alloc] init];
}
[createAccountWindowController showWindow:self];
}
im very new in cocoa development and I'm trying to load a Window.
I will explain my problem.
When the user click the menuItem I use the following code to load my window
if ( !cadastroContasController )
{
cadastroContasController = [[cadastroContas alloc]init];
[cadastroContasController SetMenuItem:sender];
}
if ( ![[cadastroContasController window] isVisible] )
{
NSLog(#"!isVisible");
[cadastroContasController showWindow:nil];
}
I my cadastroContas class looks like this:
#interface cadastroContas : NSWindowController
{
NSMenuItem *mnuCommand;
IBOutlet NSComboBox *cmbSelecao;
IBOutlet NSTextField *txtNome;
IBOutlet NSTextField *txtSaldoInicial;
IBOutlet NSTextField *txtAnotacoes;
}
- (void)windowDidBecomeKey:(NSNotification *)notification;
- (BOOL)windowShouldClose:(id)sender;
- (void)windowWillClose:(NSNotification *)notification;
- (void)SetMenuItem:(NSMenuItem*) menu;
- (NSMenuItem*) MenuItem;
#end
and the implementation is
#implementation cadastroContas
-(void)windowDidLoad
{
NSLog(#"windowDidLoad");
[mnuCommand setState:NSOnState];
}
-(id)init
{
self = [super initWithWindowNibName:#"cadastroContas"];
NSLog(#"Init self=%p", self);
return self;
}
-(void)dealloc
{
NSLog(#"Dealoc=%p", self);
[super dealloc];
}
- (void)windowDidBecomeKey:(NSNotification *)notification
{
NSLog(#"windowDidBecomeKey window=%p", [self window]);
}
- (BOOL)windowShouldClose:(id)sender
{
NSLog(#"windowShouldClose Window=%p", [self window]);
NSLog(#"mnuComando=%p GetMenuItem=%p", mnuCommand, [self MenuItem] );
if ( mnuCommand )
{
[mnuCommand setState:NSOffState];
}
return YES;
}
- (void)windowWillClose:(NSNotification *)notification
{
NSLog(#"windowWillClose Window=%p", [self window]);
NSLog(#"mnuCommand=%p GetMenuItem=%p", mnuCommand, [self MenuItem] );
[self dealloc];
}
- (void)SetMenuItem:(NSMenuItem*) menu
{
mnuCommand = menu;
}
- (NSMenuItem*) MenuItem
{
return mnuCommand;
}
#end
When the menu was clicked, I received two messages "Init" and I don't know why.
Exemple:
[2223:a0f] Init self=0x10014fe40
[2223:a0f] Init self=0x10011f5a0
The second message let the "[cadastroContasController SetMenuItem:sender];" useless.
So, I need help to understand whats going on..
Another thing, [[cadastroContasController window] is always returning NULL(0x0)!!, but inside my controller i can handle it (it isn't null).
This means you inited two instances, as shown by your logging of the self pointer: Notice that the value is different between the two messages.
You can use the Allocations instrument in Instruments to see what caused each window controller to be instantiated.
Usually, this problem happens when you create one of these in the nib and the other one in code. In the case of a window controller, the one you create in code should be the owner of its nib; you should not create another window controller as an object in the nib.
Another thing, [[cadastroContasController window] is always returning NULL(0x0)!!, but inside my controller i can handle it (it isn't null).
The window controller whose window outlet you set to the window is the one that is returning non-nil. The window controller whose window outlet you didn't set is the one that is returning nil.
Following from what I said above, after deleting the window controller you created in the nib, you should connect your File's Owner's window outlet to the window.