reference between app controller and window controllers - cocoa

at the risk of a public flogging, I was hoping someone could clarify my understanding of communicating between the app controller and a window controller. Here is the scenario using xcode4.
using the default AppDelegate.h and .m as the "controller" (which is also a delegate to MainMenu.xib). Have an ivar called int counter.
Created page.xib and PageController.h and .m (subclass NSWindowController). Imported it into AppDelegate.m
Used IBAction to create and view the page object. Like this:
if (!page1) {
PageController *page1 = [[Page
if (!page1) {
page1 = [[PageControoer alloc] initWithWindowNibName:#"page"];
}
[page1 showWindow:sender];
So the new window pops and we can press buttons, etc. The code for the new window is all in PageController.h and .m. and things basically work.
That is the context, here is where I'm confused.
a) question: let's say I want to access that original ivar in AppDelegate.h called counter from PageController. Either retrieving or updating the variable. What approach would I take?
b) confirm: let's say I'm back in the AppDelegate and want to get access to a selector from page1. I believe I can do this as so: [page1 runaction]; or [[page1 variable] setStringValue:#"hello"];
(this complies but I'm not sure it really works because I can't get the changes into the xib view.)
ok and the stumper. Say another view was created with another view controller call it Page2Controller.h and .m.
c) how should data flow between page and page2 -> via the AppDelegate or directly? what would the syntax look like to connect them together?
I've been following tutorials, but they don't really cover this back and forth messaging. Thanks for all the help!

a) Generally, if you want to have data that is accessed by your controllers, it should be in a model which they are given access to in some way. You can access things in the app delegate using this method:
AppDelegate* appDelegate = [[NSApplication sharedApplication] delegate];
[appDelegate <some method>];
b) I don't understand what you're asking. If the app delegate has a pointer to page1, then yes, you can call it directly.
c) Again, you should have a data model of some sort. The controllers should update the data model when the user changes the view. You can use notifications, IBActions, and direct calls to do the updating, for example. You should look up the Model, View, Controller design pattern.

Related

How to use NSObjectController and Managed Object Context using Cocoa Bindings

Searched entire Internet but couldn’t find the modern solution for my problem.
I want to use NSObjectController in pair with Core Data through Cocoa Bindings and struggle to set it up properly. Worth noting that I’m using latest version of Xcode and Swift.
What I’ve done:
For testing purposes I’ve done the following:
Created an macOS app with “Use Core Data” option selected (the app is not document based);
Dragged 2 NSTextFields into the Storyboard Dragged NSObjectController to the view controller scene;
Added Employee Entity to Core Data model with 2 attributes “name” and “surname”;
Done everything from the answer in How do I bind my Array Controller to my core data model?
Set NSObjectController to entity mode and typed in “Employee”,
Prepares Content selected, Use Lazy Fetching selected so all three options checked;
Binded the NSObjectController’s Managed Object Context in bindings inspector to the View Controller’s managedObjectContext;
Binded NSTextFields as follows: Value - Object Controller, Controller key - selection, Model Key Path - name (for 1st text field) and surname (for 2nd).
That’s it.
First set of questions: What I did wrong and how to fix it if it’s not completely wrong approach?
I’ve read in some post on stackoverflow that doing it that way allows automatic saving and fetching from Core Data model. That’s why I assumed it should work.
So here is a Second set of questions:
Is it true?
If it is then why text fields are not filled when view is displayed?
If it is not then how to achieve it if possible (trying to write as less code as possible)?
Third question: If I used approach that is completely wrong would someone help me to connect Core Data and NSObjectController using Cocoa bindings and show me the way of doing so with as less code written as possible using the right approach?
Taking into account that there no fresh posts about this topic in the wilds I think the right answer could help a lot of people that are developing a macOS app.
Thanks in advance!
I think your basic approach is correct, although it is important to understand that you need a real object, an instance, in order for it to work.
Creating a NSManagedObject subclass is generally desirable, and is almost always done in a real project, so you can define and use properties. You can do it easily nowadays by selecting the data model in Xcode's Project Navigator and clicking in the menu: Editor > Create NSManagedObject Subclass…. Technically it is not necessary, and in a demo or proof-of-concept, you often muddle through with NSManagedObject.
Assuming you are using the Xcode project template as you described, wherein AppDelegate has a property managedObjectContext, the following function in your AppDelegate class will maintain, creating when necessary, and return, what I call a singular object – an object of a particular entity, in this case Employee, which your app requires there to be one and only one of in the store.
#discardableResult func singularEmployee() -> NSManagedObject? {
var singularEmployee: NSManagedObject? = nil
let fetchRequest: NSFetchRequest<NSManagedObject> = NSFetchRequest(entityName: "Employee")
let objects = try? self.managedObjectContext.fetch(fetchRequest)
singularEmployee = objects?.first
if singularEmployee == nil {
singularEmployee = NSEntityDescription.insertNewObject(forEntityName: "Employee", into: self.managedObjectContext)
}
return singularEmployee
}
Then, add this line of code to applicationDidFinishLaunching
singularEmployee()

Change UILabel from appDelegate

I want to do some stuff from the appDelegate in Xcode. One of these things are to change a UILabel.
ViewController *viewController = [[UIStoryboard storyboardWithName:#"Main_iPhone" bundle:nil] instantiateViewControllerWithIdentifier:#"id"];
viewController.label.text = #"heeej";
I was told this would do the trick. Doesn't work. Label doesn't change. Does anyone know what the problem is?
There are several problems:
Don't do anything in the AppDelegate except for loading the window an the initial view controllers (if needed).
You are instantiating a new view controller in the first line. I assume you already have a view controller and you want to change the label in that view controller. Than you need a reference to that view controller and use this reference to send messages to it.
The label should not be a property of the view controller. You should try to follow the design pattern Model-View-Controller or the pattern Model-View-ViewModel. Ask you preferred search engine what those are if you don't know.
id as an identifier for anything in Objective-C is a bad idea because this is used as an object type.
Edit: You don't need a reference to change a property in the view controller. Use notifications to update the label. The system sends a notification with the name UIApplicationWillEnterForgroundNotification' (see [here][1]). Add the view controller as an observer to the[NSNotificationCenter defaultCenter]` for this name and react on the notification. Read the Apple documentation if you don't know what I am talking about.

Programatically creating a view controller view using an external class

I'm trying to create a class that will allow me to easily implement tutorial screen "overlays" within any view controller. I'm using the following as a reference:
http://highoncoding.com/Articles/824_Creating_Pulse_Application_Like_Tutorial_Overlay.aspx
So, my hope would be I could create a class ( i.e. TutorialOverlay.h / TutorialOverlay.m ), and then inside a given view controller (i.e. VievController.h / ViewController.m ) I could instantiate an instance of that class and create a tutorial screen overlay by doing something like this:
#property (nonatomic,strong) TutorialOverlay *tutorialScreen;
#synthesize tutorialScreen = _tutorialScreen;
self.tutorialScreen = [[TutorialOverlay alloc] initWithValues:#"specialscreen.png"];
[self.tutorialScreen showScreen];
Where I'm stuck is this: Since I squirreled all the code from the highoncoding.com example into this external class ( i.e. TutorialOverlay.h / TutorialOverlay.m ), that code can't do the last [self.view addSubview:topView] because the external class can't see/use the view in the view controller that called it.
I'm not sure whaere to go to make this work - delegates? Make TutorialOverlay.h / TutorialOverlay.m a subclass of UIView?
Any help and guidance appreciated - it would be nice to be able to whip these type of tutorial screens out at easily and add extra logic that would make them only run the first time an app runs, etc. rather than re-code the methods in every view controller!

Disabling a view in cappuccino

How can I disable a CPView? (so that the user can't interact with it while it's still visible)
It's useful for example when the user clicks on something that sends a request to the server and it should get disabled till the result comes back.
CPControls (as abstract subclass of CPView) contains a -setEnabled: method which most UI components inherit from.
http://cappuccino.org/learn/documentation/interface_c_p_control.html#a68d3dc4f2d0a4fad8699fd5982cddc2d
CPViews do not contain such a method, so in your CPView subclass you need to write your own method for enabling and disabling. Then override -mouseDown: and whatever else you need to (look at the docs for CPResponder for a complete list) and implement like so:
- (void)mouseDown:(id)sender
{
if ([self isEnabled])
[super mouseDown:sender];
}

Cocoa drag & drop information

I have a very simple application test in which I want to drag and drop files in a NSImageView object.
I can already get the list of files from this action, but now I want to store this data in an array to be accessed in the View using data bindings. The only code I have that works fine with data bindings, tough, has an array in AppDelegate that I access using an Array Controller. But then, my data is inside a class I created to the NSImageView called "DropView", which extends "NSImageView" class and handles the drop action.
How can I pass the array information to make the bindings possible?
Any suggestion is welcome. Thanks!
I decided to use a singleton in a bigger project. In this smaller one I did like this:
1 - Add AppDelegate reference to the subclass (m file):
#import "AppDelegate.h"
2 - Call a method declared in the AppDelegate passing all the information needed like this:
[[NSApp delegate] doSomething:someInformation];
3 - Set all the information in the method used.
4 - Make the bindings in the interface.
The simple answer is that you should not be storing model data (the array of file URLs) in a view.
Your view should pass the list of files to some other controller object, which then stores the list of files in some sort of model object. This is what MVC is about.
One way to do this is to use the delegate pattern, where your view would declare a protocol method something like this:
#protocol YourViewProtocol
- (void)imageView:(YourImageViewClass*)aView receivedDroppedURLs:(NSArray*)arrayOfURLs;
#end
Your view would also have a weak datasource property:
#interface YourImageViewClass : NSImageView
#property (weak) id <YourViewProtocol> datasource;
#end
You would then set your controller object as the datasource of the view. The controller must conform to the protocol and implement its method:
#interface YourController <YourViewProtocol>
- (void)imageView:(YourImageViewClass*)aView receivedDroppedURLs:(NSArray*)arrayOfURLs;
#end
Then, in the view method that receives the drop, you'd do something like:
- (void)receivedDroppedURLs:(NSArray*)urls
{
if([datasource conformsToProtocol:#protocol(YourViewProtocol)])
{
[datasource imageView:self receivedDroppedURLs:urls];
}
}
The other way to do this is via custom bindings. Implementing bindings in a custom view is a bit more complicated and I'd recommend that you use the delegate/datasource pattern initially, if you're not an experienced Cocoa developer.

Resources