Variables in separate class coming back null - objective-c

Ok, I think the question I had here was long-winded and difficult to get through. I'll simplify my question:
I have a class called InController.
InController has a method called nextPage that tells an int variable, inPageNumber, to add one onto itself and to call on another InController method called updateTable.
updateTable clears a table, inTable, of its current data and fills it with data relevant to the page number it retrieves from inPageNumber.
The table, inTable, is contained inside an NSBox with specific printing requirements.
I subclassed NSBox into a class called CustomViewPagination to meet these printing requirements, overriding its paginations methods. Basically, when a new printing page is required, it attempts to print the same area again, but calls on nextPage to fill the table with the data of the sequential page.
With me so far?
One of the pagination methods I overrided in CustomViewPagination, beginPageInRect, is automatically called for each printed page by default. Because of this, I placed a call to my InController method of nextPage, to change the inTable data for the current printing page.
My problem is when I call nextPage (which is a method in InController) from my CustomViewPagination class. It does nothing and when I debug it I find that all the variables required in the method are nil. However, they are the correct values when I call nextPage from inside InController.
File Extracts:
InController.h:
#import <Cocoa/Cocoa.h>
#import "CustomViewPagination.h"
#interface InController : NSObject {
IBOutlet NSWindow *inPreview;
IBOutlet CustomViewPagination *inSheet;
NSArray *iSelectedIn;
NSMutableArray *records;
int inPageNumber;
}
#property (nonatomic, retain) NSArray *iSelectedIn;
#property (nonatomic, retain) NSMutableArray *records;
InController.m:
#import "InController.h"
#implementation InController
#synthesize iSelectedIn, records;
- (IBAction) inNextPage:(id)sender {
inPageNumber = inPageNumber + 1;
NSLog(#"inPageNumber called ok");
[self updateIn];
}
- (IBAction)updateInvoice:(id)sender {
//wipe all current records and refresh empty table
[records removeAllObjects];
[inPreviewTable reloadData];
for (NSArray *s in [[iSelectedIn valueForKey:#"inJobList"] lastObject]) {
NSString *jLT = [s valueForKey:#"inJT"];
NSString *jLH = [s valueForKey:#"inJHo"];
NSString *jLC = [s valueForKey:#"inJC"];
// etc.
// if CustomViewPagination called this, records is nil, so nothing
// is cleared, and there's no *s for iSelectedIn as iSelectedIn
// is found to be nil. If InController called this, it works fine.
CustomViewPagination.h:
#import <Cocoa/Cocoa.h>
#class InController;
#interface CustomViewPagination : NSBox {
InController *inControllerInstance;
}
#end
CustomViewPagination.m:
#import "CustomViewPagination.h"
#import "InController.h"
#implementation CustomViewPagination
- (void) awakeFromNib {
inControllerInstance = [[InController alloc] init];
}
- (void)beginPageInRect:(NSRect)aRect atPlacement:(NSPoint)location {
int pageCounter = [[NSPrintOperation currentOperation] currentPage];
if (pageCounter == 1) {
// Don't respond to 1st page, do nothing.
} else {
[inControllerInstance inNextPage:self];
}
[super beginPageInRect:aRect atPlacement:location];
}
#end

You are using 2 IBOutlets in InController (inPreview & inSheet), but InController is created programmatically in CustomViewPagination's awakeFromNib.
How are the Outlets connected? (Can't be from within IB, as you are creating the InController instance programmatically). This would be an explanation why both are nil.

Related

How to initialize main window objects from custom class during program start?

I have a main window with a couple of popupbuttons. I want to clear them, then load the lists from a method in a custom class. I've got my view controller working and I know the method in the custom class (newRequest) is working because I added a NSLog command to print "Test" when the method executes. In AppDelegate I'm calling the method via:
[polyAppRequest newRequest];.
As I said, I know the method is executing. Why can't I removeallitems from the popupbutton from this custom class method?
Thanks
Keith
I read that you should use an NSWindowController to manage a window. See here:
Windows and window controllers
Adding views or windows to MainWindow
Then if your window gets complicated enough, the NSWindowController can employ various NSViewControllers to manage parts of the window.
In any case, I used an NSWindowController in my answer.
The image below shows the outlet's for File's Owner, which is my MainWindowController:
I created MainWindowController .h/.m in Xcode6.2 by:
Selecting File>New>File>OS X - Source - Cocoa Class
Selecting NSWindowController for Subclass of:
Checking also create .xib file for user interface
Then I deleted the window--not the menu--in the default MainMenu.xib, and I changed the name of MainWindowController.xib, created by the steps above, to MainWindow.xib.
The following code works for me (but I'm a Cocoa beginner!):
//
// AppDelegate.m
// PopUpButtons
#import "AppDelegate.h"
#import "MainWindowController.h"
#interface AppDelegate ()
#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.m
// PopUpButtons
//
#import "MainWindowController.h"
#import "MyData.h"
#interface MainWindowController ()
#property(strong) MyData* data;
#property(weak) IBOutlet NSPopUpButton* namePopUp;
#property(weak) IBOutlet NSPopUpButton* agePopUp;
#end
#implementation MainWindowController
-(id)init {
if (self = [super initWithWindowNibName:#"MainWindow"]) {
_data = [[MyData alloc] init]; //Get data for popups
}
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.
[[self namePopUp] removeAllItems];
[[self namePopUp] addItemsWithTitles:[[self data] drinks]];
[[self agePopUp] removeAllItems];
[[self agePopUp] addItemsWithTitles:[[self data] extras]];
}
#end
...
//
// MyData.h
// PopUpButtons
//
#import <Foundation/Foundation.h>
#interface MyData : NSObject
#property NSArray* drinks;
#property NSArray* extras;
#end
...
//
// MyData.m
// PopUpButtons
//
#import "MyData.h"
#implementation MyData
- (id)init {
if (self = [super init]) {
_drinks = #[#"coffee", #"tea"];
_extras = #[#"milk", #"sugar", #"honey"];
}
return self;
}
#end
I hope that helps. If you need any more screenshots, let me know.
Edit1:
I think I see what you are asking about. Although I don't think it is a very good approach, if I change my code to this:
//
// MyData.h
// PopUpButtons
//
#import <Cocoa/Cocoa.h>
#interface MyData : NSObject
#property (copy) NSArray* drinks;
#property (copy) NSArray* extras;
-(void)newRequest;
#end
...
//
// MyData.m
// PopUpButtons
//
#import "MyData.h"
#interface MyData()
#property (weak) IBOutlet NSPopUpButton* drinksPopUp;
#property (weak) IBOutlet NSPopUpButton* extrasPopUp;
#end
#implementation MyData
- (id)init {
if (self = [super init]) {
_drinks = #[#"coffee", #"tea"];
_extras = #[#"milk", #"sugar", #"honey"];
}
return self;
}
-(void)newRequest {
[[self drinksPopUp] removeAllItems];
[[self drinksPopUp] addItemsWithTitles:[self drinks]];
[[self extrasPopUp] removeAllItems];
[[self extrasPopUp] addItemsWithTitles:[self extras]];
}
#end
I am unable to populate the NSPopUpButtons. This is what I did:
I dragged an Object from the Object Library to the dock in IB, and in the Identity Inspector, I changed the Object's class to MyData.
Then I clicked on the Connections Inspector, and the two instance variables in MyData, drinksPopUp and extrasPopUp, were listed in the Outlets.
I dragged from the outlets to the respective NSPopUpButtons.
I guess I assumed, like you, that when my program ran, the NSPopUpButtons would be assigned to the instance variables drinksPopUp and extrasPopUp--but that doesn't seem to be the case. According to the Apple docs, you should be able to do that:
An application typically sets outlet connections between its custom
controller objects and objects on the user interface, but they can be
made between any objects that can be represented as instances in
Interface Builder,...
Edit2:
I am able to pass the NSPopUpButtons from my MainWindowController to the newRequest method, and I can use the NSPopUpButtons inside newRequest to successfully populate the data.
Edit3:
I know the method in the custom class (newRequest) is working because
I added a NSLog command to print "Test" when the method executes.
But what happens when you log the variables that point to the NSPopUpButtons? With my code in Edit1, I get NULL for the variables, which means the NSPopUpButtons never got assigned to the variables.
Edit4:
If I add an awakeFromNib method to MyData, and inside awakeFromNib I log the NSPopUpButton variables for the code in Edit1, I get non NULL values. That tells me that the MainWindowController's windowDidLoad method is executing before MyData's awakeFromNib method, and therefore you cannot call newRequest inside MainWindowController's windowDidLoad method because MyData has not been fully initialized.
Edit5:
Okay, I got the code in Edit1 to work. The Apple docs say this:
About the Top-Level Objects
When your program loads a nib file, Cocoa recreates the entire graph
of objects you created in Xcode. This object graph includes all of the
windows, views, controls, cells, menus, and custom objects found in
the nib file. The top-level objects are the subset of these objects
that do not have a parent object [in IB]. The top-level objects typically
include only the windows, menubars, and custom controller objects that
you add to the nib file [like the MyData Object]. (Objects such as File’s Owner, First
Responder, and Application are placeholder objects and not considered
top-level objects.)
Typically, you use outlets in the File’s Owner object to store
references to the top-level objects of a nib file. If you do not use
outlets, however, you can retrieve the top-level objects from the
nib-loading routines directly. You should always keep a pointer to
these objects somewhere because your application is responsible for
releasing them when it is done using them. For more information about
the nib object behavior at load time, see Managing the Lifetimes of
Objects from Nib Files.
In accordance with the bolded line above, I changed this declaration in MainWindowController.m:
#interface MainWindowController ()
#property(strong) MyData* data;
...
#end
to this:
#interface MainWindowController ()
#property(strong) IBOutlet MyData* data;
...
#end
Then, in IB I dragged a connection from the MainWindowController data outlet to the MyData Object(the Object I had previously dragged out of the Object Library and onto the doc).
I guess that causes MyData to unarchive from the .xib file and initialize before MainWindowController.

Objective-c: Singleton - passing variables

I have a singleton that I'd like to use to manage the onscreen animation of my views. Here's my.
#import <Foundation/Foundation.h>
#interface OAI_AnimationManager : NSObject {
NSMutableDictionary* sectionData;
}
#property (nonatomic, retain) NSMutableDictionary* sectionData;
+(OAI_AnimationManager* )sharedAnimationManager;
- (void) checkToggleStatus : (UIView* ) thisSection;
#end
.m file
#import "OAI_AnimationManager.h"
#implementation OAI_AnimationManager
#synthesize sectionData;
+(OAI_AnimationManager *)sharedAnimationManager {
static OAI_AnimationManager* sharedAnimationManager;
#synchronized(self) {
if (!sharedAnimationManager)
sharedAnimationManager = [[OAI_AnimationManager alloc] init];
return sharedAnimationManager;
}
}
- (void) checkToggleStatus : (UIView* ) thisSection {
//get the section data dictionary
NSLog(#"%#", sectionData);
}
#end
You'll see in the .h file I added a NSMutableDictionary and am using #property/#synthesize for it's getter and setter.
In my ViewController I instantiate the animation manager as well as a series of subclasses of UIView called Section. With each one I store the data (x/y w/h, title, etc.) in a dictionary and pass that to the dictionary delcared in animation manager. In the Section class I also instantiate animation manager and add a UITapGestureRecognizer which calls a method, which passes along which section was tapped to a method (checkToggleStatus) in animation manager.
As you can I see in the method I am just logging sectionData. Problem is I am getting null for the value.
Maybe my understanding of singletons is wrong. My assumption was the class would only be instantiated once, if it was already instantiated then that existing object would be returned.
I do need all the other Section classes data as if one animates others animate in response and I can get around it by passing the tapped Section to the animation manager and doing [[Section superview] subviews] and then looping and getting the data from each that way but it seems redundant since that data is available in the ViewController when they are created.
Am I doing something wrong in trying to transfer that data? Is there a better solution? I am open to suggestions and criticisms.
Thanks
h file
#interface OAI_AnimationManager : NSObject
#property (nonatomic, retain) NSMutableDictionary* sectionData;
+(OAI_AnimationManager* )sharedAnimationManager;
- (void) checkToggleStatus : (UIView* ) thisSection;
#end
m file
static OAI_AnimationManager* _sharedAnimationManager;
#implementation OAI_AnimationManager
#synthesize sectionData = _sectionData;
+(OAI_AnimationManager *)sharedAnimationManager {
#synchronized(self) {
if (!_sharedAnimationManager) {
_sharedAnimationManager = [[OAI_AnimationManager alloc] init];
}
}
return _sharedAnimationManager;
}
- (void) checkToggleStatus : (UIView* ) thisSection {
//get the section data dictionary
NSLog(#"%#", _sectionData);
}
#end
Notice I moved your sectionData variable from the header and moved it to the implementation file. A while back, they changed it to where you can synthesize properties and specify their instance variable names along side it... hence:
sectionData = _sectionData;
I also added and underscore to the instance variable... this is a universal convention for private variables and it also will throw a compile error now if you try to type just sectionData as you did in the return statement of checkToggleStatus:. Now you either have to type self.sectionData or _sectionData.
You didn't include the code that creates an instance of your dictionary but I bet you didn't set it as self.sectionData = [[NSDictionary alloc] init] which means it would not retain the value and you would get null the next time you called it. Classic memory management mistake... I know it well because I learned the hard way hehehe

Array management help in XCode?

I am creating a program in the iOS SDK in which there is a group of buttons. When a button is clicked, its title is added to an array, and the array is displayed in an assigned label.
When I try to create delete and clear buttons, error messages show up. They show up in the function_builder = function_builder.removeLastObject; and function_builder = function_builder.removeAllObjects; lines of the .m file. The error messages are the same: Assigning to 'NSMutableArray *_strong' from incompatible type 'void'. How do I fix this?
Thank you for any and all help
Here is the .h file:
#import <UIKit/UIKit.h>
#interface SecondViewController : UIViewController
#property (nonatomic,strong) IBOutlet UILabel *equation_field;
#property (nonatomic) NSMutableArray *function_builder;//declare array//
#property(nonatomic, readonly, retain) NSString *currentTitle;//declare button titles//
#end
And here is the .m file:
#import "SecondViewController.h"
#interface SecondViewController ()
#end
#implementation SecondViewController
#synthesize equation_field;
#synthesize currentTitle;
#synthesize function_builder;
NSMutableArray *function_builder;//create the array name//
- (IBAction)functionButtonPress:(UIButton *)sender {//code for all buttons except delete and clear//
[function_builder addObject: sender.currentTitle];//when button is pressed, its title is added to the array//
self.equation_field.text = function_builder.description;//the contents of the array appear in the assigned label//
}
- (IBAction)delete:(UIButton *)sender {//create delete button//
function_builder = function_builder.removeLastObject; //ERROR OCCURRING HERE: Assigning to 'NSMutableArray *_strong' from incompatible type 'void'//
}
- (IBAction)clear:(UIButton *)sender{//create clear button//
function_builder = function_builder.removeAllObjects;//ERROR OCCURRING HERE: Assigning to 'NSMutableArray *_strong' from incompatible type 'void'//
}
- (void)viewDidLoad {
function_builder = [[NSMutableArray alloc] init];//initialize array//
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
} else {
return YES;
}
}
#end
Many errors there..
You've assigned the results of this methods (void*) to function_builder which is of kind NSMutableArray. That makes no sense.
In order to manipulate an object, just send a message to it:
[function_builder removeLastObject]; // this will remove the last object of the array
[function_builder removeAllObjects]; // guess what ;)
For the other thing:
self.equation_field.text = [function_builder componentsJoinedByString:#", "]
This will create a string with all objects in array separated by ", " => A, B, C, D
I think that's just how xCode/objective-c converts arrays to strings(Correct me if I'm wrong), so if you want to format it differently you're going to have to iterate through the string and remove the parentheses and commas, which shouldn't be too hard honestly.
The way I'd do it is read through the string and copy the contents unless they are ( ) or , that way your spacing is still correct and you get the filtering effect that you want.

Instance variable does not retain its value

I'm learning Objective-C right now and in order to practice I wrote a simple random maze generator for OS X, which works fine. Next I tried to add some more interaction with buttons, but I'm having trouble with the instance variables as they don't retain the value I assign them. I have come across multiple questions about the same problem, but the solutions to those haven't solved my problem. I also tested if the same problem persists in a simplified version of the program, which it does.
I guess I'm doing something wrong, but I don't know what. Here's what I did:
Created a new project
Added a subclass of NSView called "TestClass"
Added a view with class TestClass in the window in MainMenu.xib
Added an object for TestClass in MainMenu.xib
Added a button to the view and set its tag to 1
Added the following code to TestClass.h and TestClass.m and connected the button to it:
TestClass.h:
#import
#interface TestClass : NSView
{
NSNumber *number;
NSButton *test;
}
#property (nonatomic, retain) NSNumber *number;
#property (assign) IBOutlet NSButton *test;
- (IBAction)testing:(id)sender;
#end
TestClass.m:
#import "TestClass.h"
#implementation TestClass
#synthesize number;
#synthesize test;
- (id)initWithFrame:(NSRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code here.
}
return self;
}
- (IBAction)testing:(id)sender
{
self.number = [[NSNumber numberWithLong:[sender tag]] retain];
}
- (void) drawRect:(NSRect)dirtyRect
{
NSLog(#"%#", number);
}
#end
Whenever I press the button, NSLog just returns null several times.
I normally figure out everything by myself (eventually...), but this time it's really driving me insane, so is there anyone who can help me?
Put the NSLog in testing:, or just put a breakpoint there and see what's stored in number.
Note that self.number = [[NSNumber numberWithLong:[sender tag]] retain]; is double-retaining the NSNumber object (which is wrong), but that shouldn't cause any immediate error.

NSCollectionView draws nothing

I'm trying to set up an NSCollectionView (I have done this successfully in the past, but for some reason it fails this time).
I have a model class called "TestModel", and it has an NSString property that just returns a string (just for testing purposes right now). I then have an NSMutableArray property declaration in my main app delegate class, and to this array I add instances of the TestModel object.
I then have an Array Controller that has its Content Array bound the app delegate's NSMutableArray. I can confirm that everything up to here is working fine; NSLogging:
[[[arrayController arrangedObjects] objectAtIndex:0] teststring]
worked fine.
I then have all the appropriate bindings for the collection view set up, (itemPrototype and content), and for the Collection View Item (view). I then have a text field in the collection item view that is bound to Collection View Item.representedObject.teststring. However NOTHING displays in the collection view when I start the app, just a blank white screen. What am I missing?
UPDATE: Here is the code I use (requested by wil shipley):
// App delegate class
#interface AppController : NSObject {
NSMutableArray *objectArray;
}
#property (readwrite, retain) NSMutableArray *objectArray;
#end
#implementation AppController
#synthesize objectArray;
- (id)init
{
if (self = [super init]) {
objectArray = [[NSMutableArray alloc] init];
}
return self;
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
TestModel *test = [[[TestModel alloc] initWithString:#"somerandomstring"] autorelease];
if (test) [objectArray addObject:test];
}
#end
// The model class (TestModel)
#interface TestModel : NSObject {
NSString *teststring;
}
#property (readwrite, retain) NSString *teststring;
- (id)initWithString:(NSString*)customString;
#end
#implementation TestModel
#synthesize teststring;
- (id)initWithString:(NSString*)customString
{
[self setTeststring:customString];
}
- (void)dealloc
{
[teststring release];
}
#end
And then like I said the content array of the Array Controller is bound to this "objectArray", and the Content of the NSCollectionView is bound to Array Controller.arrangedObjects. I can verify that the Array Controller has the objects in it by NSLogging [arrayController arrangedObjects], and it returns the correct object. Its just that nothing displays in the NSCollectionView.
UPDATE 2: If I log [collectionView content] I get nothing:
2009-10-21 08:02:42.385 CollViewTest[743:a0f] (
)
The problem is probably there.
UPDATE 3: As requested here is the Xcode project:
http://www.mediafire.com/?mjgdzgjjfzw
Its a menubar app, so it has no window. When you build and run the app you'll see a menubar item that says "test", this opens the view that contains the NSCollectionView.
Thanks
The problem is that your not correctly using KVC. There is two things you can do.
Method 1: Simple but not so elegant
Use the following code to add the object to the array
[[self mutableArrayValueForKey:#"objectArray"] addObject:test];
This isn't so elegant as you have to specify the variable using a string value, so you will not get compiler warnings when spelt incorrectly.
Method 2: Generate the KVO methods needed for the array "objectArray".
Select the property in your interface declaration
Select Scripts (the script icon in the menubar) > Code > Place
accessor decls on Clipboard
Paste the declarations in the
appropriate spot in your interface file
Select Scripts > Code > Place
accessor defs on Clipboard
Paste the definitions in the
appropriate spot in your implementation file
You can then use a method that looks like
[self insertObject:test inObjectArrayAtIndex:0];