Accessing data from an NSDictionary - objective-c

I'm using the following method to access data about an object. The first NSLog shows all of the data. The second NSLog shows the 'frame' data which comes out as: NSRect: {{168, 102}, {5, 5}}
How can I access the first set of coordinates from the NSRect and then the abscissa from the first pair?
-(void) moveTheShape:(NSTimer*)timer
{
NSDictionary *userInfo = [timer userInfo];
NSLog(#"Info: %#",userInfo);
//Info: <Shape: 0x68b6c30; frame = (151 352; 5 5); layer = <CALayer: 0x68b6c00>>
NSDictionary *frame = [userInfo valueForKey:#"frame"];
NSLog(#"frame: %#", frame);
//NSRect: {{168, 102}, {5, 5}}
}
CORRECT SOLUTION:
-(void) moveTheShape:(NSTimer*)timer
{
Shape *userInfo = [timer userInfo];
NSLog(#"Info: %#",userInfo);
CGPoint origin = userInfo.frame.origin;
NSLog(#"result: %f", origin.x);
}

The CGRect is stored as NSValue
You will need to get the CGRectValue from the NSValue
Use The following
NSValue *value = [userInfo valueForKey:#"frame"];
CGRect rect = [value CGRectValue];

If my hunch about the type of userInfo is correct:
-(void) moveTheShape:(NSTimer*)timer
{
Shape *userInfo = [timer userInfo];
CGPoint origin = userInfo.frame.origin;
// Do stuff with origin
}

int abscissa = frame.origin.x; will get you the x value. You use frame.size.width and frame.size.height to get the sizes.

Ha, abscissa, my new word for the day!
Anyhow, do you mean CGRect, as an iPhone uses CoreGraphics, not FoundationKit for all UIKit rectangles.
As for the accessing the struct's internal vars (CGPoint and CGSize to be exact), just use regular old dot notation after storing the frame into a CGRect var like so:
NSValue *value = [userInfo objectForKey:#"frame"];
CGRect frameVar = [value CGRectValue];
frameVar.origin = CGPointMake(someCoord, someOtherCoord);

Related

How to Take screen of desktop wallpaper without icons and windows for multiple screens?

In my cocoa application i want to take the screenshot of desktop wallpaper for Multiple screens i've found this link with this i made for single screen However, i am finding difficult for multiple screens. So how would i do it for multiple screens?
here is my code
- (NSImage*) desktopAsImageWithScreen:(NSScreen *)screen {
NSDictionary *deviceDescription = [screen deviceDescription];
CFMutableArrayRef windowIDs = CFArrayCreateMutable(NULL, 0, NULL);
CFArrayRef allWindowIDs = CGWindowListCreate(kCGWindowListOptionOnScreenBelowWindow, kCGDesktopIconWindowLevel);
NSLog(#"min window level: %d", kCGMinimumWindowLevel);
NSLog( #"looking for windows on level: %d", kCGDesktopWindowLevel );
NSLog( #"NOt actually looking for windows on level: %d", kCGMinimumWindowLevel );
NSLog( #"NOt actually looking for windows on the dock level: %d", kCGDockWindowLevel );
if (allWindowIDs)
{
CFArrayRef windowDescs = CGWindowListCreateDescriptionFromArray(allWindowIDs);
for (CFIndex idx=0; idx<CFArrayGetCount(windowDescs); idx++)
{
CFDictionaryRef dict = CFArrayGetValueAtIndex(windowDescs, idx);
CFStringRef ownerName = CFDictionaryGetValue(dict, kCGWindowOwnerName);
NSLog(#"owner name = %#", ownerName);
if (CFStringCompare(ownerName, CFSTR("Dock"), 0) == kCFCompareEqualTo )
{
// the Dock level has the dock and the desktop picture
CGRect windowBounds;
BOOL success = CGRectMakeWithDictionaryRepresentation(
(CFDictionaryRef)(CFDictionaryGetValue(dict, kCGWindowBounds)),
&windowBounds);
NSRect screenBounds = screen.frame;
CFNumberRef windowLayer = CFDictionaryGetValue(dict, kCGWindowLayer);
//CFDictionaryGetValue(dict, kCGWindowBounds)
NSLog(#"window bounds %f, %f, matches screen bounds? %d, on level %#",
windowBounds.size.width,
windowBounds.size.height,
CGRectEqualToRect(windowBounds, screenBounds),
windowLayer
);
NSNumber* ourDesiredLevelNumber = [NSNumber numberWithInt: kCGDesktopWindowLevel - 1];
if ( CGRectEqualToRect(windowBounds, screenBounds) && [ourDesiredLevelNumber isEqualToNumber: (__bridge NSNumber * _Nonnull)(windowLayer)] )
CFArrayAppendValue(windowIDs, CFArrayGetValueAtIndex(allWindowIDs, idx));
}
}
CFRelease(windowDescs);
CFRelease(allWindowIDs);
}
CGImageRef cgImage = CGWindowListCreateImageFromArray( screen.frame, windowIDs, kCGWindowImageDefault);
// Create a bitmap rep from the image...
NSBitmapImageRep *bitmapRep = [[NSBitmapImageRep alloc] initWithCGImage:cgImage];
// Create an NSImage and add the bitmap rep to it...
NSImage *image = [[NSImage alloc] init];
[image addRepresentation:bitmapRep];
//[bitmapRep release];
return image;
}
Any Suggestions?
Thanks in Advance!

custom Annotations being switched when reloaded on MKMapView

I've been having this issue for a couple of weeks now, and I still have not found an answer. on my MapView I have custom annotations, and when I hit the "reload button" all the information is correct as in the annotation "title, subtitle". but the annotation has changed. the annotations are in a NSMutableArray and I'm sure that the issue i am having revolves around that. here is the code I am using to reload the annotations.
so just prevent any confusion, my custom annotations work just fine when i first load the mapView. But once i hit the reload button, all the annotation's information like "location,title, subtitle" all that is correct, just the actual annotation has changed. Like all the annotations have been switched around.
if anyone can help, it would greatly be appreciated! thanks!
- (IBAction)refreshMap:(id)sender {
NSArray *annotationsOnMap = myMapView.annotations;
[myMapView removeAnnotations:annotationsOnMap];
[locations removeAllObjects];
[citiesArray removeAllObjects];
[self retrieveData];
}
-(void) retrieveData {
userLAT = [NSString stringWithFormat:#"%f", myMapView.userLocation.coordinate.latitude];
userLNG = [NSString stringWithFormat:#"%f", myMapView.userLocation.coordinate.longitude];
NSString *fullPath = [mainUrl stringByAppendingFormat:#"map_json.php?userID=%#&lat=%#&lng=%#",theUserID,userLAT,userLNG];
NSURL * url =[NSURL URLWithString:fullPath];
NSData *data = [NSData dataWithContentsOfURL:url];
json =[NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
citiesArray =[[NSMutableArray alloc]init];
for (int i = 0; i < json.count; i++)
{
//create city object
NSString * eID =[[json objectAtIndex:i] objectForKey:#"userid"];
NSString * eAddress =[[json objectAtIndex:i] objectForKey:#"full_address"];
NSString * eHost =[[json objectAtIndex:i] objectForKey:#"username"];
NSString * eLat =[[json objectAtIndex:i] objectForKey:#"lat"];
NSString * eLong =[[json objectAtIndex:i] objectForKey:#"lng"];
NSString * eName =[[json objectAtIndex:i] objectForKey:#"Restaurant_name"];
NSString * eState = [[json objectAtIndex:i] objectForKey:#"type"];
NSString * annotationPic = [[json objectAtIndex:i] objectForKey:#"Annotation"];
NSString * eventID = [[json objectAtIndex:i] objectForKey:#"id"];
//convert lat and long from strings
float floatLat = [eLat floatValue];
float floatLONG = [eLong floatValue];
City * myCity =[[City alloc] initWithRestaurantID: (NSString *) eID andRestaurantName: (NSString *) eName andRestaurantState: (NSString *) eState andRestaurantAddress: (NSString *) eAddress andRestaurantHost: eHost andRestaurantLat: (NSString *) eLat andRestaurantLong: (NSString *) eLong];
//Add our city object to our cities array
// Do any additional setup after loading the view.
[citiesArray addObject:myCity];
//Annotation
locations =[[NSMutableArray alloc]init];
CLLocationCoordinate2D location;
Annotation * myAnn;
//event1 annotation
myAnn =[[Annotation alloc]init];
location.latitude = floatLat;
location.longitude = floatLONG;
myAnn.coordinate = location;
myAnn.title = eName;
myAnn.subtitle = eHost;
myAnn.type = eState;
myAnn.AnnotationPicture = annotationPic;
myAnn.passEventID = eventID;
myAnn.hotZoneLevel = hotZone;
[locations addObject:myAnn];
[self.myMapView addAnnotations:locations];
}
}
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation
{
if([annotation isKindOfClass:[MKUserLocation class]])
return nil;
static NSString *annotationIdentifier = #"AnnotationIdentifier";
MKAnnotationView *annotationView = (MKAnnotationView *) [self.myMapView
dequeueReusableAnnotationViewWithIdentifier:annotationIdentifier];
if (!annotationView)
{
annotationView = [[MKAnnotationView alloc]
initWithAnnotation:annotation
reuseIdentifier:annotationIdentifier];
NSString *restaurant_Icon = ((Annotation *)annotation).AnnotationPicture;
NSString *restaurant_Callout = [NSString stringWithFormat:#"mini.%#",restaurant_Icon];
UIImage *oldImage = [UIImage imageNamed:restaurant_Icon];
UIImage *newImage;
CGSize newSize = CGSizeMake(75, 75);
newImage = [oldImage imageScaledToFitSize:newSize]; // uses MGImageResizeScale
annotationView.image= newImage;
annotationView.canShowCallout = YES;
UIImage *Mini_oldImage = [UIImage imageNamed:event_Callout];
UIImage *Mini_newImage;
CGSize Mini_newSize = CGSizeMake(30,30);
Mini_newImage = [Mini_oldImage imageScaledToFitSize:Mini_newSize]; // uses MGImageResizeScale
UIImageView *finalMini_callOut = [[UIImageView alloc] initWithImage:Mini_newImage];
annotationView.leftCalloutAccessoryView = finalMini_callOut;
UIButton* rightButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
annotationView.rightCalloutAccessoryView = rightButton;
}
else
{
annotationView.annotation = annotation;
}
return annotationView;
}
If nothing else, you're setting the icon and the callout based upon the annotation, but only doing that in viewForAnnotation if the annotation was not dequeued. You really want to do any annotation-specific customization outside of that if block, in case an annotation view is reused.
Unrelated to your reported issue, there are a few other observations:
You probably should be doing retrieveData asynchronously so you don't tie up the main thread with your data retrieval/parsing. Go ahead and dispatch the adding of the entry to your array and adding the annotation to the map in the main queue, but the network stuff and should definitely be done asynchronously.
You probably should check to make sure data is not nil (e.g. no network connection or some other network error) because JSONObjectWithData will crash if you pass it a nil value.
Your use of locations seems unnecessary because you're resetting it for every entry in your JSON. You could either (a) retire locations entirely and just add the myAnn object to your map's annotations; or (b) initialize locations before the for loop. But it's probably misleading to maintain this ivar, but only populate it with the last annotation.

Creating an animated GIF in Cocoa - defining frame type

I've been able to adapt some code found on SO to produce an animated GIF from the "screenshots" of my view, but the results are unpredictable. GIF frames are sometimes full images, full frames ("replace" mode, as GIMP marks it), other times are just a "diff" from previous layer ("combine" mode).
From what I've seen, when there are fewer and/or smaller frames involved, the CG writes the GIF in "combine" mode, but failing to get the colors right. Actually, the moving parts are colored correctly, the background is wrong.
When CG saves the GIF as full frames, the colors are ok. The file size is larger, but hey, obviously you cannot have the best of both worlds. :)
Is there a way to either:
a) force CG to create "full frames" when saving the GIF
b) fix the colors (color table?)
What I do is (ARC mode):
capture the visible part of the view with
[[scrollView contentView] dataWithPDFInsideRect:[[scrollView contentView] visibleRect]];
convert and resize it to NSImageBitmapRep of PNG type
-(NSMutableDictionary*) pngImageProps:(int)quality {
NSMutableDictionary *pngImageProps;
pngImageProps = [[NSMutableDictionary alloc] init];
[pngImageProps setValue:[NSNumber numberWithBool:NO] forKey:NSImageInterlaced];
double compressionF = 1;
[pngImageProps setValue:[NSNumber numberWithFloat:compressionF] forKey:NSImageCompressionFactor];
return pngImageProps;
}
-(NSData*) resizeImageToData:(NSData*)data toDimX:(int)xdim andDimY:(int)ydim withQuality:(int)quality{
NSImage *image = [[NSImage alloc] initWithData:data];
NSRect inRect = NSZeroRect;
inRect.size = [image size];
NSRect outRect = NSMakeRect(0, 0, xdim, ydim);
NSImage *outImage = [[NSImage alloc] initWithSize:outRect.size];
[outImage lockFocus];
[image drawInRect:outRect fromRect:inRect operation:NSCompositeCopy fraction:1];
NSBitmapImageRep* bitmapRep = [[NSBitmapImageRep alloc] initWithFocusedViewRect:outRect];
[outImage unlockFocus];
NSMutableDictionary *imageProps = [self pngImageProps:quality];
NSData* imageData = [bitmapRep representationUsingType:NSPNGFileType properties:imageProps];
return [imageData copy];
}
get the array of BitmapReps and create the GIF
-(CGImageRef) pngRepDataToCgImageRef:(NSData*)data {
CFDataRef imgData = (__bridge CFDataRef)data;
CGDataProviderRef imgDataProvider = CGDataProviderCreateWithCFData (imgData);
CGImageRef image = CGImageCreateWithPNGDataProvider(imgDataProvider, NULL, true, kCGRenderingIntentDefault);
return image;
}
////////// create GIF from
NSArray *images; // holds all BitmapReps
CGImageDestinationRef destination = CGImageDestinationCreateWithURL((__bridge CFURLRef)[NSURL fileURLWithPath:pot],
kUTTypeGIF,
allImages,
NULL);
// set frame delay
NSDictionary *frameProperties = [NSDictionary
dictionaryWithObject:[NSDictionary
dictionaryWithObject:[NSNumber numberWithFloat:0.2f]
forKey:(NSString *) kCGImagePropertyGIFDelayTime]
forKey:(NSString *) kCGImagePropertyGIFDictionary];
// set gif color properties
NSMutableDictionary *gifPropsDict = [[NSMutableDictionary alloc] init];
[gifPropsDict setObject:(NSString *)kCGImagePropertyColorModelRGB forKey:(NSString *)kCGImagePropertyColorModel];
[gifPropsDict setObject:[NSNumber numberWithBool:YES] forKey:(NSString *)kCGImagePropertyGIFHasGlobalColorMap];
// set gif loop
NSDictionary *gifProperties = [NSDictionary
dictionaryWithObject:gifPropsDict
forKey:(NSString *) kCGImagePropertyGIFDictionary];
// loop through frames and add them to GIF
for (int i=0; i < [images count]; i++) {
NSData *imageData = [images objectAtIndex:i];
CGImageRef imageRef = [self pngRepDataToCgImageRef:imageData];
CGImageDestinationAddImage(destination, imageRef, (__bridge CFDictionaryRef) (frameProperties));
}
// save the GIF
CGImageDestinationSetProperties(destination, (__bridge CFDictionaryRef)(gifProperties));
CGImageDestinationFinalize(destination);
CFRelease(destination);
I've checked the ImageBitmapReps, when saved as PNG individually, they are just fine.
As I understood, the color tables should be handled by CG or am I responsible to produce the dithered colors? How to do that?
Even when doing the same animation repeatedly, the GIFs produced may vary.
This is a single BitmapRep
(source: andraz.eu)
And this is the GIF with the invalid colors ("combine" mode)
(source: andraz.eu)
I read your code. Please double check the "allImages" while you are creating the CGImageDestinationRef, and the "[images count]".
the follow test code works fine:
NSDictionary *prep = [NSDictionary dictionaryWithObject:[NSDictionary dictionaryWithObject:[NSNumber numberWithFloat:0.2f] forKey:(NSString *) kCGImagePropertyGIFDelayTime] forKey:(NSString *) kCGImagePropertyGIFDictionary];
CGImageDestinationRef dst = CGImageDestinationCreateWithURL((__bridge CFURLRef)(fileURL), kUTTypeGIF, [filesArray count], nil);
for (int i=0;i<[filesArray count];i++)
{
//load anImage from array
...
CGImageRef imageRef=[anImage CGImageForProposedRect:nil context:nil hints:nil];
CGImageDestinationAddImage(dst, imageRef,(__bridge CFDictionaryRef)(prep));
}
bool fileSave = CGImageDestinationFinalize(dst);
CFRelease(dst);

CGRectIntersection requires scalar type?

I am trying to check collisions of objects from a NSMutableArray against another object (using a CGRect) but it keeps saying the method requires a scalar type?!
Here is the method throwing the error:
-(void) checkSquareToCircleCollisions{
NSMutableArray *array = [squares getSquares];
for(int i = 0; i < [squares getCount]; i++){
Square *s = [array objectAtIndex: i];
CGRect rect1 = [player getRect];
CGRect rect2 = [s getRect];
//if(CGRectIntersection(rect1, rect2)){
//[player setAlive: NO];
// }
}
}
Use CGRectIntersectsRect, not CGRectIntersection.
CGRectIntersectsRect returns a boolean: YES if the rectangles intersect. CGRectIntersection returns the CGRect that is the overlap (if any) between the two rectangles.
if (CGRectIntersectsRect(playerRect, squareRect)) {
player.alive = NO;
}

Annotations in MapView

I have this code:
-(void)handleLongPressGesture:(UIGestureRecognizer*)sender {
NSNumber* existingpoints = [[NSNumber alloc]init];
existingpoints =[NSNumber numberWithInt:0];
// This is important if you only want to receive one tap and hold event
if (sender.state == UIGestureRecognizerStateEnded)
{
[self.mapView removeGestureRecognizer:sender];
}
else {
do {
int z = 1;
existingpoints =[NSNumber numberWithInt:z];
// Here we get the CGPoint for the touch and convert it to latitude and longitude coordinates to display on the map
CGPoint point = [sender locationInView:self.mapView];
CLLocationCoordinate2D locCoord = [self.mapView convertPoint:point toCoordinateFromView:self.mapView];
// Then all you have to do is create the annotation and add it to the map
MKPointAnnotation *annotationPoint = [[MKPointAnnotation alloc] init]; annotationPoint.coordinate = locCoord;
NSString *latitude = [[NSString alloc] initWithFormat:#"%f",locCoord.latitude];
NSString *longitude = [[NSString alloc] initWithFormat:#"%f", locCoord.longitude];
annotationPoint.title = #"Event";
annotationPoint.subtitle = [NSString stringWithFormat:#"%# & %#", latitude, longitude];
[mapView addAnnotation:annotationPoint];
[[NSUserDefaults standardUserDefaults]setObject:latitude forKey:#"FolderLatitude"];
[[NSUserDefaults standardUserDefaults]setObject:longitude forKey:#"FolderLongitude"];
} while ([existingpoints intValue] == 0);
}
}
...but the problem is that when I hold, and then drag more than one pin is added. I want to add only one pin. So I tried the do method but it doesn't work. I can't understand, because when I executed the code I turn the value of the NSNumber to 1, and the while says = 0 to run the code.
Please Help!!
Your current code is prone to have quite a number of memory leaks. For example:
NSNumber* existingpoints = [[NSNumber alloc] init];
existingpoints = [NSNumber numberWithInt:0];
Is leaking because you leave the first instance of existingpoints with retain value of 1 and not freeing it anywhere. Unless you're using ARC. You can optimize the above code with just one instruction:
NSNumber* existingpoints = [NSNumber numberWithInt:0];
And retain it if you need to keep it somewhere (but i belive it's not the case).
Analyzing the code, I'd recommend NOT to use existingpoints as an NSNumber. Use an NSInteger instead (which is not an object, just a typedef to long).
Here's my rewritten code:
-(void)handleLongPressGesture:(UIGestureRecognizer*)sender {
NSInteger existingpoints = 0;
// This is important if you only want to receive one tap and hold event
if (sender.state == UIGestureRecognizerStateEnded) {
[self.mapView removeGestureRecognizer:sender];
}
else {
do {
int z = 1;
existingpoints = z;
// Here we get the CGPoint for the touch and convert it to latitude and longitude coordinates to display on the map
CGPoint point = [sender locationInView:self.mapView];
CLLocationCoordinate2D locCoord = [self.mapView convertPoint:point toCoordinateFromView:self.mapView];
// Then all you have to do is create the annotation and add it to the map
MKPointAnnotation *annotationPoint = [[MKPointAnnotation alloc] init];
annotationPoint.coordinate = locCoord;
NSString *latitude = [NSString stringWithFormat:#"%f",locCoord.latitude];
NSString *longitude = [NSString stringWithFormat:#"%f", locCoord.longitude];
annotationPoint.title = #"Event";
annotationPoint.subtitle = [NSString stringWithFormat:#"%# & %#", latitude, longitude];
[mapView addAnnotation:annotationPoint];
[[NSUserDefaults standardUserDefaults] setObject:latitude forKey:#"FolderLatitude"];
[[NSUserDefaults standardUserDefaults] setObject:longitude forKey:#"FolderLongitude"];
[annotationPoint release]; // Remove this if you're using ARC.
} while (existingpoints == 0);
}
}
Note that I've also changed the code for creating latitude and longitude for not to create any memory leaks when using ARC.
EDIT:
Further analyzing your code, I don't see why this method would be dropping two pins at once. Maybe you could check if your method is not being called twice?
More: Why do you have a do/while loop if you just want it to run once? (but maybe you're just paving your ground to further ahead)