Cocoa drag & drop information - macos

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.

Related

Generating Swift models from Core Data entities

Update for Xcode 8:
In Xcode 8, one needs to go to the Core Data Model Editor and Show the File Inspector. Near the bottom is an option for code generation. Select Swift.
Edit: I found the solution to generate a Swift model from Core Data entity:
On Xcode:
Editor > Create NSManagedOjbect > Click button "Next" > Click button "Next" > Select "Swift" Langage > Click button "Create"
I tried Swift langage by creating a new Swift project on Xcode 6 beta using Core Data.
When I generate my models from my Core Data's entities, Xcode creates Objective-C models.
Is there a way to generate Swift model rather than Obejctive-C model with Core Data ?
Thanks !
Lets have a look on the Objective-C way:
Person.h (Header-File)
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#interface Person : NSManagedObject
#property (nonatomic, retain) NSString *name;
#end
Person.m (Implementation-File)
#import "Person.h"
#implementation Person
#dynamic name;
#end
Swift
The documentation already included in Xcode6-Beta says:
Core Data provides the underlying storage and implementation of properties in subclasses of the NSManagedObject class. Add the #NSManaged attribute before each property definition in your managed object subclass that corresponds to an attribute or relationship in your Core Data model. Like the #dynamic attribute in Objective-C, the #NSManaged attribute informs the Swift compiler that the storage and implementation of a property will be provided at runtime. However, unlike #dynamic, the #NSManaged attribute is available only for Core Data support.
So that is how I would rewrite the above example for Swift (not tested):
Person.swift
import CoreData
class Person: NSManagedObject {
#NSManaged var name : NSString
}
And according to your question I think the subclass-generation-feature might be not included in Xcode6 yet. Did you made sure that you have chosen "Swift" as programming language when you were creating the Cocoa-Project in Xcode?
You can get Swift model back using NSEntityDescription.insertNewObjectForEntityForName but you must edit your core data model file and not use Person as a Class Entity but <ProjectName>.Person else it returns NSManagedObject...
Using println() you won't see Person instance but something like <_TtC5ProjectName4Person: 0xc9ad5f0> but calling methods on this will prove it's a Person instance for real. I guess it's just the way for Swift to generate unique class names, not conflict and CoreData methods show this internal mechanism.
The Apple documentation says:
Swift classes are namespaced—they’re scoped to the module (typically, the project) they are compiled in. To use a Swift subclass of the NSManagedObject class with your Core Data model, prefix the class name in the Class field in the model entity inspector with the name of your module.
According to Apple's video regarding What's new In CoreData frame: 38mins (WWDC2014 Session 225), in inspector's Data Model, prefix the class name with the project name. Like projectName.Doctor
I've tried this but what will happen is that the generated managed object class becomes: projectName.swift instead of Doctor.swift. Even the class declaration becomes class projectName: ManagedObject
Solution:
In Data model inspector, just specify the Name & Class of your object to what name you want, example: Doctor
After you generated an object model and selecting Swift, this will create a file (Doctor.swift).
Now, when inserting new records in Core Data, you might an experience error "Class not found, using default NSManagedObject instead" even if you cast the newly inserted object to a correct object name.
To solve this, you just need to add #objc(class name) above the class declaration. See sample below.
import Foundation
import CoreData
#objc(Doctor)
class Doctor: NSManagedObject {
#NSManaged var name: String
}
Then:
let doctorManagedObject = NSEntityDescription.insertNewObjectForEntityForName("Doctor", inManagedObjectContext: context) as Doctor
doctorManagedObject.name = "John" // you can now use dot syntax instead of setValue
Save context to commit insert.
I tested #NSManaged, it didn't work. :( .
But mixed models files(.h) generated by xcdatamodel, it succeed.
please read the doc and code in https://github.com/iascchen/SwiftCoreDataSimpleDemo
Alternatively, you can just add #import "Person.h" to the bridging header, Project-Bridging-Header.h that Xcode generates for you (if you accepted it's offer to generate). Then you can use all the auto-generated Obj-C as if it were native Swift.
Editor -> Create NSManagedObject Subclass works fine for Swift
Simply go through all the usual steps, but when you get to creating the files, choose Language 'Swift' if it's the first time you've used Create NSManagedObject Subclass with a Swift project it'll be defaulting to Objective C
For XCode 12.
You can change generating model language.

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!

reference between app controller and window controllers

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.

Bindings and NSButton

Using bindings, how can I make NSButton invoke a method in my AppDelegate class? I tried setting the target sleector to buttonClicked: but in console it gives me:
unrecognized selector sent to instance
0x100105060
Also there's no place in bindings to specify which instance to look for the method in so how can I set it to AppDelegate?
Thanks.
As Simon says, bindings are not appropriate for this situation. Bindings allow you to "bind" UI elements to a data source. In your situation, you need an action - not a binding.
You are getting the error because you haven't actually defined the buttonClicked action. Thus, make sure you declare it with something like in your AppDelegate.m:
- (IBAction) buttonClicked:(id) sender;
Then set the action via interface builder by first setting the "FilesOwner" to the AppDelegate and then connecting the onTouchUpInside event to the action "buttonClicked".
Hope this helps!
Bindings are used to synchronize a variable and an element of the interface.
In your case, I guess an action would be better. Declare your method with IBAction and control-drag from your button to your AppDelegate instance in Interface Builder to be able to select the action.

iOS protocols and delegates. Basic questions

I'm creating an app that has a UITableView.
The data will be comming from an XML fetched over the net. I'm using NSXMLParser for this and it works. I used my tableView controller as the delegate for this so it implements the protocol for it:
#protocol NSXMLParserDelegate;
#interface MainView : UITableViewController <NSXMLParserDelegate>
Now this works perfectly, as I've nslogged the resulting parse.
Now, I want to populate the NStableView, so Reading I find that I need to add the datasource and delegate.
UITableViewDataSource
and
UITableViewDelegate
both of which are protocols.
How would I go about doing this on the same class? can I implement more than one protocol with the same class? should I move the delegation of the parser to another object and use this controller for this purpose?
Basically the question is what is the best way to do this?
thank you
Sure, you can implement as many protocols in a class as you want:
#interface MainView : UITableViewController <NSXMLParserDelegate, UITableViewDataSource, UITableViewDelegate>
Is that the "proper" way of doing that? I don't think there's a "right" answer to that. A purist might say no. I'd say do it where is makes sense, but err on the side of breaking it out into separate classes. For example, if your view controller is a simple menu then it would make sense for your view controller to also be your table view delegate and data source; there's no advantage in breaking it out into multiple classes.
If you have to parse XML my intuition suggests that it's starting to get a bit more complex. Maybe have a data class that implements the data source and XML parser and a controller class?

Resources