Storing and retrieving a custom object from NSPasteBoard - macos

I have an object that belongs to this class that contains the following declarations:
HEADER
#interface MyClassObject : NSObject <NSCopying, NSPasteboardWriting, NSPasteboardReading>
#property (nonatomic, strong) NSArray *children;
#property (nonatomic, assign) NSInteger type;
#property (nonatomic, strong) NSString *name;
#property (nonatomic, strong) id node;
children holds other objects of this class, node holds objects that can be NSView or NSImageView.
I would like to be able to copy and paste objects of this type to/from NSPasteboard.
I have google around but the explanations are vague.
What should I do to make this class copiable/readable to NSPasteboard or in other words, make it conform to NSPasteboardWriting and NSPasteboardReading protocols?
I don't have the slightest clue on how this is done and as usual, Apple documetation and nothing is the same thing.

This is the complete solution.
First thing is to make the class conforming to NSCoding too. In that case the class declaration will be
#interface MyClassObject : NSObject <NSCopying, NSPasteboardWriting, NSPasteboardReading, NSCoding>
To make it compatible with NSCoding you have to implement encodeWithCoder: and initWithCoder:... in my case:
- (void)encodeWithCoder:(NSCoder *)coder {
[coder encodeObject:#(self.type) forKey:#"type"];
[coder encodeObject:self.name forKey:#"name"];
// converting the node to NSData...
// the object contained in node must be compatible with NSCoding
NSData *nodeData = [NSKeyedArchiver archivedDataWithRootObject:self.node];
[coder encodeObject:nodeData forKey:#"node"];
NSData *childrenData = [NSKeyedArchiver archivedDataWithRootObject:self.children];
[coder encodeObject:childrenData forKey:#"children"];
}
- (id)initWithCoder:(NSCoder *)coder {
self = [super init];
if (self) {
_type = [[coder decodeObjectForKey:#"type"] integerValue];
_name = [coder decodeObjectForKey:#"name"];
NSData *nodeData = [coder decodeObjectForKey:#"node"];
_efeito = [NSKeyedUnarchiver unarchiveObjectWithData:nodeData];
NSData *childrenData = [coder decodeObjectForKey:#"children"];
_children = [NSKeyedUnarchiver unarchiveObjectWithData:childrenData];
_parent = nil; // I want this to be nil when the object is recreated
}
To make the class work with NSPasteboard you have to add these 4 methods:
-(id)initWithPasteboardPropertyList:(id)propertyList ofType:(NSString *)type {
return [NSKeyedUnarchiver unarchiveObjectWithData:propertyList];
}
+(NSArray *)readableTypesForPasteboard:(NSPasteboard *)pasteboard {
// I am using the bundleID as a type
return #[[[NSBundle mainBundle] bundleIdentifier]];
}
- (NSArray *)writableTypesForPasteboard:(NSPasteboard *)pasteboard {
// I am using the bundleID as a type
return #[[[NSBundle mainBundle] bundleIdentifier]];
}
- (id)pasteboardPropertyListForType:(NSString *)type {
// I am using the bundleID as a type
if(![type isEqualToString:[[NSBundle mainBundle] bundleIdentifier]]) {
return nil;
}
return [NSKeyedArchiver archivedDataWithRootObject:self];
}
Then, on the class you implement the copy and paste you add:
- (void)copy:(id)sender {
NSPasteboard *pasteBoard = [NSPasteboard generalPasteboard];
[pasteBoard clearContents];
NSArray *copiedObjects = #[theObjectYouWantToCopyToThePasteboard];
[pasteBoard writeObjects:copiedObjects];
}
- (void)paste:sender {
NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
NSArray *classArray = #[[LayerObject class]];
NSDictionary *options = [NSDictionary dictionary];
BOOL ok = [pasteboard canReadItemWithDataConformingToTypes:#[[[NSBundle mainBundle] bundleIdentifier]]];
if (ok) {
NSArray *objectsToPaste = [pasteboard readObjectsForClasses:classArray options:options];
MyObjectClass *object = [objectsToPaste objectAtIndex:0];
// object is the one you have copied to the pasteboard
// now you can do whatever you want with it.
// add the code here.
}
}

Ill only discuss writing. it is basically the same for reading (but in reverse of course ;))
write
declare the type you write
- (NSArray *)writableTypesForPasteboard:(NSPasteboard *)pasteboard {
return #[#"com.mycompany.mytype"];
}
write the data
- (id)pasteboardPropertyListForType:(NSString *)type {
//check the type we are asked to write
if(![type isEqualToString:#"com.mycompany.mytype"]) return nil;
//create a _plist_ object
// :: ONLY PLIST TYPES
NSMutableDictionary *plist = [[NSMutableDictionary alloc] init];
...
return plist;
}

Related

UISearchController with updateSearchResultsForSearchController

I found how to implement a searchBar, but I don't know what to do in updateSearchResultsForSearchController method. All my data is fetched from CoreData. Here I put my code. If anyone had similar problem please tell me what to do.
#interface PlumbListTableViewController ()<NSFetchedResultsControllerDelegate, UIPageViewControllerDelegate, UISearchBarDelegate, UISearchResultsUpdating, UISearchControllerDelegate>
#property (nonatomic, strong) NSFetchedResultsController *fetchedResultsController;
#property (nonatomic, strong) NSManagedObjectContext *managedObjectContext;
#property (nonatomic, strong) NSArray *array;
#property (nonatomic, strong) NSNumber *number;
#property (nonatomic) int sum;
#property (nonatomic, strong) PickerViewController *picker;
#property (nonatomic, strong) UISearchController *searchController;
#property (nonatomic, strong) NSMutableArray *fileteredTableData;
#property (nonatomic, strong) NSArray *products;
#property (nonatomic, strong) NSArray *recipies;
#property (nonatomic, strong) NSArray *searchResults;
#end
#implementation PlumbListTableViewController
-(void)viewDidLoad {
[super viewDidLoad];
//All data from CoreData
self.products = [Product allProducts];
self.searchResults = [NSMutableArray arrayWithCapacity:[self.products count]];
[self.fetchedResultsController performFetch:nil];
CoreDataStack *coreDataStack = [CoreDataStack defaultStack];
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"AddEntry"];
fetchRequest.resultType = NSDictionaryResultType;
self.recipies = [coreDataStack.managedObjectContext executeFetchRequest:fetchRequest error:nil];
}
-(void) viewWillAppear:(BOOL)animated {
[super viewWillAppear:YES];
[self initializeSearchController];
self.tabBarController.tabBar.hidden = NO;
[self showTotalSum];
[self.tableView reloadData];
}
-(void)initializeSearchController {
//instantiate a search results controller for presenting the search/filter results (will be presented on top of the parent table view)
UITableViewController *searchResultsController = [[UITableViewController alloc] initWithStyle:UITableViewStylePlain];
searchResultsController.tableView.dataSource = self;
searchResultsController.tableView.delegate = self;
//instantiate a UISearchController - passing in the search results controller table
self.searchController = [[UISearchController alloc] initWithSearchResultsController:searchResultsController];
//this view controller can be covered by theUISearchController's view (i.e. search/filter table)
self.definesPresentationContext = YES;
//define the frame for the UISearchController's search bar and tint
self.searchController.searchBar.frame = CGRectMake(self.searchController.searchBar.frame.origin.x, self.searchController.searchBar.frame.origin.y, self.searchController.searchBar.frame.size.width, 44.0);
self.searchController.searchBar.tintColor = [UIColor whiteColor];
//add the UISearchController's search bar to the header of this table
self.tableView.tableHeaderView = self.searchController.searchBar;
//this ViewController will be responsible for implementing UISearchResultsDialog protocol method(s) - so handling what happens when user types into the search bar
self.searchController.searchResultsUpdater = self;
//this ViewController will be responsisble for implementing UISearchBarDelegate protocol methods(s)
self.searchController.searchBar.delegate = self;
}
-(void)updateSearchResultsForSearchController:(UISearchController *)searchController {
}
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"edit"]) {
UITableViewCell *cell = sender;
NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
UINavigationController *navigationController = segue.destinationViewController;
PlumbAddViewController *entryViewController = (PlumbAddViewController *)navigationController.topViewController;
entryViewController.entry = [self.fetchedResultsController objectAtIndexPath:indexPath];
}
}
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// Return the number of sections.
return self.fetchedResultsController.sections.count;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// Return the number of rows in the section.
id <NSFetchedResultsSectionInfo> sectionInfo = [self.fetchedResultsController sections][section];
return [sectionInfo numberOfObjects];
}
(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
ConfigureCellPlumb *cell = [self.tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
AddEntry *entry = [self.fetchedResultsController objectAtIndexPath:indexPath];
[cell configureCellforTable:entry];
return cell;
}
-(NSFetchRequest *)entryListFetchRequest {
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"AddEntry"];
fetchRequest.sortDescriptors = #[[NSSortDescriptor sortDescriptorWithKey:#"date" ascending:NO]];
return fetchRequest;
}
-(NSFetchedResultsController *)fetchedResultsController {
if (_fetchedResultsController != nil) {
return _fetchedResultsController;
}
CoreDataStack *coreDataStack = [CoreDataStack defaultStack];
NSFetchRequest *fetchRequest = [self entryListFetchRequest];
_fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:coreDataStack.managedObjectContext sectionNameKeyPath:#"sectionName" cacheName:nil];
_fetchedResultsController.delegate = self;
return _fetchedResultsController;}
According to the docs, updateSearchResultsForSearchController: is "called when the search bar's text or scope has changed or when the search bar becomes first responder."
So, within this method you want to update your table to show the proper search results. This is what mine looks like (yours will look different but the idea is the same):
- (void)updateSearchResultsForSearchController:(UISearchController *)searchController
{
//make sure model has only results that correspond to the search
[self updateFilteredContentWithSearchText:[self.searchController.searchBar text]];
//update the table now that the model has been updated
[self.specialtySearchResultsTVC.tableView reloadData];
}
//helper method
- (void)updateFilteredContentWithSearchText:(NSString*)searchText
{
[self.specialtySearchResultsTVC.filteredSpecialties removeAllObjects];
for (Specialty *specialty in self.specialties)
{
NSRange nameRange = [specialty.name rangeOfString:searchText options:NSCaseInsensitiveSearch];
if (nameRange.location != NSNotFound)
{
[self.specialtySearchResultsTVC.filteredSpecialties addObject:specialty];
}
}
}

RestKit KVC Validation won't call validation methods

this is my first question here :)
OK so I have a projet with ReskKit 0.23.3 via cocoapods. I use RestKit/CoreData.
I fetch an URL, the result got mapped to my object and is correctly saved by core data. I want to use Key-Value Validation to check some values retrieved against the one already persisted. I read that i could use the methods validateKey:error: on my NSManagedObject. Somehow, it is never called. I'm frustrated...
Here are my files (for simplicity, i concatenated logic code into one flat file here):
JSON response /collections/{id}
{
"id": "00000000-0000-0000-0000-00000000000",
"image_url": "http://server/image.png",
"name": "Collection C",
"etag": 42,
"ctag": 42
}
Collection.h
#interface Collection : NSManagedObject
#property(nonatomic, strong) NSString *collectionId;
#property(nonatomic, strong) NSString *name;
#property(nonatomic, strong) NSURL *imageUrl;
#property(nonatomic, strong) NSNumber *etag;
#property(nonatomic, strong) NSNumber *ctag;
#end
Collection.m
#implementation Collection
#dynamic collectionId, name, imageUrl, etag, ctag;
- (BOOL)validateCollectionId:(id *)ioValue error:(NSError **)outError {
NSLog(#"Validating id");
NSLog(#"Coredata collection id: %#", self.collectionId);
NSLog(#"GET collection id: %#", (NSString *)*ioValue);
return YES;
}
- (BOOL)validateEtag:(id *)ioValue error:(NSError **)outError {
NSLog(#"Validating etag");
NSLog(#"Coredata collection etag: %#", self.etag);
NSLog(#"GET collection etag: %#", (NSString *)*ioValue);
return YES;
}
#end
Code
NSManagedObjectModel *managedObjectModel = [NSManagedObjectModel mergedModelFromBundles:nil];
RKManagedObjectStore *managedObjectStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:managedObjectModel];
[managedObjectStore createPersistentStoreCoordinator];
NSString *storePath = [RKApplicationDataDirectory() stringByAppendingString:#"/MyApp.sqlite"];
NSError *error;
NSPersistentStore *persistentStore = [managedObjectStore addSQLitePersistentStoreAtPath:storePath fromSeedDatabaseAtPath:nil withConfiguration:nil options:nil error:&error];
NSAssert(persistentStore, #"Failed to add persistent store with error: %#", error);
[managedObjectStore createManagedObjectContexts];
managedObjectStore.managedObjectCache = [[RKInMemoryManagedObjectCache alloc] initWithManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
[RKManagedObjectStore setDefaultStore:managedObjectStore];
NSURL *url = [NSURL URLWithString:#"http://server/api"];
RKObjectManager *objectManager = [self managerWithBaseURL:url];
objectManager.requestSerializationMIMEType = RKMIMETypeJSON;
objectManager.managedObjectStore = [RKManagedObjectStore defaultStore];
RKEntityMapping *collectionMapping = [RKEntityMapping mappingForEntityForName:#"Collection" inManagedObjectStore:[RKManagedObjectStore defaultStore]];
[collectionMapping addAttributeMappingsFromDictionary:#{#"id": #"collectionId",
#"image_url": #"imageUrl"}];
[collectionMapping addAttributeMappingsFromArray:#[#"name", #"etag", #"ctag"]];
[collectionMapping setIdentificationAttributes:#[#"collectionId"]];
RKResponseDescriptor *collectionResponseDescriptors = [RKResponseDescriptor responseDescriptorWithMapping:collectionMapping
method:RKRequestMethodGET pathPattern:#"collections/:collectionId"
keyPath:nil
statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
[objectManager addResponseDescriptor:collectionResponseDescriptor];
[objectManager getObjectsAtPath:#"collections/00000000-0000-0000-0000-00000000000" parameters:nil success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
Collection *collection = (Collection *)[mappingResult.array firstObject];
NSLog(#"Collection: %#", collection);
} failure:^(RKObjectRequestOperation *operation, NSError *error) {
NSLog(#"Oh noes :(");
}];
Output
2014-09-13 12:39:04.242 MyApp[41958:607] I restkit:RKLog.m:33 RestKit logging initialized...
2014-09-13 12:39:05.028 MyApp[41890:607] Collection: <NSManagedObject: 0x9108a60> (entity: Collection; id: 0x94166d0 <x-coredata://6645F428-7631-45F0-A8AF-E2352C50F35E/Collection/p1> ; data: {
collectionId = "00000000-0000-0000-0000-00000000000";
ctag = 42;
etag = 42;
imageUrl = "http://server/image.png";
name = "Collection C";
})
So, I get my Log with the Collection, but None of the NSLog in the validate<Key>:error: methods got triggered... Could not figure out why!
Edit
With some breaks, i figured this is RKMappingOperation who is responsible for calling those validation methods on my object. Precisely it's validateValue:atKeyPath:
RKMappingOperation.m
...
- (BOOL)validateValue:(id *)value atKeyPath:(NSString *)keyPath
{
BOOL success = YES;
if (self.objectMapping.performsKeyValueValidation && [self.destinationObject respondsToSelector:#selector(validateValue:forKeyPath:error:)]) {
NSError *validationError;
success = [self.destinationObject validateValue:value forKeyPath:keyPath error:&validationError];
...
}
...
But self.destinationObject is an NSManagedObject and not a Collection object...
Console
(lldb) po [self.destinationObject class]
NSManagedObject
I hope you could lead me to the right way :) Thank you!
It appears that you have not specified that the entity should use the Collection class in the Core Data model. If you don't specify anything then NSManagedObject will be used by default.

Query data from Parse.com, iterate through, add certain parts to an NSObject, and add the object to an array of objects

I'm using iOS7 Xcode 5 with Parse.com's SDK. While querying data via parse, I'm trying to construct a Person (NSObject) for each returned object and create an NSArray of defaultPeople.
Here is the code for the Person:
Person.h
// Person.h
#import <Foundation/Foundation.h>
#interface Person : NSObject
#property (nonatomic, strong) NSString *name;
#property (nonatomic, strong) UIImage *image;
#property (nonatomic, assign) NSUInteger age;
#property (nonatomic, strong) NSString *gender;
#property (nonatomic, strong) NSString *location;
#property (nonatomic, strong) NSString *tagline;
#property (nonatomic, strong) NSString *objectId;
- (instancetype)initWithName:(NSString *)name
image:(UIImage *)image
age:(NSUInteger)age
gender:(NSString*)gender
location:(NSString*)location
tagline:(NSString*)tagline
objectId:(NSString*)objectId;
#end
Person.m:
// Person.m
#import "Person.h"
#implementation Person
#pragma mark - Object Lifecycle
- (instancetype)initWithName:(NSString *)name
image:(UIImage *)image
age:(NSUInteger)age
gender:(NSString*)gender
location:(NSString *)location
tagline:(NSString*)tagline
objectId:(NSString *)objectId {
self = [super init];
if (self) {
_name = name;
_image = image;
_age = age;
_gender = gender;
_location = location;
_tagline = tagline;
_objectId = objectId;
}
return self;
}
#end
Now here's the code I am using to try and create the array in my view controller .m file :
- (NSArray *)defaultPeople {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSLog(#"Current City for Querying: %#", [defaults objectForKey:#"CurrentCity"]);
if ([defaults objectForKey:#"CurrentCity"]) {
PFQuery *query = [PFQuery queryWithClassName:#"_User"];
[query whereKey:#"CurrentCity" equalTo:[defaults objectForKey:#"CurrentCity"]];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
// The find succeeded.
NSLog(#"Successfully retrieved %d scores.", objects.count);
// Do something with the found objects
for (PFObject *object in objects) {
NSString *userID = object.objectId;
NSString *first = [object objectForKey:#"FirstName"];
NSString *city = [object objectForKey:#"CurrentCity"];
NSUInteger age = (int)[object objectForKey:#"Age"];
NSString *gender = [object objectForKey:#"Gender"];
NSString *tagline = [object objectForKey:#"Tagline"];
Person *p = [[Person alloc]
initWithName:first
image:[UIImage imageWithData:
[NSData dataWithContentsOfURL:
[NSURL URLWithString:
[object objectForKey:#"PictureURL"]]]]
age:age
gender:gender
location:city
tagline:tagline
objectId:userID];
[self.people addObject:p]
}
} else {
NSLog(#"Error: %# %#", error, [error userInfo]);
}
}];
}
return self.people; //people was defined in the interface as:
//#property (nonatomic, strong) NSMutableArray *people;
}
I know that the querying is fine because I've NSLogged each NSString/NSUInteger in the for loop and it always returns the right value. My problem is creating a new Person object from those values and adding it to the defaultPeople array after each iteration. The result of this code is that my defaultPeople array always returns (null). PLEASE HELP!!! Thanks :)
Clayton
Ok guys FINALLY I figured out how do do this in a block that actually works:
- (void)queryForAllPostsNearLocation:(CLLocation *)currentLocation withNearbyDistance:(CLLocationAccuracy)nearbyDistance {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setBool:1 forKey:#"Users"];
PFQuery *query = [PFQuery queryWithClassName:#"_User"];
// If no objects are loaded in memory, we look to the cache first to fill the table
// and then subsequently do a query against the network.
if (query.countObjects == 0) {
query.cachePolicy = kPFCachePolicyCacheThenNetwork;
}
// Create a PFGeoPoint using the current location (to use in our query)
PFGeoPoint *userLocation =
[PFGeoPoint geoPointWithLatitude:[Global shared].LastLocation.latitude longitude:[Global shared].LastLocation.longitude];
// Create a PFQuery asking for all wall posts 1km of the user
[query whereKey:#"CurrentCityCoordinates" nearGeoPoint:userLocation withinKilometers:10];
// Include the associated PFUser objects in the returned data
[query includeKey:#"objectId"];
//Run the query in background with completion block
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (error) { // The query failed
NSLog(#"Error in geo query!");
} else { // The query is successful
defaultPeople = [[NSMutableArray alloc] init];
// 1. Find new posts (those that we did not already have)
// In this array we'll store the posts returned by the query
NSMutableArray *people = [[NSMutableArray alloc] initWithCapacity:100];
// Loop through all returned PFObjects
for (PFObject *object in objects) {
// Create an object of type Person with the PFObject
Person *p = [[Person alloc] init];
NSString *userID = object.objectId;
p.objectId = userID;
NSString *first = [object objectForKey:#"FirstName"];
p.name = first;
NSString *city = [object objectForKey:#"CurrentCity"];
p.location = city;
NSString *age = [object objectForKey:#"Age"];
p.age = age;
NSString *gender = [object objectForKey:#"Gender"];
p.gender = gender;
NSString *tagline = [object objectForKey:#"Tagline"];
p.tagline = tagline;
UIImage *img = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:[NSString stringWithFormat:#"%#",[object objectForKey:#"PictureURL"]]]]];
p.image = img;
if (![p.objectId isEqualToString:myID] && ![p.gender isEqualToString:myGender] && ![people containsObject:p]) {
[people addObject:p];
NSLog(#"Person: %#",p);
}
}
[defaultPeople addObjectsFromArray:people];
[[Global shared] setDefaultPeople:defaultPeople];
NSLog(#"Default People: %#",[Global shared].defaultPeople);
NSLog(#"Success. Retrieved %lu objects.", (unsigned long)[Global shared].defaultPeople.count);
if (defaultPeople.count == 0) {
[defaults setBool:0 forKey:#"Users"];
} else {
[defaults setBool:1 forKey:#"Users"];
}
}
}];
}
The BOOL returns on the bottom are to let the controller know whether or not to switch view controllers when prompted. If the switch controller toggle is hit, it only switches if the BOOL = 1, i.e. there are people in the area.
Thanks for all your help guys. Seriously.
[self.people addObject:p] is happening in the background thread so "return self.people" happens before self.people is updated. Thats why it is always returns nil.
instead of [query findObjectsInBackground] you can do
NSArray *objects = [query findObjects]
You need to return people inside the block, otherwise it will hit the return statement before it finishes finding the objects. It's finding them asynchronously with the block.
Another alternative is to get rid of the block and do:
NSArray *array = [query findObjects];
for (PFObject *object in array) {
NSString *userID = object.objectId;
NSString *first = [object objectForKey:#"FirstName"];
NSString *city = [object objectForKey:#"CurrentCity"];
NSUInteger age = (int)[object objectForKey:#"Age"];
NSString *gender = [object objectForKey:#"Gender"];
NSString *tagline = [object objectForKey:#"Tagline"];
Person *p = [[Person alloc]
initWithName:first
image:[UIImage imageWithData:
[NSData dataWithContentsOfURL:
[NSURL URLWithString:
[object objectForKey:#"PictureURL"]]]]
age:age
gender:gender
location:city
tagline:tagline
objectId:userID];
[self.people addObject:p];
}
return self.people;

Drag Drop delegate for NSView could not set an attribute

I am using NSView delegate to read the dragged excel values. For this I have subclassed NSView. My code is like-
#interface SSDragDropView : NSView
{
NSString *textToDisplay;
}
#property(nonatomic,retain) NSString *textToDisplay; // setters/getters
#synthesize textToDisplay;// setters/getters
#implementation SSDragDropView
- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender{
[self setNeedsDisplay: YES];
return NSDragOperationGeneric;
}
- (void)draggingExited:(id <NSDraggingInfo>)sender{
[self setNeedsDisplay: YES];
}
- (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender {
[self setNeedsDisplay: YES];
return YES;
}
- (BOOL)performDragOperation:(id < NSDraggingInfo >)sender {
NSArray *draggedFilenames = [[sender draggingPasteboard] propertyListForType:NSFilenamesPboardType];
if ([[[draggedFilenames objectAtIndex:0] pathExtension] isEqual:#"xls"]){
return YES;
} else {
return NO;
}
}
- (void)concludeDragOperation:(id <NSDraggingInfo>)sender{
NSArray *draggedFilenames = [[sender draggingPasteboard] propertyListForType:NSFilenamesPboardType];
NSURL *url = [NSURL fileURLWithPath:[draggedFilenames objectAtIndex:0]];
NSString *textDataFile = [NSString stringWithContentsOfURL:url usedEncoding:nil error:nil]; //This text is the original excel text and its getting displayed.
[self setTextToDisplay:textDataFile];
}
I am setting the textDataFile value to a string attribute of that class. Now I am using SSDragDropView attribute value in some other class like-
SSDragDropView *dragView = [SSDragDropView new];
NSLog(#"DragView Value is %#",[dragView textToDisplay]);
But I am getting null each time. Is it like I can not set an attribute value in those delegate methods?
The above problem can be resolved just by declaring a global variable in your SSDragDropView.h class.
#import <Cocoa/Cocoa.h>
NSString *myTextToDisplay;
#interface SSDragDropView : NSView
{
The same can be set inside the desired delegate method
- (void)concludeDragOperation:(id <NSDraggingInfo>)sender {
// .... //Your Code
NSString *textDataFile = [NSString stringWithContentsOfURL:url usedEncoding:nil error:nil];
myTextToDisplay = textDataFile;
// .... //Your Code
}
:)
Add
[dragView registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType, nil]];
- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender{
NSPasteboard *pboard = [sender draggingPasteboard];
NSArray *paths = [pboard propertyListForType:NSFilenamesPboardType];
NSLog(#"%#",paths);
[self setNeedsDisplay: YES];
return NSDragOperationGeneric;
}
Below code will print nil because you are not dragging anything on NSView.
SSDragDropView *dragView = [SSDragDropView new];
NSLog(#"DragView Value is %#",[dragView textToDisplay]);

Archiving and Unarchiving results in Bad Access

I'm having trouble setting up a model object to save the visual state of user generated CALayers in a simple graphics application for the iphone.
I'm attempting to save the background color and frame of all the current layers on screen by passing those properties to model objects which implement the NSCoding protocol and then into an NSMutableArray which the app delegate owns. Then I archive the array with NSKeyedArchiver and store it in the NSUserDefaults.
Each CALayer's backgroundColor property is converted to a UIColor to be encoded by the model object for storage. I think that I'm unarchiving the array incorrectly or not restoring state from the unarchived array correctly. When I attempt to access the UIColor object that was store in the model object, I get an EXC_BAD_ACCESS.
I thought it was possibly a bug with encoding UIColor objects so tried pulling the values out of the CGColorRef with the CGColorGetComponents function and storing them in an array to encode and archive, but I had the same result of bad access after unarchiving, so I think I'm just doing it wrong.
This is my model object:
#interface AILayerData : NSObject <NSCoding> {
UIColor* color;
CGRect frame;
}
#property (retain) UIColor* color;
#property (assign) CGRect frame;
#end
#implementation AILayerData
#synthesize color;
#synthesize frame;
- (void)encodeWithCoder:(NSCoder *)coder;
{
[coder encodeObject:color forKey:#"color"];
[coder encodeCGRect:frame forKey:#"frame"];
}
- (id)initWithCoder:(NSCoder *)coder;
{
self = [[AILayerData alloc] init];
if (self != nil)
{
color = [coder decodeObjectForKey:#"color"];
frame = [coder decodeCGRectForKey:#"frame"];
}
return self;
}
#end
And this is my archiving implementation:
#implementation AppDelegate
- (void)applicationWillTerminate:(UIApplication *)application {
NSArray *layersArray = viewController.view.layer.sublayers;
dataArray = [NSMutableArray array];
for(AILayer *layer in layersArray)
{
AILayerData *layerData = [[AILayerData alloc] init];
layerData.frame = layer.frame;
UIColor *layerColor = [UIColor colorWithCGColor:layer.backgroundColor];
layerData.color = layerColor;
[dataArray addObject:layerData];
[layerData release];
}
[[NSUserDefaults standardUserDefaults] setObject:[NSKeyedArchiver archivedDataWithRootObject:layerDataArray] forKey:#"savedArray"];
}
#end
And here is where I restore state:
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
spaceView = [[AISpaceView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.view = spaceView;
[spaceView release];
spaceView.delegate = self;
NSUserDefaults *currentDefaults = [NSUserDefaults standardUserDefaults];
NSData *dataRepresentingSavedArray = [currentDefaults objectForKey:#"savedArray"];
if (dataRepresentingSavedArray != nil) {
[self restoreStateWithData:dataRepresentingSavedArray];
}
}
- (void)restoreStateWithData:(NSData *)data
{
NSArray *savedLayers = [NSKeyedUnarchiver unarchiveObjectWithData:data];
if (savedLayers != nil) {
NSArray *restoredLayers = [[NSArray alloc] initWithArray:savedLayers];
for(AILayerData *layerDataObject in restoredLayers) {
UIColor *layerColor = layerDataObject.color;
AILayer *newLayer = [[AILayer alloc] init];
newLayer.backgroundColor = layerColor.CGColor;
newLayer.frame = layerDataObject.frame;
newLayer.isSelected = NO;
[self.view.layer addSublayer:newLayer];
[newLayer release];
}
[restoredLayers release];
[spaceView.layer layoutSublayers];
}
}
#end
Any help with this is greatly appreciated. I'm pretty much a noob. I was encoding, archiving and unarching an NSArray of NSNumbers converted from the color's floats in pretty much the same way and getting bad access.
You certainly want to retain the color in initWithCoder:
color = [[coder decodeObjectForKey:#"color"] retain];
or, with the dot syntax as color was declared as a retain property:
self.color = [coder decodeObjectForKey:#"color"];
You are over-releasing layerColor: You don't own it (layerDataObject does), but you are releasing it.
It looks like NSCoder for iPhone doesn't respond to -encodeWithCGRect:
Source: http://17.254.2.129/iphone/library/documentation/Cocoa/Reference/Foundation/Classes/NSCoder_Class/Reference/NSCoder.html

Resources