How do I programmatically change a custom view object's member variables? - xcode

I appreciate you taking the time to read this and hopefully help me out!
I am writing my first program in Xcode 4, using Objective-C and Cocoa for the first time. I have been following the tutorials, but at this point I want to display custom graphics using a custom view, where the graphics change based on the values in my model object.
My controller class, GameController, based on NSObject, creates my model object during initialization, and successfully receives button action messages, updates the model object appropriately, and updates text strings on the window in return using its outlets. I accomplished this by creating an NSObject object in my xib file (which is called the default (?) name of MainMenu.xib, but it includes all the UI in my application), using the utilities pane to set its class to GameController, and then connecting its actions and outlets to the appropriate UI elements.
I have dragged a custom view into my window, and used the utilities pane to set its class to my custom view class, BoardView, which is based on NSView. In BoardView, I have overridden drawRect: to fill in the background of my view with a blue color, and then draw each of the graphics defined by my model class (GameStatus).
The problem is, I do not know how to access the data in GameStatus from my BoardView class, or have GameController update a member variable of BoardView. I have not instantiated BoardView anywhere besides in Interface Builder, by dropping a custom view on my window. I do not have a BoardView object in my xib file (the column or list on the left side in Interface Builder).
I tried to create an outlet to a BoardView object in my GameController class (which does have an object in my xib, as I mentioned above), and connecting that outlet to the BoardView custom view in my window, and Interface Builder seemed fine with that. However, when I run the program, the value of the BoardView outlet pointer in my GameController class is 0x0. It seems like the outlet is not being connected to a BoardView object. I don't know if I need to do something else in Interface Builder to make an actual object (I tried creating one in the list to the left, but then couldn't figure out a way to connect it to the actual custom view displayed on the window).
To add to the confusion, when I run my application, the BoardView area of the window will display the blue background, and in fact any other graphics which I define in the drawRect: function. However, without any way to talk to my model object, I can't change the graphics based on the state of the model. I'm not sure if the fact that the hard-coded graphics are displaying correctly means that there is an object there somewhere, or whether it is somehow drawing based on the general template of the class somehow. The latter doesn't really make sense to me, but if the former is true, I'm can't figure out how to talk to that object from other parts of my code.
I feel like I'm just missing some basic understanding of how Xcode / Interface Builder creates objects or something. I would really appreciate someone telling me exactly what I'm missing here in the connection between my MVC objects / classes.
Thank you in advance for any help you can give me!
EDIT 2011/09/06:
To download a copy of my project to take a look at it, go here:
http://talix.homeip.net/2011/rival/
That's my home server and I'm an amateur at this, so please leave a comment if it isn't working. Thanks! Also, if there is a better way to respond to comments other than editing my original post, please let me know; I'm also new to this website. ;-)
-Joe

It sounds like you're only instantiating one GameController and one BoardView, and that's happening in the nib.
This, then, is the heart of the problem:
I tried to create an outlet to a BoardView object in my GameController class (which does have an object in my xib, as I mentioned above), and connecting that outlet to the BoardView custom view in my window, and Interface Builder seemed fine with that. However, when I run the program, the value of the BoardView outlet pointer in my GameController class is 0x0. It seems like the outlet is not being connected to a BoardView object.
Where in your code are you needing a reference to your BoardView but getting nil? Is that in GameController's init method, or in some other method of GameController?
If it's inside init, this is what I'd expect. The code that loads nibs must initialize the objects before it connects the outlets.
(If it's outside of init, I'd suggest starting by disconnecting and reconnecting the outlet.)
View = BoardView
Controller = GameController
Model = GameStatus
In MVC, the controller usually brokers communication between the model and the view, so I suggest you handle it this way:
Add a gameController outlet to BoardView and connect it to your game controller.
In drawRect, have the BoardView get the game status from the game controller.
I also suggest you make GameController your application's delegate:
Delete RivalAppDelegate.[hm].
In your nib, delete the reference to Rival App Delegate, and connect the Game Controller reference to the File's Owner delegate outlet.
You've got two instance of BoardView in your nib. Hook up the one inside your window to the boardView outlet of GameController and delete the other.
In GameController.h, after #interface GameController : NSObject, add <NSApplicationDelegate>
In GameController.m, implement applicationDidFinishLoading and set up your application there. (You can even call setGameStatus there if you want to.)

Related

How to wire-connect NSTableView/Table View to avoid runtime error "Could not connect action, target class NSObject does not respond to")

In a view-based single-column NSTableView containing a default NSTextField in its Table Cell View, I'm trying to listen to confirmed user edits by connecting the NSTextField's action, within an Interface Builder view of the .xib, to a method in my ViewController for the window. But at run time (during window initialization) I get "Could not connect action, target class NSObject does not respond to -textCellChanged". I don't understand which NSObject is being incorrectly targeted, and I have many other NSViews in the window correctly connected to other outlets and actions in the same WindowController.
I see various other posts with a similar symptom, often also in the context of NSTableView, and have explored the solutions or partial solutions to those other problems in my context without success. Is there any particular magic about wiring Table View Cells in Interface Builder that I am overlooking? Or perhaps phrased differently: how is the target object actually determined at runtime, when the action is simply a class method in the File's Owner (and when does this vary for different controls all wired to the same owner of a common superview)?
Here are some particulars of context:
File Owner is set to a subclass of NSWindowController and Module there correctly inherited to my app target.
Probably relevant: I am not using Storyboards, and the top-level object in my XIB's outline view hierarchy is a Window (NSPanel), rather than an View or View Controller. The NSWindowController only appears within the XIB as the File Owner (not as its own object in Outline View).
In any of the various wiring scenarios I've tried (following), Interface Builder "looks like" the wiring op has succeeded. After wiring, the File Owner's Connections inspector lists the intended connection under Received Actions ("textCellChanged: ... [x] Table View Cell"), along with many other actions connecting components in the NSTableView's superview to other methods in the NSViewController.
Likewise, connecting the NSTextField as an OUTLET in the same NSViewController works with no problem. It's only the action (or target/action?? in IB one only sets "action") that fails.
The File Owner is also the data source and delegate of the NSTableView, and the NSTextField is set to Action:Send on End of Editing and Behavior:Editable. I don't think any of these are relevant to the particular symptom, which is just a failure to connect an action.
The NSWindowController is Swift; I have tried implementing the appropriate action within both the main NSWindowController implementation or in an extension that implements NSTableViewDelegate to no noticeably different effect.
Other posts suggest Xcode bugs in wiring, though in older versions of Xcode (I'm in 10.2). Here are various approaches I've tried, all with similar results:
Ctrl+Drag from Table View Cell icon in IB Outline View to NSWindowController source module, targeting there either an existing #IBAction or permitting Xcode to generate a new Connection (type Action, Object: File's Owner) and with it a new method in the ViewController
Reverse Drag from Source Code "left-column radio circle" next to an #IBAction to the Table View Cell in Outline View of my .xib
Ctrl+Drag from Table View Cell icon (in Outline View) to Placeholder/File's Owner icon, then choosing an appropriate action method from the pop-up list of methods implemented in the view controller.
Possibly some others
Finally, here are some related posts and how they differ:
This sounds like an identical symptom, but in comments OP claims to have fixed the issue by a combination of setting File Owner to the View Controller (done) and by working around blocking XCode bugs (not visible in my context).
This suggests I'm linking to a stale (deleted) method; definitely not the case when I allow Xcode to create the method for me.
This unanswered post suggests the user gave up on the situation as an IB bug and gave up in preference for a non-target/action workaround. I suppose I could pursue listening to notifications on the NSTextField as a similar workaround.
Finally, the accepted answer to a similar symptom here is that the connection to File Owner is incorrect in that case, where File Owner was the NSApplication object rather than the View Controller. In my case, File Owner is the View Controller object that defines these methods, so feels like the correct target.
Any stone unturned here? Thanks in advance for your help.
The file's owner in a xib file is a placeholder object representing the owner of the nib and representing the owner object passed to makeView(withIdentifier:owner:). Pass the owner of the nib (usually the view controller or window controller) to makeView(withIdentifier:owner:).

Swift class to interact with Interface Builder via IBOutlet

Good evening all,
I'm slowly working through my first OS X app. I have been having a hard time getting my Swift class to interact with an NSPopUpButton. Just to make sure I was doing it right, I created a new project and successfully erased all entries and entered text into the NSPopUpButton via AppDelegate. However, as soon as I try to move the same functionality to my own class, I can't even get the IBOutlet connection across to the new class.
Is a particular subclass type required of a new class to work properly with interface builder?
Here is a screenshot of the class I have created, as well as AppDelegate where I am trying to call the function belonging to this class.
Finally, here is the IB element in question, should I be able to select my own class under the 'Custom Class' inspector?
I am an iOS developer, but I would imagine the same principles would apply to your situation.
A ViewController class and an interface created in interface builder are two seperate things. While it may appear that they are connected via an iboutlet, they are actually independent and one can be instantiated without the other.
Currently, you are only creating an instance of your ViewController class in your App Delegate - and that's all. The system has no idea that your xib even exists! The outlets will only be connected once your app connects your xib to your ViewController class.
How do we do this? It's actually quite simple. Instead of instantiating our view controller like this:
let viewcontroller = ViewController()
We would connect our view controller to our xib in the instantiation:
let viewcontroller = ViewController(nibName: "MainWindow", bundle: NSBundle().mainBundle)
The nibName is telling the system the file name of your xib, and the NSBundle().mainBundle is telling the system where to look for the xib.
This will all only work if your xib has been assigned a custom class, like you mentioned. In your xib in interface builder, select the entire view controller. Then, in the custom class inspector type in the name of your ViewController class (in your case: ViewController - it should autocomplete). This will make sure your outlets are connected.
And you should be set up!! Let me know if you have any more problems come up.
Good luck!
EDIT:
This replaces the first part of my answer, however the part about hooking things up in Storyboard remains true. Upon reconsidering, I've believe I've realized that we are only creating the view controller, and not adding it to our view. Despite this, I believe we can take a short cut solution by adding one method to your view controller subclass (the one we set in the Storyboard). Start typing in viewDidLoad, and it should autocomplete. Type in super.viewDidLoad() at the beginning of the method. After that, type self.listUpdate(). This should work if the classes are hooked up correctly in Storyboard. This means you can delete the variables you created in the App Delegate.
Reference: You might also find Apple's documentation on creating a view controller handy (it's in Objective C online, but can be easily converted to Swift - it's the concept that counts): NSViewController Class Reference

Swift, storyboards and core data...a missing persistentstorecoordinator?

I am working on a new Cocoa project using Swift, Core Data and storyboards, and have come across a problem that makes no sense to me. After some fairly extensive hunting around, including on this site, I have come to the conclusion that I must be missing something obvious, but cannot figure out what. Here is what I have done so far:
1.Create a new project, Cocoa Application, using Swift, Storyboards, and Core Data.
2. Create an entity in the .xcdatamodeld. Let’s call it Dataset.
3. Create a subclass of NSSplitViewController (for what i want to do in the rest of the program).
4. Set the window content of the main window to and instance of myVC. I checked, and it loads up and displays fine.
5. In the viewController.swift, get the managedObjectContext like so:
#IBOutlet var moc:NSManagedObjectContext!
override func viewDidLoad() {
super.viewDidLoad()
// Do view setup here.
let appDelegate = NSApplication.sharedApplication().delegate as AppDelegate
moc = appDelegate.managedObjectContext
println("mainsplitviewcontroller moc:")
println(moc)
println("mainsplitviewcontroller psc:")
println(moc.persistentStoreCoordinator)
NSLog("Main split view loaded")
}
(yes, I have about dependency injection, but I want to solve this problem first).
In IB, put a managedObjectContext object in the View Controller instance.
In IB, connect the myVC outlet for the variable moc to the managedObjectContext.
In IB, create an array controller. Set it To Entity. Entity Name is Dataset. Turn on Prepares Content.
Either as an outlet or a binding, connect the array controller to the MOC. Using outlet, just dragging from managed object context in it's right-click popup to the icon for the MOC created in 6 above. For bindings, the old fashioned way, going to the bindings tab, and under Parameters, Bind to: (view controller), Model Key Path: moc. (moc is from 5 above)
Then, I build and run. and I get the error: "Cannot perform operation since managed object context has no persistent store coordinator."
This happens whichever way I try to do 9, above.
Now, the thing is, from my println statements, the objects referred to in both the app delegate and the viewcontroller are the same, both for the managed object context and for the persistent store controller, as below:
appdelegate moc:
appdelegate psc:
mainsplitviewcontroller moc:
mainsplitviewcontroller psc:
I wish I could show images, but I am new here and so cannot do that. Am I doing something clearly wrong? I thought I understood the process: make sure the VC can access the MOC, then put the MOC object into the VC's window in IB, make it an outlet, and connect it to an array controller. Why would the swift file for the view controller seem to show that the PSC is the same as the app delegate, but in IB, the array controller think the MOC has no PSC at all?
Thanks for reading!
I don't know if this is going to help, but I'm not surprised that your project shows that error. You have two managed object contexts - one created by your app delegate, and one created by the storyboard. Your interface code is connecting to that second MOC, which is not connected to your persistent store.

NSArrayController returns null

I have an NSArrayController bound to CoreData in my application. It is also bound to a TableView that displays the data. Two buttons are bound to the ArrayController that add and remove lines. All of this is working as expected. I can add, edit, save, and remove CoreData Entries.
There is a section of my app that is to accept drag and drop operations from files (working). It takes the data from the files, looks for various information, and is to insert this information into the Core Data database via the NSArray Controller.
I have added the class handling the parsing/adding of the file to the database as an object in IB. I created an IBOutlet for the array controller in the class, and bound the controller to the class' referencing outlet.
If I add a button to the interface to directly call the method that adds a custom record to the database, everything works. If the method is called via the drag and drop operation, nothing works, even logging a simple [arrayController className] returns null (though returns NSArrayController as expected when the method is called from the button click).
The only difference I can see is that when accessed through the button click, the method is called directly, while the other way passes through my drag and drop class before loading the parsing class, but I'm completely stuck on how to remedy this situation. I'll be happy to provide code, just not sure which code you'll need.
Any help is appreciated. Thanks!
==================
UPDATE
turns out I was connecting the IBOutlet to a class (a subclass of a view) object in IB instead of to the view itself handling the drops. Connecting these up made things work. Well, not work, I have other issues to iron out now, but the Array controller is now instantiated.
Moved from comment to answer: The array controller you are trying to add stuff is not instantiated. I assume you are not referring to your original NSArrayControllerinstance but maybe a new created one? Probably a problem of communication between your class instances.
Debugging this should be straightforward ... using the debugger. Set a few breakpoints (one at each action the button(s) call, and one at each point where your class instances are meant to talk to each other (your importer and your main controller)). Run, test, step through the code when the debugger breaks at each breakpoint.
My guess: An outlet is not hooked up (is nil) in IB or is not yet reconnected at runtime (see -awakeFromNib and make sure you're not trying to touch an outlet or action that hasn't been fully reconnected from the nib at runtime by the time you're trying to use it).
Something’s not hooked up right, BUT you don’t want to do it this way anyways. There’s no advantage to inserting via an NSArrayController. Just create new objects with NSEntityDescriptions:
+ (id)insertNewObjectForEntityForName:(NSString *)entityName inManagedObjectContext:(NSManagedObjectContext *)context;
And you’re done. If your NSArrayController is hooked up correctly it’ll auto-fetch the new objects at the end of the event so the user will see them “immediately.”

IBOutlet not getting set in Cocoa MVC project

I might not be using IBOutlet correctly, or some other subtlety with how NIB files work is causing me trouble - any help would be much appreciated (feel free to propose an alternate way to accomplish what I want).
I have a View object and a Controller object. Both are in the NIB. The Controller's init is also called when the NIB is loaded and the View is initialized in the 'awakeFromNib' callback.
I need a way to connect these two objects - specifically, enable the 'View' object to call functions on the Controller.
Based on documentation online, the way to get these connected is to define an IBOutlet in the View and connect it to the Controller in the Interface Builder. So i created an
IBOutlet Controller* _controller;
in the View interface and graphically connected it to the Controller object in Interface Builder by making a connection from the View to the Controller and assigning the _controller outlet to the Controller (the blue Generic Object box in Interface Builder).
At runtime though, _controller is always _nil. I have verified that the Controller's init was indeed called.
Is there something obvious I'm missing about this?
Any simpler way to connect these two? Since they're both created by the NIB I don't have a common object that has a pointer to both.
Try accessing the IBOutlet in viewDidLoad instad.
When awakeFromNib is called not all the IBOutlets are populated (even though the documentation seem to imply it).

Resources