What is the proper way to declare a delegate variable? - cocoa

In MyModel.h, I declared a delegate variable like this:
#property(weak) IBOutlet id <MyProtocol> delegate;
I've also seen a delegate variable declared like this:
#property(weak) IBOutlet NSObject <MyProtocol>* delegate;
I'm wondering which I should use.
Also, Xcode 6.2 indicates I'm doing something wrong because when I connect the delegate outlet in IB, Xcode still shows an empty circle to the left of the declaration instead of a filled in circle. This is what I did:
1) In IB, I dragged on Object out of the Library onto the dock, and I changed its class to: MyModel.
2) In IB, I dragged another Object onto the dock, and I changed its class to: MyController. I declared the MyController class like this:
#interface MyController : NSObject <MyProtocol>
#property(strong) IBOutlet MyModel* model;
#end
3) In IB, I hooked up the delegate outlet for the MyModel Object to the MyController Object.
But in Xcode, it still shows an empty circle to the left of the line:
#property(weak) IBOutlet id <MyProtocol> delegate;
In other words, Xcode is saying the outlet is not connected to anything--yet my app is able to communicate with the controller using the delegate property.
If I delete <MyProtocol> from that line, the circle to the left of the line fills in, i.e. Xcode is saying the outlet is now connected to something. Is that an Xcode bug?
Here are the files for my HelloDelegate app:
MyProtocol.h:
//
// MyProtocol.h
// HelloDelegate
//
#class MyModel; //#import "MyModel.h" doesn't work for some reason
#protocol MyProtocol
-(void)sayHello:(MyModel*)model;
#end
MyModel.h:
//
// MyModel.h
// HelloDelegate
//
#import <Foundation/Foundation.h>
#import "MyController.h"
#interface MyModel : NSObject
#property NSString* name;
-(id)initWithName:(NSString*)name;
-(void)doStuff;
#end
MyModel.m:
//
// MyModel.m
// HelloDelegate
//
#import "MyModel.h"
#interface MyModel()
#property(weak) IBOutlet id <MyProtocol> delegate;
#end
#implementation MyModel
-(void)doStuff {
[[self delegate] sayHello:self];
}
-(id) init {
return [self initWithName:#"world"];
}
//Designated initializer:
-(id) initWithName:(NSString *)name {
if (self = [super init]) {
[self setName:name];
}
return self;
}
#end
MyController.h:
//
// MyController.h
// HelloDelegate
//
#import <Foundation/Foundation.h>
#import "MyProtocol.h"
#interface MyController : NSObject <MyProtocol>
#property(strong) IBOutlet MyModel* model;
#end
MyController.m:
//
// MyController.m
// HelloDelegate
//
#import "MyController.h"
#import "MyModel.h"
#import <Cocoa/Cocoa.h>
#interface MyController()
#property(weak) IBOutlet NSTextField* label;
#end
#implementation MyController
-(void)sayHello:(MyModel*)model {
NSString* labelText = [NSString stringWithFormat:#"Hello, %#!", [model name]];
[[self label] setStringValue:labelText];
}
#end
AppDelegate.m:
//
// AppDelegate.m
// HelloDelegate
//
#import "AppDelegate.h"
#import "MyController.h"
#import "MyModel.h"
#interface AppDelegate ()
#property (weak) IBOutlet NSWindow *window;
#property (strong) IBOutlet MyController* controller;
#end
#implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
// Insert code here to initialize your application
[[[self controller] model] doStuff];
}
- (void)applicationWillTerminate:(NSNotification *)aNotification {
// Insert code here to tear down your application
}
#end

The main difference comes along when you type something as id <SomeProtocol> and then try to send it a message such as respondsToSelector: and the compiler won't let you. It comes as a surprise - or at least it sure came as a surprise to me - that id <SomeProtocol> is not a form of id. The only messages you can send to such a beast without casting are those defined in the protocol. That's in stark contrast to id plain and simple, which can be sent any known message.
Thus, in my view, as in that of those who know better than I, NSObject <SomeProtocol>* is better, because now this thing is seen by the compiler as an NSObject, and can be sent all the messages declared for NSObject.

Related

Cocoa NSTextField

i have a problem with accessing value from the NSTExtField in different class here is the code:
AppDelegate.h
#import <Cocoa/Cocoa.h>
#interface AppDelegate : NSObject <NSApplicationDelegate>
#property (assign) IBOutlet NSWindow *window;
#property (weak) IBOutlet NSTextField *numberOfPhrases;
#end
AppDelegate.m
#import "AppDelegate.h"
#implementation AppDelegate
#synthesize numberOfPhrases;
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
NSLog(#"%#",[numberOfPhrases stringValue]);
}
TestClass.h
#interface TestClass : NSObject
- (IBAction)doSomething:(id)sender;
#end
TestClass.m
#implementation TestClass
- (IBAction)doSomething:(id)sender {
NSLog(#"%#",[numberOfPhrases stringValue]); ?????????
}
You obviously can't access the text field value in another class without a link to it.
To access the text field's value you either need to have one more IBOutlet to it in this class or an IBOutlet to AppDelegate so that you can access its property.
TestClass.h
#interface TestClass : NSObject
{
IBOutlet NSTextField *numberOfPhrases; // connect it to the new referencing outlet of text field by dragging a NSObject object in your xib and setting its class to "TestClass"
}
- (IBAction)doSomething:(id)sender;
#end
OR an another option is to have a IBOutlet of AppDelegate in TestClass (because if you only create a new instance of AppDelegate and not its IBOutlet then a different instance of text field will be created and you will not be able to access the value of your text field)
TestClass.h
#interface TestClass : NSObject
{
IBOutlet AppDelegate *appDel; // connect in the xib
}
- (IBAction)doSomething:(id)sender;
#end
TestClass.m
#implementation TestClass : NSObject
- (IBAction)doSomething:(id)sender
{
[[appDel numberOfPhrases]stringValue]; //get the string value in text field
}
#end
The only thing you're missing is the addition to your TestClass.m file:
#import "TestClass.h"
#import "AppDelegate.h"
#implementation TestClass
- (IBAction)doSomething:(id)sender {
AppDelegate *theInstance = [[AppDelegate alloc] init];
[theInstance numberOfPhrases];
}
#end
You need to include the class header of AppDelegate.h in TestClass.m, then you simply call an instance through [[AppDelegate alloc] init]; You'll need to link your NSTextField to the Sent Actions in Interface Builder do:Something -> TestClass and Referencing Outlets numberOfPhrases -> AppDelegate.
Output:
2014-01-21 23:32:56.499 test[6236:303] Wonders Never Cease

Switching back and forth between windows XCode interfaces

I am having a very difficult time switching between forms in Cocoa interfaces. From my initial form and its delegate, I can hide the initial window then load and display the second window with all properties on it. This is working working... Alas, on attempting to return to the initial window, I hide the second window and the initial does not return...
Here are my .h and .m for initial form and for formTwo...
.h
#import <Cocoa/Cocoa.h>
#class frmTwoDelegate;
#interface AppDelegate : NSObject {
#private
frmTwoDelegate *_frmTwo;
}
#property (assign) IBOutlet NSWindow *window;
- (IBAction)BtnSwitchAction:(id)sender;
#end
.m
#import "AppDelegate.h"
#import "frmTwoDelegate.h"
#implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
...
}
- (IBAction)BtnSwitchAction:(id)sender {
if (!_frmTwo) {
_frmTwo = [[DecriptDelegate alloc] initWithWindowNibName:#"frmTwo"];
[_frmTwo setFrmStart:self];
}
[_frmTwo showWindow:sender];
[_window setIsVisible:NO];
}
#end
Here are .h and .m for frmTwo
.h
#import <Cocoa/Cocoa.h>
#class AppDelegate;
#interface frmTwo : NSWindowController{
#private
AppDelegate *frmStart;
__unsafe_unretained NSTextView *_TxtView;
}
#property (retain) AppDelegate *frmStart;
#property (assign) IBOutlet NSWindow *frmTwo;
#property (unsafe_unretained) IBOutlet NSTextView *TxtView;
- (IBAction)BtnOpenActionPreformed:(id)sender;
- (IBAction)BtnBackActionPreformed:(id)sender;
#end
.m
#import "frmTwo.h"
#import "AppDelegate.h"
#implementation frmTwo
#synthesize frmStart;
- (id)initWithWindow:(NSWindow *)window
{
...
}
- (void)windowDidLoad
{
...
}
- (IBAction)BtnOpenActionPreformed:(id)sender
{
...
}
- (IBAction)BtnBackActionPreformed:(id)sender {
[frmStart ShowWindow];
[_frmTwo setIsVisible:NO];
}
#end
Here's a simpler way to achieve what you are doing. I'm not going to write the .h definitions, just infer what the variables represent from their names.
- (IBAction)BtnSwitchAction:(id)sender {
if (!_formTwo) {
_formTwo = [[DecriptDelegate alloc] initWithWindowNibName:#"frmTwo"];
[_formTwo setFrmStart:self];
}
if(_formOne.isVisible) {
[_window close];
[_formTwo showWindow:sender];
} else if(_formTwo.isVisible) {
[_formTwo close];
[_window showWindow:sender];
}
}
In your nib, make sure both windows have the 'Release when closed' checkbox unchecked so your window is not released when you call close. In your second FormTwo window controller, you should call the BtnSwitchAction from your BtnBackActionPreformed method.
I know there's a number of ways you can connect the window switching code to the back button, but I recommend having all the window switching logic in one method on the AppDelegate rather than manipulating OTHER windows from your BtnBackActionPreformed. That controller and method shouldn't know about the details of the other windows, it should just tell the AppDelegate to do the switch.

Mac OS X Cocoa multiview application navigation

I've already spent 2 full days trying to figure out how to use NSViewControllers in order to create a multiview application.
Here is what I do.
I have 2 View Controllers and the MainMenu.xib's Window.
I also have an AppController that is the delegate for both View Controllers.
When I launch the app, I'm first greeted with the MainMenu.xib's Window's view which holds a button. On clicking this button, an IBAction is sent to the appController and asks for the SecondViewController to display it's nib. So far, everything's fine and the nib file is displayed correctly.
On the secondViewController, there's another button that sends another IBAction to the appController and asks for the FirstViewController to be displayed but nothing happens,
no crash, no warning... Any help would be much appreciated...
Thanks in advance for your patience...
Here is the code for the AppController.h :
#import <Foundation/Foundation.h>
#import "SecondViewController.h"
#import "FirstViewController.h"
#interface AppController : NSObject
#property (strong) IBOutlet NSWindow *mainWindow;
#property (strong) IBOutlet SecondViewController *secondViewController;
#property (strong) IBOutlet FirstViewController *firstViewController;
- (IBAction)secondButtonfromsecondViewControllerClicked:(id)sender;
- (IBAction)buttonClicked:(id)sender;
#end
and here is the code for the AppController.m :
#import "AppController.h"
#implementation AppController
#synthesize mainWindow = mainwindow;
#synthesize secondViewController;
#synthesize firstViewController;
- (IBAction)buttonClicked:(id)sender {
NSLog(#"button from second View Controller clicked");
self.secondViewController = [[SecondViewController
alloc]initWithNibName:#"SecondViewController" bundle:nil];
self.mainWindow.contentView = self.secondViewController.view;
[self.secondViewController.view setAutoresizingMask:NSViewWidthSizable |
NSViewHeightSizable];
}
- (IBAction)secondButtonfromsecondViewControllerClicked:(id)sender {
NSLog(#"button from first ViewController clicked");
self.firstViewController = [[FirstViewController
alloc]initWithNibName:#"FirstViewController" bundle:nil];
self.mainWindow.contentView = [self.firstViewController view];
}
#end
Well, anyone can help me, I just need a single view application that displays a first ViewController with a button on the first viewController that takes me to a second view controller with a second button that takes me back to my first viewcontroller... I've already spent more than a week on that... in vain... PS : I don't want any button on the mainMenu.xib window nor tabs.
here is the solution to my question then.
Here's the code for the AppDelegate.h:
// AppDelegate.h
#import <Cocoa/Cocoa.h>
#import "FirstViewController.h"
#import "SecondViewController.h"
//We need to declare the AppDelegate class as being the delegate for both
//FirstViewController and SecondViewController
#interface AppDelegate : NSObject <NSApplicationDelegate,
FirstViewControllerDelegate, SecondViewControllerDelegate>
#property (strong, nonatomic) NSWindow *window;
#property (strong) FirstViewController *firstViewController;
#property (strong) SecondViewController *secondViewController;
-(void) goToSecondView;
-(void) goToFirstView;
#end
Now, here's the AppDelegate.m:
// AppDelegate.m
#import "AppDelegate.h"
#implementation AppDelegate
#synthesize window = _window;
#synthesize firstViewController;
#synthesize secondViewController;
-(void) awakeFromNib {
[self goToFirstView];
self.firstViewController.delegate = self;
}
-(void) goToSecondView {
if (self.secondViewController ==nil) {
self.secondViewController =[[SecondViewController alloc]
initWithNibName:#"SecondViewController" bundle:nil];
}
self.window.contentView = [self.secondViewController view];
}
-(void) goToFirstView {
if (self.firstViewController ==nil) {
self.firstViewController =[[FirstViewController alloc]
initWithNibName:#"FirstViewController" bundle:nil];
}
self.window.contentView = [self.firstViewController view];
}
#end
Next we need to set delegates in the FirstViewController and the SecondViewController
// FirstViewController.h
#import <Cocoa/Cocoa.h>
#import "SecondViewController.h"
//We declare the delegation protocole:
#protocol FirstViewControllerDelegate <NSObject>
-(void)goToSecondView;
#end
#interface FirstViewController : NSViewController
- (IBAction)firstViewControllerButtonClicked:(id)sender;
#property (nonatomic, strong) id <FirstViewControllerDelegate> delegate;
#end
And here is the FirstViewController.m:
// FirstViewController.m
#import "FirstViewController.h"
#implementation FirstViewController
#synthesize delegate;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
self.delegate = [NSApp delegate];
}
return self;
}
- (IBAction)firstViewControllerButtonClicked:(id)sender {
NSLog(#"button from first View Controller clicked");
if ([self.delegate respondsToSelector:#selector(goToSecondView)]) {
[self.delegate goToSecondView];
}
}
#end
Now, same thing for the SecondViewController:
// SecondViewController.h
#import <Cocoa/Cocoa.h>
#protocol SecondViewControllerDelegate <NSObject>
-(void)goToFirstView;
#end
#interface SecondViewController : NSViewController
#property (nonatomic, strong) id <SecondViewControllerDelegate> delegate;
- (IBAction)goToFirstViewControllerButtonClicked:(id)sender;
#end
And here's the SecondViewController.m:
// SecondViewController.m
#import "SecondViewController.h"
#interface SecondViewController ()
#end
#implementation SecondViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
self.delegate = [NSApp delegate];
}
return self;
}
- (IBAction)goToFirstViewControllerButtonClicked:(id)sender {
NSLog(#"button from Second View Controller clicked");
if ([self.delegate respondsToSelector:#selector(goToFirstView)]) {
[self.delegate goToFirstView];
}
}
#end
Well, I guess this code may be improved and if you have any suggestion, feel free to let me know. Hope it will help others.
THE PROBLEM: When the user presses a button in View2, you want View1 to appear. It's not.
STEP 1: You say that the button should be invoking an action on your AppController. Set a breakpoint (or add a diagnostic log) in that action, just to verify that it is, in fact, being invoked.
STEP 2: Think about what you want that action to do, precisely. My guess is that you want to hide View2 and show View1. Perhaps
[view2 setHidden: YES];
[view1 setHidden: NO];
(I'm not using your names here, of course.) Or you might animate the transitions, either cross-fading the views or moving them.
STEP 3: My guess is that STEP 2 will solve your problem. If it doesn't, use the debugger again to verify that view1 and view2 are not null. (If they're null, you probably have weak variables where you need them to be strong.)
STEP 4: In the unlikely event that you're still stuck, check the frames of view1 and view2. Perhaps view1 isn't where you think it is.
STEP 5: If you're still stuck, check the alphaValue of view1. Maybe you set it to be transparent, and it's being drawn transparently in the right place.
STEP 6: I bet there is no step 6!
This isn't much of an answer at the moment, however I have some concerns about your code that I wanted to work through with you.
Are you sure you have connected the outlets and actions in Interface Builder. Please verify this.
You don't need mainWindow as there is already a window property that points to the main window (verify this in Interface Builder). Also this looks wrong:
#synthesize mainWindow = mainwindow;
^
W
So dump that and just use the existing window outlet provided by Xcode.
Don't re-create the view controllers if they already exist:
if (self.secondViewController == nil)
{
self.secondViewController = [[SecondViewController alloc] initWithNibName:#"SecondViewController"
bundle:nil];
}
self.window.contentView = self.secondViewController.view;

hide an object from another class

I'm trying to hide an object in my viewController, with code executed from a custom class, but the object is nil.
FirstViewController.h
#import <UIKit/UIKit.h>
#interface FirstViewController : UIViewController {
IBOutlet UILabel *testLabel;
}
#property (nonatomic, retain) IBOutlet UILabel *testLabel;
- (void) hideLabel;
FirstViewController.m
I synthesize testLabel and I have a function to hide it. If I call the function from viewDidAppear it works, but I want to call it from my other class. When called from the other class, testLabel is nil
#import "FirstViewController.h"
#import "OtherClass.h"
#implementation FirstViewController
#synthesize testLabel;
- (void) hideLabel {
self.testLabel.hidden=YES;
NSLog(#"nil %d",(testLabel==nil)); //here I get nil 1 when called from OtherClass
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
OtherClass *otherClass = [[OtherClass alloc] init];
[otherClass hideThem];
//[self hideLabel]; //this works, it gets hidden
}
OtherClass.h
#class FirstViewController;
#import <Foundation/Foundation.h>
#interface OtherClass : NSObject {
FirstViewController *firstViewController;
}
#property (nonatomic, retain) FirstViewController *firstViewController;
-(void)hideThem;
#end
OtherClass.m
calls the hideLabel function in FirstViewController. In my original project, (this is an example obviously, but the original project is at work) I download some data here and I want to hide my loading label and indicator when download is done
#import "OtherClass.h"
#import "FirstViewController.h"
#implementation OtherClass
#synthesize firstViewController;
-(void)hideThem {
firstViewController = [[FirstViewController alloc] init];
//[firstViewController.testLabel setHidden:YES]; //it doesn't work either
[firstViewController hideLabel];
}
Any ideas?
Your UILabel is nil because you just initialized your controller but didn't load it's view. Your controller`s IBoutlets are instantiated from the xib or storyboard automatically when you ask access to the bound view for the first time, so in order to access them you first have to load its view by some means.
EDIT (after OP comments):
Since your FirstViewController is already initialized and your OtherClass is instantiated by that controller, you could just hold a reference to it and not try to initialize a new one.
So try something like this:
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
OtherClass *otherClass = [[OtherClass alloc] init];
otherClass.firstViewController = self;
[otherClass hideThem];
}
In your OtherClass.m:
-(void)hideThem {
[self.firstViewController hideLabel];
}

Checkbox setState:0 on app launch

I'm having a problem with a checkbox. I want to set it to 0 (unchecked) on app launch, but the checkbox is controlled by another class "myClass" for example.
Here's what I did:
I opened Interface Builder and put a checkbox (NSButton) in my window, dragged NSObject in my MainMenu.xib window, renamed it to say "myClass". Added an outlet called "myCheckbox" (NSButton) and linked it to the checkbox I created earlier. Finally, I added some things.
Here's the code for my myClass.m:
#import "myClass.h"
#implementation myClass
- (void) changeState
{
[myCheckbox setState:0];
}
#end
myClass.h
#import <Cocoa/Cocoa.h>
#interface myClass : NSObject {
IBOutlet NSButton *myCheckbox;
}
- (void) changeState;
#end
Then I made some changes in the AppDelegate files so they execute some things when the app is launched:
#import "UntitledAppDelegate.h"
#import "myClass.h"
#implementation UntitledAppDelegate
#synthesize window;
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
myClass * someClass = [[myClass alloc] init];
[someClass changeState];
}
#end
UntitledAppDelegate.h:
#import <Cocoa/Cocoa.h>
#interface UntitledAppDelegate : NSObject <NSApplicationDelegate> {
NSWindow *window;
}
#property (assign) IBOutlet NSWindow *window;
#end
The purpose (if this works) is to set a value to the check box depending on the setting stored in the Defaults file.
The problem might be easy or too simple but I'm only a beginner...
Some help would be appreciated, Thanks !
- (void) awakeFromNib
{
[myCheckbox setState:0];
}
in myClass.m solved it.

Resources