mouseDown not firing properly on NSTextField - objective-c

I tried implementing the second answer posted in this post here. I have the desire as the person asking the question however my mouseDown is not working/registering. Here is what I have.
AppDelegate.h
AppDelegate.m
MouseDownTextField.h
MouseDownTextField.m
and there relavent content:
AppDelegate.h
#import <Cocoa/Cocoa.h>
#import "MouseDownTextField.h"
#interface AppDelegate : NSObject <MouseDownTextFieldDelegate> {
NSWindow *window;
IBOutlet NSMenu *statusMenu;
NSStatusItem *statusItem;
NSMutableArray *selector;
NSMutableArray *display;
NSTimer *timer;
MouseDownTextField *quoteHolder; }
#property IBOutlet MouseDownTextField *quoteHolder;
#end
AppDelegate.m
- (void)displayString:(NSString *)title {
NSRect frame = NSMakeRect(50, 0, 200, 17);
quoteHolder = [[MouseDownTextField alloc] initWithFrame:frame];
[[self quoteHolder] setDelegate:self];
[quoteHolder setStringValue:title];
[quoteHolder setTextColor:[NSColor blueColor]];
[test addSubview:quoteHolder];
[statusItem setView:test]; }
-(void)mouseDownTextFieldClicked:(MouseDownTextField *)textField {
NSLog(#"Clicked");}
MouseDownTextField.h
#import <Appkit/Appkit.h>
#class MouseDownTextField;
#protocol MouseDownTextFieldDelegate <NSTextFieldDelegate>
-(void) mouseDownTextFieldClicked:(MouseDownTextField *)textField;
#end
#interface MouseDownTextField: NSTextField {
}
#property(assign) id<MouseDownTextFieldDelegate> delegate;
#end
MouseDownTextField.m
#import "MouseDownTextField.h"
#implementation MouseDownTextField
-(void)mouseDown:(NSEvent *)event {
[self.delegate mouseDownTextFieldClicked:self]; }
-(void)setDelegate:(id<MouseDownTextFieldDelegate>)delegate {
[super setDelegate:delegate]; }
-(id)delegate {
return [super delegate]; }
#end
Thoughts on what could be wrong or what i have done wrong?

You are creating quoteHolder in IB, you should remove the following line of code and you should be fine.
quoteHolder = [[MouseDownTextField alloc] initWithFrame:frame];
The result of reassigning the NSTextField is that the one you are clicking is no longer the one registered with the delegate. No need to add it as a subview either, it's already been added to the view hierarchy in IB.
Also, make sure in IB, under Accessibility, "User Interaction Enabled" is checked for the NSTextField.
As for the follow up quesion, how could you have multiple of these?
If you were adding multiple NSTextField instances in IB, each would be referenced as a #property just as you did with quoteHolder. The linkage is done in IB like this linked answer.
These could all have the same delegate. When mouseDownTextFieldClicked: is pressed you could interrogate the NSTextField for a unique id which could be assigned in IB as well. Hope this helps.

Related

How to correctly implement a NSWindowController subclass with xib-file

I am trying to implement a NSWindowController subclass with new xib-file, I read up in lots of books, and researched on StackOverflow, but none of the steps provided made my window show, nor did the subclass code get executed. The new xib-file has its File's Owner set to "LogNavigatorController" and connections to the window and its contents have been made.
My AppDelegate.h:
#import <Cocoa/Cocoa.h>
#class LogNavigatorWindowController;
#interface AppDelegate : NSObject <NSApplicationDelegate>
{
LogNavigatorWindowController *logsWindowController;
}
#end
My AppDelegate.m:
#import "AppDelegate.h"
#import "LogNavigatorWindowController.h"
#implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
// Insert code here to initialize your application
logsWindowController = [[LogNavigatorWindowController alloc] initWithWindowNibName:#"LogNavigatorWindowController"];
[logsWindowController showWindow:self];
}
#end
My LogNavigatorWindowController.h:
#import <Cocoa/Cocoa.h>
#interface LogNavigatorWindowController : NSWindowController
{
NSArray *directoryList1;
NSArray *directoryList2;
NSMutableArray *directoryList;
NSMutableArray *filePaths1;
NSMutableArray *filePaths2;
}
#property (assign) IBOutlet NSWindow *window;
#property (weak) IBOutlet NSTableView *logsTableView;
#property (unsafe_unretained) IBOutlet NSTextView *logsTextView;
#property (assign) IBOutlet NSArrayController *LogListController;
#property (retain) NSMutableArray *logsArray;
- (void) myDirectoryLogFunction;
#end
My LogNavigatorController.m:
#import "LogNavigatorWindowController.h"
#interface LogNavigatorWindowController ()
#end
#implementation LogNavigatorWindowController
#synthesize logsTableView;
#synthesize logsTextView;
#synthesize window;
- (id)init
{
self = [super initWithWindowNibName:#"LogNavigatorWindowController"];
[self loadWindow];
[self showWindow:#"Log Navigator"];
[self.window makeKeyAndOrderFront:nil];
if (self)
{
// Initialization code here.
[self myDirectoryLogFunction];
}
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.
}
- (void) myDirectoryLogFunction
{
NSLog(#"Code execution test successful");
}
#end
You don't need to create the window property since it is already available for NSWindowController subclasses. Maybe that causes the problem.
Also your init method contains a lot of code that doesn't belong there. Remove
[self loadWindow];
[self showWindow:#"Log Navigator"];
[self.window makeKeyAndOrderFront:nil];
as well as replace
self = [super initWithWindowNibName:#"LogNavigatorWindowController"];
with
self = [super init];
You may want to remove the init method at all, since you don't need it in your case.
and move
[self myDirectoryLogFunction];
to the windowDidLoad method.
Also always check that the code for instantiating the window controller (in your case from the app delegates didFinishLaunching: ) is called. Sometimes it helps to create a new project and test there, if you may have changed too much within the original project and by accident removed delegate connections or similar.

Using protocol to trigger a segue from a UIView inside a UIViewcontroller

CONFIGURATION:
-UIviewController embedded in Navigationcontroller.
-UIviewController has a UIscrollview as subview
-UIscrollview has some views where charts are created: each view containing a chart has its own .h and .m file and from this file I want trigger a segue to a tableview controller.
-A Tableviewcontroller was added in xcode and a segue from the UIviewController to the TableViewcontroller was created as well (Xcode)
-created a protocol in the UIView to have the segue pushed from there.
PROBLEM:
delegate always nil, segue method will never be called
UIVIEW .h file
#protocol UItoUIvcDelegate <NSObject>
-(void)triggerSegue;
#end
#interface CFfirstGraph : UIView <CPTPlotDataSource , CPTPieChartDelegate,UIActionSheetDelegate>
#property(weak, nonatomic) id <UItoUIvcDelegate> delegate;
#end
UIVIEW .m file (snippet)
-(void)pieChart:(CPTPieChart *)pieChart sliceWasSelectedAtRecordIndex:(NSUInteger)index
{
if (self.delegate == nil)
{
NSLog(#"nil");
}
[self.delegate triggerSegue];
}
UIVIEWCONTROLLER .h file
#import "CFfirstGraph.h"
#interface CFMainViewController : UIViewController <UItoUIvcDelegate, UITableViewDelegate, UITableViewDataSource>
#end
UIVIEWCONTROLLER .m file (snippet)
- (void)viewDidLoad
{
[super viewDidLoad];
self.scrollView.contentSize = CGSizeMake(320, 1000);
[self.view addSubview:self.scrollView];
CFfirstGraph *click =[[CFfirstGraph alloc]init];
click.delegate = self ;
}
-(void)triggerSegue
{
[self performSegueWithIdentifier:#"detailedData" sender:self];
NSLog(#"estoy aqui");
}
What am I doing wrong ? why the delegate is always nil ? I tried to add the method setDelegate but still no luck.
Thanks,
dom
Make CFfirstGraph as a strong property.
#property (strong, nonatomic) CFfirstGraph * click;
- (void)viewDidLoad
{
[super viewDidLoad];
self.click =[[CFfirstGraph alloc]init];
self.click.delegate = self ;
self.scrollView.contentSize = CGSizeMake(320, 1000);
[self.view addSubview:self.scrollView];
}
ok, after many hours of sweat I found the issue.
First...delegate = nil was not the main problem.
The real issue was the protocol method triggering the segue was never called.
If i create and initialize a CFfirstGraph object (or even property) it won't be related to the view created already in x-code, and this is the main issue.
On the other hand...if I "CTRL-drag" an outlet from the UIview to the CFMainViewController i will have a property that is exactly the one i need:
#interface CFMainViewController () <UIScrollViewDelegate>
#property (weak, nonatomic) IBOutlet CFfirstGraph *FirstGraph;
Then i assign the delegate to self (CFMainViewController) in the viewDidLoad:
- (void)viewDidLoad
{
[super viewDidLoad];
self.FirstGraph.delegate = self ;
}
and the delegate method "triggerSegue" will be executed being called from the UIVIEW.
Best Regards,
dom

obj-c : Cant change subView of viewcontroller with Notification

I'm confused about below situation.
I have a viewcontroller(VC), it has 1 subview(SubV) and 1 other class.(classA)
Also i have an event handler called from classA, i want this event handler to change my subV in VC.
When i access SubV from VC directly, it is OK, image of subview changed etc.
But when the classA triggers an event handler of VC, it reaches VC, also access subView's method but no change in my subView !!! (I also try delegate but the result is same)
ViewController.h
#interface ViewController : UIViewController {
.
IBOutlet SubView *subView;
ClassA *classA;
.
}
#property (retain, nonatomic) IBOutlet SubView *subView;
#property (retain, nonatomic) ClassA *classA;
ViewController.m
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.subView = [self.subView init];
self.classA = [[ClassA alloc] init];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(eventListener:) name:#"eventType" object:nil];
}
- (void) eventListener:(NSNotification *) not
{
[self.subView RefreshView]; // it doesnt work! calls refreshView method but no change
}
- (IBAction)buttonPressed:(id)sender
{
[self.subView RefreshView]; // it works perfect
}
SubView.h
#interface SubView : UIImageView
#property int state;
#property NSArray *imageArray;
- (void) RefreshView;
- (id) init;
#end
SubView.m
- (void) RefreshView{
[self stopAnimating];
self.imageArray = [NSArray arrayWithObjects:[UIImage imageNamed:#"a.png"], nil];
self.animationDuration = 1;
self.animationImages = self.imageArray;
self.animationRepeatCount = 0;
[self startAnimating];
}
ClassA.m
-(void)methodA{
[myEvent requestEvent];
}
So, what i am trying to do here is accessing & changing subView with a button in Viewcontroller and with a thread running in another classA
Edit: Not quite sure what to make of this. You've edited your post to the changes I proposed, making my answer look superfluous at best and deranged at worst...
You have created the class SubView as a subclass of UIImageView. But the IBOutlet *subView is not a member of class SubView but a member of UIImageView. I suspect this might carry over to the xib/storyboard as well(?) If so, this means that any messages you send to your subView instance will be handled by a stock UIImageView rather than your own class...
#property (retain, nonatomic) IBOutlet UIImageView *subView;
should probably read
#property (retain, nonatomic) IBOutlet SubView *subView;
Finally i've found the solution! Because of the thread that i run in ClassA, i should use '
[self performSelectorOnMainThread:#selector(myMethodToRefreshSubView) withObject:nil waitUntilDone:NO];
in my eventListener method.

Objective-C: Calling class 2 instance from class1 [alloc init] way is not working

I've got the following method on a GameScreen.m file, with its own declaration - (void) drawNumbers on a GameScreen.h file:
//GameScreen.h
#import <UIKit/UIKit.h>
#interface GameScreen : UIView
{
IBOutlet UIButton *cell00;
}
- (void) drawNumbers;
- (IBAction) onCellClick:(id)sender;
#property (nonatomic, retain) IBOutlet UIButton *cell00;
#end
//GameScreen.m
#import "GameScreen.h"
- (void) drawNumbers
{
//testing if this works, so far it doesn't
[cell00 setTitle:#"Whatever" forState:UIControlStateNormal];
[cell00 setTitle:#"Whatever" forState:UIControlStateHighlighted];
}
I'm trying to call this method from my GameScreenViewController.m file, this way:
//GameScreenViewController.m
#import "GameScreenViewController.h"
#import "GameScreen.h"
...
- (void) viewDidLoad
{
GameScreen *aGameScreen = [[GameScreen alloc] init];
[aGameScreen drawNumbers];
[aGameScreen release];
[super viewDidLoad];
}
This is supposed to change the title of a button in a GameScreen.xib file where GameScreenViewController.m is the viewController and GameScreen class is the event handler where I get all the button clicks, timers running, etc. I am trying to call [drawNumbers] from [viewDidLoad] since I want the title to be changed when the screen is brought up front (screen management is done through the AppDelegate files).
The thing is, if I call drawNumbers instance from inside the same class through
//GameScreen.m
#import GameScreen.h
-(void) onButtonClick:(id)sender
{
//some other code
[self drawNumbers];
}
it works (as to say, nothing wrong with the code implementation or the graphic interface).
I've browsed through Apple Guide and tons of pages on the Internet, but I can't seem to find any light to this. Any further help (including answers as to where exactly find the answer in the ADG) would be really appreciated.
(Edited: here goes the AppDelegate code to flip to the specific view, just in case):
//myAppAppDelegate.h
#import <UIKit/UIKit.h>
#class myAppViewController, GameScreenViewController;
#interface myAppDelegate : NSObject <UIApplicationDelegate>
{
UIWindow *window;
myAppViewController *viewController;
GameScreenViewController *gameScreenViewController;
}
- (void) flipToGameScreen;
#property (nonatomic, retain) UIWindow *window;
#property (nonatomic, retain) GameScreenViewController *gameScreenViewController;
#end
//myAppAppDelegate.m
-(void) flipToGameScreen
{
GameScreenViewController *aGameScreenView = [[GameScreenViewController alloc] initWithNibName: #"GameScreen" bundle:nil];
[self setGameScreenViewController:aGameScreenView];
[aGameScreenView release];
[gameScreenViewController.view.frame = [[UIScreen mainScreen] applicationFrame];
[viewController.view removeFromSuperview];
[self.window addSubview:[gameScreenViewController view]];
}
Since your cell00 is to be set by a NIB it will be nil if you simply do [[GameScreen alloc] init]. It will only be set if the corresponding NIB is loaded (and a connection is actually set up).
If the cell can be accessed in your viewDidLoad, create a property on GameScreen and pass it through the property (or a dedicated initWithCell: or something).
If you have something like an IBOutlet GameScreen *aGameScreen; on your GameScreenViewController (and also established a connection to cell00 in the same NIB) you should access that instead.

Programmatically changing a UILabel from the App Controller in a Navigation Based iOS App

I'm having a lot of trouble with what seems like a very simple thing. I cannot update a UILabel programmatically from a Navigation-based iOS App. I don't want to use a button as this label is designed to report the status of an external system, and should update on launch. There is no need to make the user go though the extra step on touching the button if I don't have to.
The following is a somewhat exhaustive list of the steps I've taken. I'm sorry if some of this seems unnecessary, but in my experience even the smallest forgotten step can be the cause of the issue.
From a fresh Navigation-based App in Xcode here are the steps I'm taking:
Replace UITableView with a generic UIView class
Re-wire File's Owner's view outlet to the new UIView
Add a UILabel to the center of the UIView, make the text centered, and leave the default text.
Save and Exit Interface Builder
RootViewController.h
#import <UIKit>
#interface RootViewController : UIViewController {
UILabel *myLabel;
}
#property (nonatomic, retain) IBOutlet UILabel *myLabel;
#end
RootViewController.m
#import "RootViewController.h"
#implementation RootViewController
#synthesize myLabel;
...
Removed TableView stuff from RootViewController.m
Wire IBOutlet myLabel to the Label in RootViewController.xib
Save and Exit Interface Builder
tempNavAppAppDelegate.m
...
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
// Add the navigation controller's view to the window and display.
[self.window addSubview:navigationController.view];
[self.window makeKeyAndVisible];
RootViewController *rootViewCont = navigationController.visibleViewController;
rootViewCont.myLabel.text = #"test";
NSLog(#"Label Text: %#", rootViewCont.myLabel.text);
return YES;
}
...
Build/Run
The Label shows as "Label" not "test". And the log reports:tempNavApp[94186:207] Label Text: (null)
I've tried a number of different ways to get this done, but any help would be appreciated.
The Journey
After discovering that my rootViewCont.myLabel was also nil, thanks to the help of mprudhom, I decided to test and see if I could assign myLabel.text a value in RootViewController.m's - (void)viewDidLoad method.
It worked, I was able to change the text directly from the RootViewController. But while this proved my View Controller wasn't broken, it did not solve my initial desire to change the UILabel from tempNavAppAppDelegate.m.
Elliot H. then suggested that navigationController.visibleViewController wasn't actually returning a view controller. I had tested for the value of rootViewCont and it came back as a RootViewController, but Elliot's suggestion got me thinking about the app's lifecycle and when the different parts of my code was actually loaded up.
So I started printing an NSLog at each step of the launch process (application:didFinishLaunchingWithOptions:, applicationDidBecomeActive:, viewDidLoad, viewDidAppear:), and discovered to my surprise that [self.window makeKeyAndVisible]; does not mean that the view will load before application:didFinishLaunchingWithOptions: is complete.
With that knowledge in hand I knew where the problem was. The solution (or at least my solution) seems to be NSNotificationCenter. I have now registered for notifications in tempNavAppAppDelegate and I am broadcasting a notification in RootViewController's viewDidAppear: method.
The Pertinent Code
RootViewController.h:
#interface RootViewController : UIViewController {
IBOutlet UILabel *myLabel;
}
#property (nonatomic, retain) UILabel *myLabel;
#end
RootViewController.m:
#implementation RootViewController
#synthesize myLabel;
- (void)viewDidLoad {
[super viewDidLoad];
NSParameterAssert(self.myLabel);
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[[NSNotificationCenter defaultCenter] postNotificationName:#"viewDidAppear" object:self];
}
tempNavAppAppDelegate.h:
#interface tempNavAppAppDelegate : NSObject {
UIWindow *window;
UINavigationController *navigationController;
}
#property (nonatomic, retain) IBOutlet UIWindow *window;
#property (nonatomic, retain) IBOutlet UINavigationController *navigationController;
- (void)viewDidAppearNotification:(id)notification;
#end
tempNavAppAppDelegate.m:
#implementation tempNavAppAppDelegate
#synthesize window;
#synthesize navigationController;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[self.window addSubview:navigationController.view];
[self.window makeKeyAndVisible];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(viewDidAppearNotification:) name:#"viewDidAppear" object:nil];
return YES;
}
- (void)viewDidAppearNotification:(id)notification
{
NSString *noteClass = [NSString stringWithFormat:#"%#", [[notification object] class]];
if ([noteClass isEqualToString:#"RootViewController"]) {
RootViewController *noteObject = [notification object];
noteObject.myLabel.text = #"Success!";
}
}
If this code is printing nil:
rootViewCont.myLabel.text = #"test";
NSLog(#"Label Text: %#", rootViewCont.myLabel.text);
Then almost certainly it is because rootViewCont.myLabel itself is nil. Try logging the value of rootViewCont.myLabel as well and you'll see.
Are you sure you wired up the label to your UILabel IBOutput declaration in Interface Builder? That's most commonly the problem.
I personally always assert all my expected outlets in viewDidLoad so that I catch early on when the outlets have been (accidentally or not) been decoupled in Interface Builder. E.g.:
- (void)viewDidLoad {
[super viewDidLoad];
NSParameterAssert(rootViewCont.myLabel);
}
your interface should look like this
#import <UIKit>
#interface RootViewController : UIViewController {
// IBOutlet here...
IBOutlet UILabel *myLabel;
}
#property (nonatomic, retain) UILabel *myLabel;
#end
Is visibleViewController actually returning the view controller? My guess is since application:didFinishLaunchingWithOptions: hasn't returned yet, it's possible UINavigationController hasn't properly configured that property to return yet, even though you've added the navigation controller's subview to the view hierarchy, it's probably that visibleViewController isn't valid until after viewDidAppear: is called on the view controller in question.
Try having an IBOutlet to the RootViewController directly, or create it programmatically, and then assign the label text.
Just a general reminder: If an object is nil (in this case visibleViewController would be returning nil), and you send it a message, you won't crash, because messages to nil are valid and won't do anything. When you call the myLabel accessor on the rootViewCont object, if rootViewCont is nil, myLabel will return nil always.