In a document based Cocoa App I want to fill a NSBox in the Document.xib with a view,
by selecting the view with a NSMenuItem. However, the box is not updated with the view.
If I insert a button in the Document.xib, which is connected with the same IBAction as the NSMenuItem, the app works in the expected way.
I created the tree files:
- ViewController.h
- ViewController.m
- prettyView.xib
In ViewController.m the XIB File of the view is initialized.
// ViewController.m
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
- (id)init
{
if(![super initWithNibName:#"prettyView" bundle:nil]){
return nil;
}
[self setTitle:#"Pretty View"];
return self;
}
#end
The Document.h contains outlets for the box and two buttons.
One button fills the box with the view, the other one clears the box.
// Document.h
#import <Cocoa/Cocoa.h>
#class ViewController;
#interface Document : NSDocument
#property (weak) IBOutlet NSBox *contentBox;
- (IBAction)fillBox:(id)sender;
- (IBAction)clearBox:(id)sender;
#property ViewController * myViewController;
#end
In Document.m the view controller is instantiated.
// Document.m
#import "Document.h"
#import "ViewController.h"
#interface Document ()
#end
#implementation Document
- (instancetype)init {
self = [super init];
if (self) {
_myViewController = [ViewController new];
}
return self;
}
The methods for the IBActions are implemented in Document.m too.
- (IBAction)fillBox:(id)sender {
NSLog(#"Fill Box selected from %#", [sender className]);
[self.contentBox setContentView:[self.myViewController view]];
}
- (IBAction)clearBox:(id)sender {
NSLog(#"Clear Box selected");
[self.contentBox setContentView:nil];
}
The method fillBox is connected to one of the both buttons as well as to the NSMenuItem.
Pressing the button, a message is written to the console and the view is shown in the box.
Selecting the NSMenuItem, a message is written too, but the view is not displayed in the box.
The IBActions must not be connected with Document but with First Responder of MainMenu.xib.
Related
I followed the advice here on how to setup a MainWindowController: NSWindowController for my project's single window. I used a Cocoa class to create the .h/.m files, and I checked the option Also create .xib for User Interface. As a result, Xcode automatically hooked up a window, which I renamed MainWindow.xib, to my MainWidowController.
Next, I deleted the window in the default MainMenu.xib file (in Interface Builder I selected the window icon, then I hit the delete key). After that, I was able to Build my project successfully, and my controller's window in MainWindow.xib displayed correctly with a few buttons on it.
Then I tried adding an NSTableView to my MainWindowController's window. In Xcode, I dragged the requisite delegate and datasource outlets for the NSTableView onto File's Owner, which is my MainWindowController, and I implemented the methods in MainWindowController.m that I thought would make the NSTableView display my data:
- tableView:viewForTableColumn:row:
- numberOfRowsInTableView:
Now, when I Build my project, I don't get any errors, but the data doesn't appear in the NSTableView.
My code is below. Any tips are welcome!
//
// AppDelegate.h
// TableViews1
//
#import <Cocoa/Cocoa.h>
#interface AppDelegate : NSObject <NSApplicationDelegate>
#end
...
//
// AppDelegate.m
// TableViews1
//
#interface AppDelegate ()
#property (weak) IBOutlet NSWindow *window;
#property (strong) MainWindowController* mainWindowCtrl;
#end
#implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
// Insert code here to initialize your application
[self setMainWindowCtrl:[[MainWindowController alloc] init] ];
[[self mainWindowCtrl] showWindow:nil];
}
- (void)applicationWillTerminate:(NSNotification *)aNotification {
// Insert code here to tear down your application
}
#end
...
//
// MainWindowController.h
// TableViews1
//
#import <Cocoa/Cocoa.h>
#interface MainWindowController : NSWindowController
#end
...
//
// MainWindowController.m
// TableViews1
//
#import "MainWindowController.h"
#import "Employee.h"
#interface MainWindowController () <NSTableViewDataSource, NSTableViewDelegate>
#property (strong) NSMutableArray* employees;
#property (weak) IBOutlet NSTableView* tableView;
#end
#implementation MainWindowController
- (NSView*)tableView:(NSTableView *)tableView
viewForTableColumn:(NSTableColumn *)tableColumn
row:(NSInteger)row {
Employee* empl = [[self employees] objectAtIndex:row];
NSString* columnIdentifier = [tableColumn identifier];
//The column identifiers are "firstName" and "lastName", which match my property names.
//You set a column's identifier by repeatedly clicking on the TableView until only
//one of the columns is highlighted, then select the Identity Inspector and change the column's 'Identifier' field.
NSString* emplInfo = [empl valueForKey:columnIdentifier]; //Taking advantage of Key-Value coding
NSTableCellView *cellView =
[tableView makeViewWithIdentifier:columnIdentifier
owner:self];
NSLog(#"The Table view is asking for employee: %#", [empl firstName]);
[[cellView textField] setStringValue:emplInfo];
return cellView;
}
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView {
return [[self employees] count];
}
- (void)windowDidLoad {
[super windowDidLoad];
// Implement this method to handle any initialization after your window controller's window has been loaded from its nib file.
Employee* e1 = [[Employee alloc] initWithFirstName:#"Joe" lastName:#"Blow"];
Employee* e2 = [[Employee alloc] initWithFirstName:#"Jane" lastName:#"Doe"];
[self setEmployees:[NSMutableArray arrayWithObjects:e1, e2, nil]];
//Test to see if the employees array was populated correctly:
Employee* e = [[self employees] objectAtIndex:0];
NSLog(#"Here is the first employee: %#", [e firstName]);
//I see the output: "Here is the first employee: Joe"
}
- (id)init {
return [super initWithWindowNibName:#"MainWindow"];
}
- (id)initWithWindowNibName:(NSString *)windowNibName {
NSLog(#"Clients cannot call -[%# initWithWindowNibName] directly!",
[self class]
);
[self doesNotRecognizeSelector:_cmd];
return nil;
}
#end
...
//
// Employees.h
// TableViews1
#import <Foundation/Foundation.h>
#interface Employee : NSObject
#property NSString* firstName;
#property NSString* lastName;
- initWithFirstName:(NSString*)first lastName:(NSString*)last;
#end
...
//
// Employees.m
// TableViews1
//
#import "Employee.h"
#implementation Employee
- (id)initWithFirstName:(NSString *)first lastName:(NSString *)last {
if (self = [super init]) {
_firstName = first; //I read that you shouldn't use the accessors in init methods.
_lastName = last;
}
return self;
}
#end
File's Owner(=MainWindowController) connections:
NSTableView connections:
Response to comments:
Here is why calling [self tableView] reloadData] at the end of -windowDidLoad, as suggested in the comments, didn't work:
My _tableView instance variable--created by my #property declaration in MainWindowController.m--doesn't point to anything; therefore calling:
[[self tableView] reloadData]
I think is equivalent to calling:
[nil reloadData]
which doesn't do anything.
I never assigned anything to the _tableView instance variable in the -init method, nor did I assign it a value by dragging an outlet somewhere in Interface Builder. To fix that problem, I selected MainWindow.xib (the controller's window) in the Project Navigator(left pane), and then in the middle pane(Interface Builder), I selected the cube representing the File's Owner(selecting the Identity Inspector in the right pane reveals that the File's Owner is the MainWindowController). Then in the right pane, I selected the Connections Inspector, and it revealed an outlet called tableView, which is the IBOutlet variable I declared in MainWindowController.m.
Next, I dragged from the tableView outlet onto the TableView in the middle pane:
Doing that assigns the NSTableView object to the _tableView instance variable that was created by my #property declaration in MyWindowControler.m:
#property (weak) IBOutlet NSTableView* tableView;
As an experiment, I disconnected the outlet, then commented out the #property declaration for tableview, and the tableView outlet no longer appeared in the Connections Inspector. Also, if I change the declaration from:
#property (weak) IBOutlet NSTableView* tableView;
to:
#property (weak) NSTableView* tableView;
...then the tableView outlet doesn't appear in the Connections Inspector. That experiment answered a couple of questions I had about whether I should declare a property as an IBOutlet or not: if you need to assign one of the objects in Interface Builder to one of your variables, then declare the variable as an IBOutlet.
Thereafter, calling [self tableView] reloadData] at the end of -windowDidLoad succeeds in populating the TableView. However, I have not seen any tutorials that call reloadData, and even Apple's guide does not do that.
So, I am still puzzled about whether calling -reloadData is a hack or it's the correct way to do things.
Without it, your table view sits there blissfully clueless about your
expectation that it should even bother asking its datasource for data.
I assumed that an NSTableView automatically queries its datasource when it is ready to display itself, and that my code needed to be able to provide the data at that time.
I don't see you sending -reloadData to your table view anywhere. Tacking it onto the end of -windowDidLoad would be a good place. Without it, your table view sits there blissfully clueless about your expectation that it should even bother asking its datasource for data.
For all it knows, the data is simply not ready / available, so why would it try? More importantly, when should it try? It'd be rather rude of it to try whenever it pleases, considering the UI may not have finished loading / connecting to outlets, or its datasource may be in a vulnerable state (like teardown during/after dealloc) and sending datasource requests may result in a crash, etc.
Two things:
1st, set some breakpoints on when you set your employees array in windowDidLoad vs. when the table first attempts to populate itself and your numberOfRowsInTableView implementation gets called. If the latter happens before the former, then you'll need to add a reloadData after you create your array.
2nd, I personally always use NSCell instead of NSViews for my tables, so I always implement objectValueForTableColumn in my table's datasource. So I'm not sure if there's something different you need to do when you use NSView objects and implement viewForTableColumn. Is there a reason you're not using NSCell?
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;
I see there are lots of posts on this topic but none seem to solve my problem.
I have to a view controller with has just a textfield, a navigation bar button (called save) and a number/punctuation keyboard.
The other view controller has a static table view with 2 rows.
Workflow: When a user taps on the 1st row in the table, the second view controller appears (this already works).
The user then enters a number and when they tap save, the number typed should be used to set the detail label of the 1st row in the table view.
I have set up my protocols and delegates but something is wrong as the 2nd view controller does not disappear and also detail label never gets updated to reflect this typed number.
I am very stumped. Been through lots of code samples and tried so many things but still no solution. Any help is greatly appreciated.
Below is my code for both classes.
1st View (The table view)
.h file (The hourlyRateDetialLabel is the detail label from the table view):
#import UIKit/UIKit.h
#import "priceCalculatorHrRateSettingsViewController.h"
#interface priceCalculatorSettingsViewController : UITableViewController<SettingsViewControllerDelegate>
#property (strong, nonatomic) IBOutlet UILabel *hourlyRateDetialLabel;
#end
.m file
#import "priceCalculatorSettingsViewController.h"
#import "priceCalculatorHrRateSettingsViewController.h"
#interface priceCalculatorSettingsViewController ()
#end
#implementation priceCalculatorSettingsViewController
#synthesize hourlyRateDetialLabel;
- (void)viewDidLoad
{
[super viewDidLoad];
}
#pragma mark - Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
priceCalculatorHrRateSettingsViewController *vc = [[priceCalculatorHrRateSettingsViewController alloc] init];
vc.settingsViewDelegate = self;
}
- (void) HourlyRateDidSave:(priceCalculatorHrRateSettingsViewController *)controller didSetHourlyRate:(NSString *)rateValue{
self.hourlyRateDetialLabel.text = rateValue;
[self dismissViewControllerAnimated:YES completion:nil];
}
#end
The 2nd Class (The View with the textfield and save button)
.h file
#import UIKit/UIKit.h
#class priceCalculatorHrRateSettingsViewController;
#protocol SettingsViewControllerDelegate <NSObject>
- (void)HourlyRateDidSave:
(priceCalculatorHrRateSettingsViewController *)controller didSetHourlyRate:(NSString *)rateValue;
#end
#interface priceCalculatorHrRateSettingsViewController : UIViewController<UITextFieldDelegate>
#property (strong, nonatomic) IBOutlet UITextField *setHourlyRate;
#property (weak, nonatomic) id<SettingsViewControllerDelegate> settingsViewDelegate;
- (IBAction)saveHourlyRateValue:(id)sender;
#end
.m file
#import "priceCalculatorHrRateSettingsViewController.h"
#interface priceCalculatorHrRateSettingsViewController ()
#end
#implementation priceCalculatorHrRateSettingsViewController{
}
#synthesize setHourlyRate = _setHourlyRate;
#synthesize settingsViewDelegate;
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
//Automatically show the keybaord
[_setHourlyRate becomeFirstResponder];
}
- (void)viewDidUnload
{
[self setSetHourlyRate:nil];
[super viewDidUnload];
// Release any retained subviews of the main view.
}
- (IBAction)saveHourlyRateValue:(id)sender {
[self.settingsViewDelegate HourlyRateDidSave:self didSetHourlyRate:_setHourlyRate.text];
}
#end
The provided code includes:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
priceCalculatorHrRateSettingsViewController *vc = [[priceCalculatorHrRateSettingsViewController alloc] init];
vc.settingsViewDelegate = self;
}
This creates a controller when a cell is selected but never presents it and yet the unexpected behavior is described as:
...2nd view controller does not disappear...
These seems to be at odds with each other. Without an accurate description of 1. the expected behavior 2. the observed behavior and 3. the actual implementation producing the observed behavior, it is almost impossible for anyone to solve this problem. Please do not ask us all to guess how to help.
I created a new Cocoa application project in Xcode then add a NSOutlineView and a NSTextView objects onto window. Those two objects were subclassed as MyOutlineView and MyTextView. After that I made two outlets for them and wrote code like below.
The problem, I found, is application has two different MyOutlineView instances in runtime. Working(valid) outline view instance is not equal to the myOutlineView outlet instance. What am I missing?
//
// AppDelegate.h
#import <Cocoa/Cocoa.h>
#import "MyOutlineView.h"
#import "MyTextView.h"
#interface AppDelegate : NSObject <NSApplicationDelegate>
#property (assign) IBOutlet NSWindow *window;
#property (weak) IBOutlet MyOutlineView *myOutlineView;
#property (unsafe_unretained) IBOutlet MyTextView *myTextView;
#end
//
// AppDelegate.m
#import "AppDelegate.h"
#implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)n
{
NSLog(#"AppDelegate.myOutlineView(INVALID)::%#", _myOutlineView);
NSLog(#"AppDelegate.myTextView::%#", _myTextView);
}
#end
//
// MyOutlineView.h
#import <Cocoa/Cocoa.h>
#interface MyOutlineView : NSOutlineView <NSOutlineViewDataSource>;
#end
//
// MyOutlineView.m
#import "MyOutlineView.h"
#implementation MyOutlineView
- (id)initWithCoder:(NSCoder *)aDecoder
{
// This method is called first.
self = [super initWithCoder:aDecoder];
NSLog(#"MyOutlineView initWithCoder(INVALID)::%#", self);
return self;
}
- (id)initWithFrame:(NSRect)frame
{
// This method is also called but through a different instance with first one.
self = [super initWithFrame:frame];
NSLog(#"MyOutlineView initWithFrame(valid)::%#", self);
return self;
}
- (NSInteger)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item
{
NSLog(#"MyOutlineView data source delegate(valid)::%#", self);
return 0;
}
- (id)outlineView:(NSOutlineView *)outlineView child:(NSInteger)index ofItem:(id)item
{
return nil;
}
- (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item
{
return NO;
}
#end
//
// MyTextView.h
#import <Cocoa/Cocoa.h>
#interface MyTextView : NSTextView
#end
//
// MyTextView.m
#import "MyTextView.h"
#implementation MyTextView
- (id)initWithCoder:(NSCoder *)aDecoder
{
// This method is called.
self = [super initWithCoder:aDecoder];
NSLog(#"MyTextView initWithCoder::%#", self);
return self;
}
- (id)initWithFrame:(NSRect)frame
{
// But this method is NOT called at all.
self = [super initWithFrame:frame];
NSLog(#"MyTextView initWithFrame::%#", self);
return self;
}
#end
Output:
MyTextView initWithCoder:: [MyTextView: 0x10013be80]
MyOutlineView initWithCoder(INVALID):: [MyOutlineView: 0x10014bc90]
MyOutlineView initWithFrame(valid):: [MyOutlineView: 0x1001604a0]
MyOutlineView data source delegate(valid)::[MyOutlineView: 0x1001604a0]
AppDelegate.myOutlineView(INVALID):: [MyOutlineView: 0x10014bc90]
AppDelegate.myTextView:: [MyTextView: 0x10013be80]
Because of this, I have to put "AppDelegate.myOutlineView = self;" into MyOutletView's implementation wherever it calls related methods of AppDelegate. It does not seem natural.
Xcode doesn't seem to let you set an outline view's delegate or data source to itself.
So I'm guessing you're doing something like this:
Which is to say: instantiating a second copy of your custom outline view class.
Here's the output from this setup:
2012-09-26 14:11:34.511 testproj[30255:403] -[MyOutlineView initWithCoder:]
2012-09-26 14:11:34.531 testproj[30255:403] -[MyOutlineView initWithFrame:]
By removing the extra (highlighted) instance of My Outline View, the initWithFrame: line goes away.
To make the outline view its own delegate, do this instead:
- (void) awakeFromNib {
self.delegate = self;
}
That said, the point of the Delegation pattern is avoiding the need to subclass. If you do need an outline view subclass, try overriding the NSOutlineView / NSTableView methods directly, instead of using the delegate protocol.
I can't reproduce your problem. I dropped all your code that you posted into a test app, and I only get one instantiation of each object. Neither of the initWithFrame methods are getting called when I try it. My output is:
2012-09-26 09:00:38.945 TextViewDoubleInstantiationProblem[451:303] MyTextView initWithCoder::<MyTextView: 0x100123990>
Frame = {{0.00, 0.00}, {381.00, 182.00}}, Bounds = {{0.00, 0.00}, {381.00, 182.00}}
Horizontally resizable: NO, Vertically resizable: YES
MinSize = {381.00, 182.00}, MaxSize = {463.00, 10000000.00}
2012-09-26 09:00:38.953 TextViewDoubleInstantiationProblem[451:303] MyOutlineView initWithCoder(INVALID)::<MyOutlineView: 0x101a1cb90>
2012-09-26 09:00:39.005 TextViewDoubleInstantiationProblem[451:303] AppDelegate.myOutlineView(INVALID)::<MyOutlineView: 0x101a1cb90>
2012-09-26 09:00:39.005 TextViewDoubleInstantiationProblem[451:303] AppDelegate.myTextView::<MyTextView: 0x100123990>
Frame = {{0.00, 0.00}, {381.00, 182.00}}, Bounds = {{0.00, 0.00}, {381.00, 182.00}}
Horizontally resizable: NO, Vertically resizable: YES
MinSize = {381.00, 182.00}, MaxSize = {463.00, 10000000.00}
Do you have any other code in your app that you're not showing?
The calls to initWithCoder: come from loading objects that are defined in a nib file. I assume that's what you want to have happen since you mention creating outlets. In that case, they call to initWithFrame: strikes me as more likely to be "invalid" than the coder one.
I'd set a breakpoint in initWithFrame: and trace where that call is coming from in order to identify the extra allocation.
I'm working on a game for the iPad, and I have it start up with a menu screen. For a while, the menu screen would come up just fine in the simulator. I'm using the main view controller that xcode provides when starting up a view-based application. But, unfortunately, I accidentally cut off the connection between the UIView and the view controller in interface builder, and after reconnecting it, the screen comes up as blank now. It works fine when I simulate the screen in interface builder, but not when running in xcode. Here's the code for the view controller:
//
// FunctionMachineViewController.h
// FunctionMachine
//
// Created by Kameron Schadt on 5/24/11.
// Copyright 2011 Willamette University. All rights reserved.
//
#import <UIKit/UIKit.h>
#interface FunctionMachineViewController : UIViewController {
IBOutlet UITextField* equation;
IBOutlet UISlider* startLevel;
IBOutlet UITextView* startLevelNumber;
}
- (IBAction) startOnePlayer:(id)sender;
- (IBAction) startTwoPlayer:(id)sender startingEquation:(NSString*)equationUsed;
- (IBAction) sliderValueChanged:(UISlider*)sender;
#property(nonatomic, retain) IBOutlet UISlider* startLevel;
#property(nonatomic, retain) IBOutlet UITextField* equation;
#property(nonatomic, retain) IBOutlet UITextView* startLevelNumber;
#end
//
// FunctionMachineViewController.m
// FunctionMachine
//
// Created by Kameron Schadt on 5/24/11.
// Copyright 2011 Willamette University. All rights reserved.
//
#import "FunctionMachineViewController.h"
#import "GameViewController.h"
#implementation FunctionMachineViewController
#synthesize equation, startLevel, startLevelNumber;
- (IBAction)sliderValueChanged:(UISlider*)sender {
[startLevelNumber setText:[NSString stringWithFormat:#" %.1f", [sender value]]];
}
-(IBAction)startOnePlayer:(id)sender
{
GameViewController* GameView = [[GameViewController alloc] initWithNibName:nil bundle:nil];
[GameView isOnePlayer:YES];
[self presentModalViewController:GameView animated:YES];
}
-(IBAction)startTwoPlayer:(id)sender startingEquation:(NSString*)equationUsed
{
GameViewController* GameView = [[GameViewController alloc] initWithNibName:nil bundle:nil];
[GameView isOnePlayer:NO];
[self presentModalViewController:GameView animated:YES];
}
- (void)viewDidLoad {
[super viewDidLoad];
}
// Override to allow orientations other than the default portrait orientation.
- (BOOL)shouldAutorotateToInterfaceOrientation: (UIInterfaceOrientation)interfaceOrientation {
return YES;
}
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
- (void)viewDidUnload {
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)dealloc {
[super dealloc];
}
#end
I didn't really see any problem here, so I'm assuming it has something to do with me reconnecting the view controller to the view. I don't have an actual view file that I'm using, just the viewcontroller. Can anybody help?
Check the setting of "Main nib file base name" in [YourApp]-info.plist, in the "Supporting Files" folder – if you've changed the name of your root view controller, you may need to change the name here as well.
For some odd reason my Referencing outlet for the App Delegate was disconnected.
Try creating a referencing outlet from delegate to File's Owner using the connections inspector (farthest right menu) for your App Delegate.