PDA

View Full Version : Problem updating NSTextField from another controller object




kpaustin
Mar 14, 2009, 04:38 PM
I have a pretty simple app (at the moment) in development, and I'm having a
problem getting an NSTextField to update. (see code below)

Basically I have a custom view (NSView called buildView) and a text field
(called statusField0) that is contained in a "statusController" in my XIB.
I have connected everything in IB (that I know of). Now, the status
controller that I am intending to use will print out status messages to a
group of NSTextFields at the bottom of the window. statusController has
all the IBOutlets to these fields. buildView is intended to control (or
"intercept" if you will) Mouse and Key clicks in the custom buildView NSView,
and draw things in the window...but it will also need to update some status
info in the NSTextFields controlled by statusController.

The problem is that, on a keyboard click, the NSView correctly updates
itself (it redraws - right now it just removes some images from itself) but
I can't figure out a mechanism to update the NSTextField in statusController.

The sequence of events, taken from NSLog Console messages, is:
2009-03-14 16:07:56.568 myApp[1083:10b] SC init
2009-03-14 16:07:56.569 myApp[1083:10b] SC init done
2009-03-14 16:07:56.570 myApp[1083:10b] SC init
2009-03-14 16:07:56.571 myApp[1083:10b] SC init done
2009-03-14 16:07:56.575 myApp[1083:10b] SC init
2009-03-14 16:07:56.576 myApp[1083:10b] SC init done
2009-03-14 16:07:56.578 myApp[1083:10b] SC AwakeFromNIB <- statusField0 has "Initial String"
2009-03-14 16:07:56.603 myApp[1083:10b] Accept bV
2009-03-14 16:07:56.603 myApp[1083:10b] Accept bV <- buildView accepts first responder
2009-03-14 16:07:56.620 myApp[1083:10b] buildView drawRect
2009-03-14 16:07:57.576 myApp[1083:10b] buildView drawRect <- draws splash graphics/logos fine
2009-03-14 16:08:00.485 myApp[1083:10b] Key 0x05 (5) Pressed <- keyDown in buildview calls [statCont buildViewKeyDown:@"FromBuildView"];
2009-03-14 16:08:00.485 myApp[1083:10b] buildViewKeyDown In <- NSLog proves it's being called
2009-03-14 16:08:00.485 myApp[1083:10b] buildViewKeyDown Out <- statusField0 is NOT updated, still "Initial String" ??????????
2009-03-14 16:08:00.486 myApp[1083:10b] buildView drawRect <- splash images disappear correctly
2009-03-14 16:08:03.209 myApp[1083:10b] Pushed <- statusController button pushed [self buildViewKeyDown:@"Pushed"];
2009-03-14 16:08:03.210 myApp[1083:10b] buildViewKeyDown In <- NS Log proves it's being called
2009-03-14 16:08:03.210 myApp[1083:10b] buildViewKeyDown Out <- statusField0 IS updated to "Pushed"

Now I have tried everything I can think of...a static String
in buildViewKeyDown, copying the string...bindings. Nothing
seems to allow me to update the NSTextField when the event
is called from "over the wall". How do I cause it to update.
No amount of setNeedsDisplay seems to work, either. And I've
also tried resignFirstResponder tactics.

I also have NSObjects of buildView and statusController class
in the XIB...are there direct connections there that I need.
Note that all the connections to the IBOutlets, pushbuttons etc.
are there and seem to work when the event comes from it's own
controller.

Please help, this is driving me crazy. I'm porting a card game
from Windows 32 API world and it's a very different mindset
over here already. I promise you'll enjoy the game if you help
me get it working. :) I have left the relevant portions of the
code in the listing below. Please don't hesitate with requests
for addtional information. Thanks in advance!

==========================================
statusController.h:
==========================================
#import <Cocoa/Cocoa.h>

@interface statusController : NSObject {
IBOutlet NSTextField *statusField0;
NSString *strStat0;
}

- (void) buildViewKeyDown:(NSString *)bstr;
- (IBAction)pushed:(id)sender;

@end

==========================================
statusController.m:
==========================================
#import "statusController.h"

@implementation statusController

//========================================
// awaken
//========================================
- (void)awakeFromNib {
NSLog(@"SC AwakeFromNIB");
NSFont* font = [NSFont fontWithName:@"Arial" size:11.0];
[statusField0 setFont:font];
[statusField0 setStringValue:strStat0];
}

//========================================
// called from statusContoller.pushed - works
// called from buildView.keyDown - DOESN'T work
//========================================
- (void) buildViewKeyDown:(NSString *)bstr {
NSLog(@"buildViewKeyDown In");
[statusField0 setStringValue:bstr];
[statusField0 setNeedsDisplay:YES];
NSLog(@"buildViewKeyDown Out");
}

//========================================
// button pushed from SC
//========================================
- (IBAction)pushed:(id)sender {
NSLog(@"Pushed");
[self buildViewKeyDown:@"Pushed"];
}

//========================================
// SC init
//========================================
- (id) init
{
[super init];
NSLog(@"SC init");
strStat0 = [[NSString alloc] initWithString:@"Initial String"];
NSLog(@"SC init done");
return self;
}

@end

==========================================
buildView.h:
==========================================
#import <Cocoa/Cocoa.h>

@class statusController;

@interface buildView : NSView {
NSImage *logoImage;
NSImage *copyRightImage;
NSTimer *copyTimer;
int showCopyRight;
int showLogo;
statusController *statCont;
}
- (void)startCopyTimer;
- (void)handleCopyTimer:(NSTimer *)aTimer;

@end

#import "buildView.h"
#import "statusController.h"

==========================================
buildView.m
==========================================
@implementation buildView

-(BOOL) acceptsFirstResponder {
NSLog(@"Accept bV");
return YES;
}

//========================================
// handleKeyboard
//========================================
-(void)keyDown:(NSEvent *)anEvent {
unsigned short kc = [anEvent keyCode];
NSLog(@"Key 0x%02x (%d) Pressed", kc, kc);
showCopyRight = 0;
showLogo = 0;
[statCont buildViewKeyDown:@"FromBuildView"];
[self setNeedsDisplay:YES];
}

//========================================
// initWithFrame - load all the images we need initially
//========================================
- (id)initWithFrame:(NSRect)frame {
self = [super initWithFrame:frame];
if (self) {
// load some images I need later
}
if (!statCont) {
statCont = [[statusController alloc] init];
}
return self;
}

//========================================
// drawRect
//========================================
- (void)drawRect:(NSRect)rect {
NSLog(@"buildView drawRect");
NSRect bounds = [self bounds];
//=====================================
// set the background to Green
//=====================================
[[NSColor colorWithDeviceRed:0.282 green:0.498 blue:0.118 alpha:1.0] set];
[NSBezierPath fillRect:bounds];

...

}
@end



kainjow
Mar 14, 2009, 05:35 PM
I think the problem is in buildView's initWithFrame: you're creating a new instance of statusController, but I'm guessing that statusController is created in the nib (since it's a subclass of NSObject and you don't load the nib manually). I believe you need buildView to reference the existing statusController object, instead of creating a new one, which you can do by declaring and connecting an IBOutlet to it.

A quick way to test this is by NSLogging self in buildViewKeyDown, and you will see different values if there are 2 instances of statusController.
NSLog(@"buildViewKeyDown In (%@)", self);

kpaustin
Mar 14, 2009, 06:05 PM
OK, one step closer...you're correct, when I added the NSLog above it showed that there were two instances of statusController. However, after I add the IBOutlet to buildView.h and make the connection in IB, the function buildViewKeyDown is no longer being called on the keyboard event. I'm still doing the:
[statCont buildViewKeyDown:@"FromBuildView"];

and in buildView.h in have:
IBOutlet statusController *statCont;

Both objects in the XIB are instantiated and the classes are set accordingly. It shows statusController as an IBOutlet of buildView, and buildView as a referencing outlet in statusController.

I just added:
if (statCont) {
NSLog(@"StatCont exists from buildView");
} else {
NSLog(@"StatCont does not exist from buildView");
}
in buildView.keyDown and it prints "StatCont does not exist ...".

Is there some other connection/instantiation/init that I have to do
for the statusController to show up and be available to buildView?

EDIT: Also I removed the init:
//if (!statCont) {
// statCont = [[statusController alloc] init];
// }
from buildView's initWithFrame

Thanks again!

kpaustin
Mar 14, 2009, 07:14 PM
OK, so I figured something out, but I don't know what's going on...I have a custom NSView that I drag from the library onto my XIB GUI window. Then I drag an NSObject into the XIB classes window. I set it's class to buildView. In the object window (with the blue cube) I set all the outlets and everything. But when I right-click on the custom buildView in the GUI, it shows that my outlet sCont has no connection. So I make that connection, and now the update to the NSTextField in my statusController works! So what did I miss in connecting the GUI representation of my Custom NSView to the blue cube representation in the classes window? It says buildView on the GUI window. The classes window changes the name underneath the icon to "Build View" and "Status Controller", which I don't like. But the Outlet connections and everything are hooked up. I don't like the integration of IB and Xcode so far, but maybe I'll get used to it. The ultimate goal will be to create the XIB by hand instead of all this Control-Click-Mouse-Drag connect stuff. Anyway, if anyone knows what I'm doing wrong in creating my GUI, I'd appreciate it. -ken