So I got a Window:
Window.xib
I got a WindowController too:
WindowController.h reads:
#import <Cocoa/Cocoa.h>
#interface MainWindowController : NSWindowController
{
IBOutlet NSView *firstView;
IBOutlet NSView *secondView;
IBOutlet NSView *thirdView;
int currentViewTag;
}
-(IBAction)switchView:(id)sender;
#end
And the WindowController.m reads:
#import "MainWindowController.h"
#interface MainWindowController ()
#end
#implementation MainWindowController
-(id)init
{
self = [super initWithWindowNibName:#"MainWindow"];
if (self){
// Initialization code here
}
return self;
}
//- (void)windowDidLoad {
// [super windowDidLoad];
//
// // Implement this method to handle any initialization after your window controller's window has been loaded from its nib file.
//}
#pragma mark - Custom view drawing
-(NSRect)newFrameForNewContentView:(NSView *)view
{
NSWindow *window = [self window];
NSRect newFrameRect = [window frameRectForContentRect:[view frame]];
NSRect oldFrameRect = [window frame];
NSSize newSize = newFrameRect.size;
NSSize oldSize = oldFrameRect.size;
NSRect frame = [window frame];
frame.size = newSize;
frame.origin.y -= (newSize.height - oldSize.height);
return frame;
}
-(NSView *)viewForTag:(int)tag{
NSView *view = nil;
if (tag == 0) {
view = firstView;
} else if (tag == 1) {
view = secondView;
} else {
view = thirdView;
}
return view;
}
-(BOOL) validateToolbarItem:(NSToolbarItem *)item
{
if ([item tag] == currentViewTag) return NO;
else return YES;
}
-(void)awakeFromNib
{
[[self window] setContentSize:[firstView frame].size];
[[[self window] contentView]addSubview:firstView];
[[[self window] contentView]setWantsLayer:YES];
}
-(IBAction)switchView:(id)sender
{
int tag = [sender tag];
NSView *view = [self viewForTag:tag];
NSView *previousView = [self viewForTag:currentViewTag];
currentViewTag = tag;
NSRect newFrame = [self newFrameForNewContentView:view];
[NSAnimationContext beginGrouping];
if ([[NSApp currentEvent] modifierFlags] & NSShiftKeyMask)
[[NSAnimationContext currentContext] setDuration:1.0];
[[[[self window]contentView]animator]replaceSubview:previousView with:view];
[[[self window]animator]setFrame:newFrame display:YES];
[NSAnimationContext endGrouping];
}
#end
The problem I have is that when i switch tabs in my app, the custom view (and there are three of different sizes) draw differently each time. Look at the screenshots, all of the numbers should be centre aligned but they are sometimes and others not. Can anyone see what my error is please?
I will also add that all of the actions have been correctly configured + the code works perfectly if the custom view size is the same all the time.
The view that works
The view that almost works
Again pointing out that in my .xib all of the numbers are aligned to 0x and 0y axis.
Appdelegate.h
#import <Cocoa/Cocoa.h>
#class MainWindowController;
#interface AppDelegate : NSObject <NSApplicationDelegate> {
MainWindowController *mainWindowController;
}
#end
Appdelegate.m
#interface AppDelegate ()
#property (nonatomic) IBOutlet NSWindow *window;
#end
#implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
// Insert code here to initialize your application
}
- (void)applicationWillTerminate:(NSNotification *)aNotification {
// Insert code here to tear down your application
}
-(void)awakeFromNib
{
if(!mainWindowController){
mainWindowController = [[MainWindowController alloc]init];
}
[mainWindowController showWindow:nil];
}
#end
In Interface Builder, make sure to disable the "autoresizes subviews" checkbox of the default view of your window
I've been trying to get this NSTableView to populate for the last 7 hours. I am trying to get a list of all the currently running application and put them into an NSTableView. Eventually I would like to parse the resultes and organize the PID in one column and the Application Bundle in the other. I am getting an EXC_BAD_ACCESS error on " return [listOfWindows objectAtIndex:row];" I am currently using Xcode 4.3.2 and running OS X Lion 10.7.4. Thanks in advance everyone!
#interface AppDelegate : NSObject <NSApplicationDelegate>
{
IBOutlet NSMenu *statusMenu;
IBOutlet NSButton *button;
IBOutlet NSWindow *menuWindow;
IBOutlet NSTableView *proTable;
NSArray *listOfWindows;
IBOutlet NSArrayController *arrayController;
AppDelegate *mainMenu;
NSWorkspace *workSpace;
NSStatusItem *statusItem;
}
#property (assign) IBOutlet NSWindow *window;
-(IBAction)loadConfig:(id)sender;
#end
#import "AppDelegate.h"
#implementation AppDelegate
#synthesize window = _window;
- (void) awakeFromNib
{
[[NSDistributedNotificationCenter defaultCenter] addObserver:self
selector:#selector(loadMenu:)
name:#"WhiteBox"
object:nil];
[self addStatusItem];
//[proTable setDataSource:self];
listOfWindows = [[NSWorkspace sharedWorkspace] runningApplications];
NSLog(#"index %#", listOfWindows);
int y = 0;
y = [listOfWindows count];
NSLog(#"y = %d", y);
[proTable setAllowsMultipleSelection:YES];
}
-(void)applicationWillTerminate
{
NSLog(#"Will Terminate");
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
}
-(void)applicationDidResignActive:(NSNotification *)notification
{
NSLog(#"Resign Active");
}
-(void) addStatusItem
{
//Create a variable length status item from the system statusBar
statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength];
[statusItem retain];
//Set a Title for it
[statusItem setTitle:#"Status Item"];
//Set an Image and an alternate image
//[statusItem setImage:[NSImage imageNamed:#"lnc"]];
//[statusItem setAlternateImage: [NSImage imageNamed:#"status"]];
//Add a Tool Tip
[statusItem setToolTip:#"Status Item Tooltip"];
//Choose to highlight the item when clicked
[statusItem setHighlightMode:YES];
//To Trigger a method on click use the following two lines of code
[statusItem setMenu:statusMenu];
//[statusItem setAction:#selector(loadMenu:)];
}
-(IBAction)loadConfig:(id)sender
{
if(! [menuWindow isVisible] )
{
[menuWindow makeKeyAndOrderFront:sender];
} else {
[menuWindow performClose:sender];
}
}
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView
{
return [listOfWindows count];
}
- (id)tableView:(NSTableView *)tableView
objectValueForTableColumn:(NSTableColumn *)tableColumn
row:(NSInteger)row
{
return [listOfWindows objectAtIndex:row];
}
#end
What object is the table view's data source? I don't see any object in the source you posted as implementing the NSTableViewDataSource protocol.
Further, have you tried putting breakpoints in the various data source methods to see if the debugger stops in them? If not, it's usually a good sign that your data source isn't connected to your table view.
I got: -[NSRunningApplication copyWithZone:]: unrecognized selector error when I ran your code. This could be fixed by changing your return line in tableView:objectValueForTableColumn:row: to
return [[listOfWindows objectAtIndex:row]localizedName];
NSRunningApplication doesn't conform to NSCopying, so I don't know if you can put instances of that class in a table view. However, you can get its properties like localizedName, processIdentifier, and bundleIdentifier.
I've run into this problem before with classes that don't implement NSCopying, I'd be happy to know if anyone knows a way to use these classes in table views or outline views.
I am trying to create a simple framework with a nib that has a button on it which can be customized (selector wise and title wise.) for this, i did the following:
I added a property:
#property (nonatomic,retain) NSButton*accessoryButton;
and connected it to my outlet:
#synthesize accessoryButton = littleButton;
I then shared the instance as such:
+ (TestWindow *)sharedPanel
{
return sharedPanel ? sharedPanel : [[[self alloc] init] autorelease];
}
- (id)init
{
if (sharedPanel) {
[self dealloc];
} else {
sharedPanel = [super init];
}
return sharedPanel;
}
and load the nib:
if( !somewindow )
{
[NSBundle loadNibNamed: #"window" owner:nil];
}
[NSApp activateIgnoringOtherApps:YES];
[somewindow center];
[somewindow setLevel:NSModalPanelWindowLevel];
[somewindow makeKeyAndOrderFront:self];
When I however want to change the title for example from my sample project, it never works.
[TestWindow sharedPanel] setTitle:#"hi"]; //doesnt work
Here's my setTitle: method:
-(void)setTitle:(NSString *)buttonTitle
{
[[self accessoryButton] setTitle:buttonTitle];
[[self accessoryButton] display];
}
I don't get an error but nothing happens either. What am I missing?
Is the button nil at runtime? Are you sure your button's outlet is connected?
Does your init function get called when the NIB is loaded?
I can't seem to find a way to get notified when an NSTextField loses focus by pressing the Tab key. I get a nice textDidEndEditing when clicking another control or when pressing Enter, but not if I change the focus by pressing the Tab key.
Also tried to yank KeyDown and doCommandBySelector for this purpose but I got nowhere.
Any ideas?
Thanks in advance
Edit:
Forgot to mention, but I tried resignFirstResponder too. This is the code I tried:
- (BOOL)resignFirstResponder
{
NSRunAlertPanel(#"", #"Lost Focus",#"OK", nil, nil);
return [super resignFirstResponder];
}
- (BOOL)becomeFirstResponder
{
NSRunAlertPanel(#"", #"Got focus",#"OK", nil, nil);
return [super becomeFirstResponder];
}
Strangely, what happens here is that when getting focus, both becomeFirstResponder and resignFirstResponder are called one after the other. But when changing focus away from the control, neither are.
"I get a nice textDidEndEditing when
clicking another control or when
pressing Enter, but not if I change
the focus by pressing the Tab key."
As of April 2011, with OS X 10.6 libs, I'm using:
- (void)controlTextDidEndEditing:(NSNotification *)aNotification
...to listen for NSTextField losing focus, and it's working correctly. Is this possible in your situation? Is it something that used to be broken, but is now fixed by Apple?
If so, it's much less code :).
Ok, I've found a way to do it: use a window delegate to make the window return a custom field editor. This field editor keeps track of the last TextField that's been activated and calls its textDidEndEditting method when losing firstResponder itself. Here's an example of how to do it:
#import <Cocoa/Cocoa.h>
#import <AppKit/AppKit.h>
#interface MyTextField : NSTextField
- (BOOL)resignFirstResponder;
- (void)textDidEndEditing:(NSNotification *)notification;
#end
#interface MyFieldEditor : NSTextView
{
MyTextField * lastBox;
}
-(void) setLastEditBox:(MyTextField*) box;
#end
#interface MyWindowDelegate : NSWindowController
{
MyFieldEditor *fieldEditor;
}
#end
#implementation MyFieldEditor
-(void) setLastEditBox:(MyTextField*) box{ lastBox = box; }
-(id)init
{
if (self = [super init])
[self setFieldEditor:YES];
return self;
}
- (BOOL)resignFirstResponder
{
// Activate the last active editbox editting-end event
if(lastBox != nil)
{
[lastBox textShouldEndEditing:self];
lastBox = nil;
}
return [super resignFirstResponder];
}
#end
#implementation MyWindowDelegate
-(id)windowWillReturnFieldEditor:(NSWindow *)sender toObject:(id)client
{
if(fieldEditor == nil) // Return our special field editor
fieldEditor = [[[MyFieldEditor alloc] autorelease] init];
return fieldEditor;
}
#end
#implementation MyTextField
- (BOOL)resignFirstResponder
{
// We're losing first responder, inform the field editor that this was the last edit box activated
MyFieldEditor* myTf = (MyFieldEditor*) [[self window] fieldEditor:YES forObject:self];
[myTf setLastEditBox:self];
return [super resignFirstResponder];
}
- (void)textDidEndEditing:(NSNotification *)notification;
{
[super textDidEndEditing:notification];
[self setStringValue:#"RECEIVED ENDEDITING"];
}
#end
int main(int argc, char *argv[])
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSApplication *app = [NSApplication sharedApplication];
NSRect frame = NSMakeRect(100, 100, 200, 150);
// Create the window
NSWindow* window = [[[NSWindow alloc] autorelease ] initWithContentRect:frame styleMask:NSClosableWindowMask|NSResizableWindowMask
backing:NSBackingStoreBuffered defer:NO];
[window setDelegate:[[MyWindowDelegate alloc] autorelease]];
MyTextField * tf = [ [[ MyTextField alloc ] autorelease] initWithFrame: NSMakeRect( 30.0, 100.0, 150.0, 22.0 ) ];
[ [ window contentView ] addSubview: tf ];
MyTextField * tf2 = [ [[ MyTextField alloc ] autorelease] initWithFrame: NSMakeRect( 30.0, 40.0, 150.0, 22.0 ) ];
[ [ window contentView ] addSubview: tf2 ];
[window makeKeyAndOrderFront: window];
[app run];
[pool release];
return 0;
}
You have to do only this
For key Tab
self.textfield.delegate = self;
and then implement this method
- (void)control:(NSControl *)control textView:(NSTextView *)fieldEditor doCommandBySelector:(SEL)commandSelector
{
NSLog(#"Selector method is (%#)", NSStringFromSelector( commandSelector ) );
if (commandSelector == #selector(insertTab:)) {
//Do something against TAB key
//Or Call a Method
}
}
or see my answer at
Execute an Action when the Enter-Key is pressed in a NSTextField?
With the understanding that I mentioned in my other post, I figured out an answer. It's a little convoluted but it works. You have to subclass both the NSTextField and the NSWindow because you need information from both to set this up. Here's the subclasses:
HMTextField.h
#import <Foundation/Foundation.h>
#interface HMTextField : NSTextField {
}
#end
HMTextField.m
#import "HMTextField.h"
#import "HMWindow.h"
#implementation HMTextField
- (BOOL)becomeFirstResponder {
[(HMWindow*)[self window] setTfBecameFirstResponder:YES];
return [super becomeFirstResponder];
}
#end
HMWindow.h
#import <Foundation/Foundation.h>
#interface HMWindow : NSWindow {
BOOL tfIsFirstResponder, tfBecameFirstResponder;
}
#property (nonatomic, readwrite, assign) BOOL tfBecameFirstResponder;
#end
HMWindow.m
#import "HMWindow.h"
#implementation HMWindow
#synthesize tfBecameFirstResponder;
-(id)init {
if (self = [super init]) {
tfIsFirstResponder = NO;
}
return self;
}
- (NSResponder *)firstResponder {
id fr = [super firstResponder];
if ([fr isEqualTo:[self fieldEditor:NO forObject:nil]]) {
tfIsFirstResponder = YES;
} else {
if (tfIsFirstResponder && tfBecameFirstResponder) {
NSLog(#"the text field stopped being first responder");
tfBecameFirstResponder = NO;
}
tfIsFirstResponder = NO;
}
return fr;
}
#end
Make the classes and make your objects their class. You'll be notified of the first responder change from your text field where the NSLog message is in the HMWindow.m file. If you need help understanding how it works let me know.
Here's an example of how to indicate the appropriate time a custom NSTextFieldCell (NSCell) should draw its own bezel & focus ring (in the method [NSTextFieldCell drawWithFrame: inView]), by 'borrowing' the cell's highlight field, setting it when the text field gains focus, and clearing it when the text field loses focus (editing completes).
This technique overcomes some problems:
The cell can't easily determine if it has focus.
The cell can't easily determine which higher level component (e.g. text field or button) it belongs to to track via its parent
NSTextField can instantaneously resign first responder after gaining it, which could make it seem like it lost user focus when it didn't.
Since we're re-purposing the cell's "highlighted" state field, in order to communicate the focus state to the cell, be sure to return nil from the custom NSTextFieldCell's [highlightColorWithFrame: inView:] method.
#import "CustomTextField.h"
#implementation CustomTextField
-(BOOL)becomeFirstResponder {
((NSTextFieldCell *)self.cell).highlighted = true;
return [super becomeFirstResponder];
}
-(void)textDidEndEditing:(NSNotification *)notification {
((NSTextFieldCell *)self.cell).highlighted = false;
[super textDidEndEditing:notification];
}
#end
Complex answers. There is a simpler way to do it.
Don't forget to subclass your NSTextField to NotificableTextField and set its delegate to your view controller.
NotificableTextField.h:
#import <Cocoa/Cocoa.h>
#protocol NotificableTextFieldDelegate <NSObject>
#optional
- (void)textFieldStartedEditing:(NSTextField *)textField;
- (void)textFieldEndedEditing:(NSTextField *)textField;
#end
#interface NotificableTextField : NSTextField
#end
NotificableTextField.m:
#import "NotificableTextField.h"
#implementation NotificableTextField
- (void)awakeFromNib
{
[super awakeFromNib];
self.target = self;
self.action = #selector(inputEnd);
}
- (BOOL)becomeFirstResponder
{
BOOL status = [super becomeFirstResponder];
if (status && [self.delegate respondsToSelector:#selector(textFieldStartedEditing:)])
[(id<NotificableTextFieldDelegate>)self.delegate textFieldStartedEditing:self];
return status;
}
- (void)inputEnd
{
if ([self.delegate respondsToSelector:#selector(textFieldEndedEditing:)])
[(id<NotificableTextFieldDelegate>)self.delegate textFieldEndedEditing:self];
}
#end
NSTextField is a subclass of NSResponder. NSResponder has a method - (BOOL)resignFirstResponder. That will notify you when the NSTextField is no longer first responder... ie. loses focus. So subclass your NSTextField and do your stuff in there.
I created a local event loop and showed up a borderless window (derived from NSPanel),
I found in the event loop there's no NSMouseMoved event received, although I can receive Mouse button down/up events.
What should I do to get the NSMouseMoved events? I found making the float window as key window can receive the NSMouseMoved events, but I don't want to change key window. And it appears this is possible, because I found after clicking the test App Icon in System Dock Bar, I can receive the mousemoved events, and the key window/mainwindow are unchanged.
Here's the my test code: (Create a Cocoa App project names FloatWindowTest, and put a button to link with the onClick: IBAction).
Thanks in advance!
-Jonny
#import "FloatWindowTestAppDelegate.h"
#interface FloatWindow : NSPanel
#end
#interface FloatWindowContentView : NSView
#end
#implementation FloatWindowTestAppDelegate
#synthesize window;
- (void)delayedAction:(id)sender
{
// What does this function do?
// 1. create a float window
// 2. create a local event loop
// 3. print out the events got from nextEventMatchingMask.
// 4. send it to float window.
// What is the problem?
// In local event loop, althrough the event mask has set NSMouseMovedMask
// there's no mouse moved messages received.
//
FloatWindow* floatWindow = [[FloatWindow alloc] init];
NSEvent* event = [NSApp currentEvent];
NSPoint screenOrigin = [[self window] convertBaseToScreen:[event locationInWindow]];
[floatWindow setFrameTopLeftPoint:screenOrigin];
[floatWindow orderFront:nil];
//Making the float window as Key window will work, however
//change active window is not anticipated.
//[floatWindow makeKeyAndOrderFront:nil];
BOOL done = NO;
while (!done)
{
NSAutoreleasePool* pool = [NSAutoreleasePool new];
NSUInteger eventMask = NSLeftMouseDownMask|
NSLeftMouseUpMask|
NSMouseMovedMask|
NSMouseEnteredMask|
NSMouseExitedMask|
NSLeftMouseDraggedMask;
NSEvent* event = [NSApp nextEventMatchingMask:eventMask
untilDate:[NSDate distantFuture]
inMode:NSDefaultRunLoopMode
dequeue:YES];
//why I cannot get NSMouseMoved event??
NSLog(#"new event %#", [event description]);
[floatWindow sendEvent:event];
[pool drain];
}
[floatWindow release];
return;
}
-(IBAction)onClick:(id)sender
{
//Tried to postpone the local event loop
//after return from button's mouse tracking loop.
//but not fixes this problem.
[[NSRunLoop currentRunLoop]
performSelector:#selector(delayedAction:)
target:self
argument:nil
order:0
modes:[NSArray arrayWithObject:NSDefaultRunLoopMode]];
}
#end
#implementation FloatWindow
- (id)init
{
NSRect contentRect = NSMakeRect(200,300,200,300);
self = [super initWithContentRect:contentRect
styleMask:NSTitledWindowMask
backing:NSBackingStoreBuffered
defer:YES];
if (self) {
[self setLevel:NSFloatingWindowLevel];
NSRect frameRect = [self frameRectForContentRect:contentRect];
NSView* view = [[[FloatWindowContentView alloc]
initWithFrame:frameRect] autorelease];
[self setContentView:view];
[self setAcceptsMouseMovedEvents:YES];
[self setIgnoresMouseEvents:NO];
}
return self;
}
- (BOOL)becomesKeyOnlyIfNeeded
{
return YES;
}
- (void)becomeMainWindow
{
NSLog(#"becomeMainWindow");
[super becomeMainWindow];
}
- (void)becomeKeyWindow
{
NSLog(#"becomeKeyWindow");
[super becomeKeyWindow];
}
#end
#implementation FloatWindowContentView
- (BOOL)acceptsFirstMouse:(NSEvent *)theEvent
{
return YES;
}
- (BOOL)acceptsFirstResponder
{
return YES;
}
- (id)initWithFrame:(NSRect)frameRect
{
self = [super initWithFrame:frameRect];
if (self) {
NSTrackingArea* area;
area = [[NSTrackingArea alloc] initWithRect:frameRect
options:NSTrackingActiveAlways|
NSTrackingMouseMoved|
NSTrackingMouseEnteredAndExited
owner:self
userInfo:nil];
[self addTrackingArea:area];
[area release];
}
return self;
}
- (void)drawRect:(NSRect)rect
{
[[NSColor redColor] set];
NSRectFill([self bounds]);
}
- (BOOL)becomeFirstResponder
{
NSLog(#"becomeFirstResponder");
return [super becomeFirstResponder];
}
#end
I think I find the right answer. This is somehow related to tell NSWindow to accept MouseMoved event. but what to my surprise is, The window should accept mouseMoved events isn't the Floating Window, but the current Key window. So the solution is simple, just enable key window to accept mouse moved events before starting tracking, and revert the switch after ending tracking.