NSMutableArray of Classes in XCode - xcode

I'm working on an iPhone app and I have defined a class as so:
#interface PlotData : NSObject {
NSString *sProbeID;
NSMutableArray *dataPoints;
}
#property (nonatomic, retain) NSString *sProbeID;
#property (nonatomic, retain) NSMutableArray *dataPoints;
#end
#implementation PlotData
#synthesize sProbeID;
#synthesize dataPoints;
- (void)dealloc {
[sProbeID release];
[dataPoints release];
[super dealloc];
}
#end
In my main code, I need to create a NSMutableArray of this class. I've got the NSMutableArray defined in the main code (called AllTheProbes) and then this code attempts to find the sProbeID and if it doesn't find it, it adds a new PlotData class to the array.
-(void) AddDataPointDictionary:(NSDictionary *)aDict WithProbe:(ProbeObj *)aProbe{
NSLog(#"In AddDataPointDictionary.");
//The first step is to find the probe.
int nProbeLoc = -1;
PlotData *aPlotDataObj;
for (int i=0; i < [self.AllTheProbes count]; i++) {
aPlotDataObj = [self.AllTheProbes objectAtIndex:i];
if (aPlotDataObj.sProbeID == aProbe.sID) {
nProbeLoc = i;
}
}
if (nProbeLoc == -1) {
NSLog(#" Did not find the record for %#.", aProbe.sID);
//We need to add this probe to the array of all probes.
PlotData *newPlot = [[PlotData alloc]init];
newPlot.sProbeID = aProbe.sID;
NSMutableArray *newArr = [[NSMutableArray alloc]initWithCapacity:0];
newPlot.dataPoints = newArr;
[self.AllTheProbes addObject:newPlot];
[newPlot release];
[newArr release];
//set aPlotDataObj equal to the object we just added.
for (int i=0; i < [self.AllTheProbes count]; i++) {
aPlotDataObj = [self.AllTheProbes objectAtIndex:i];
if (aPlotDataObj.sProbeID == aProbe.sID) {
nProbeLoc = i;
}
}
NSLog(#" Found the added record at %d.", nProbeLoc);
aPlotDataObj = [self.AllTheProbes objectAtIndex:nProbeLoc];
}
else{
NSLog(#" Found %#.", aPlotDataObj.sProbeID);
//Use the record we found
aPlotDataObj = [self.AllTheProbes objectAtIndex:nProbeLoc];
}
//Add the dictionary to the plot array
[aPlotDataObj.dataPoints addObject:aDict];
NSLog(#" Point added.");
}
The problem I am having is that the data does not appear to get stored. When a probe is not found, after adding the new PlotData to the AllTheProbes array, the program still does not find the record. Here's the output from the NSLogs.
2011-05-21 09:53:24.600 Stoker Monitor[4545:207] In AddDataPointDictionary.
2011-05-21 09:53:24.601 Stoker Monitor[4545:207] Did not find the record for 7200001259348330.
2011-05-21 09:53:24.601 Stoker Monitor[4545:207] Found the added record at -1.
2011-05-21 09:53:24.602 Stoker Monitor[4545:207] Point added.
Notice that the 3rd output line says it found the added record at -1, which means it did not find it after adding it.
Can anyone tell me what I am doing wrong?
Thanks,
NCGrimbo

Have you alloc, inited the array?
Like so:
NSMutableArray *AllTheProbes = [[NSMutableArray alloc] init];
And why are you calling self.AllTheProbes? shouldnt it just be "AllTheProbes"?
Hope that helps.

Related

PHImagemanager requestImageForAsset memory issue

I want to use PHImagemanager to get all photos on the device.
If I set the targetsize too high the app will crash because of memory warnings. So I tested the request without any use of the returned images and set each image to nil, but still app is crashing. I don't know what I'm doing wrong. Can someone help please?
requestOptions = [[PHImageRequestOptions alloc] init];
requestOptions.deliveryMode = PHImageRequestOptionsDeliveryModeHighQualityFormat;
requestOptions.synchronous = false;
assetsOfPhotos = [PHAsset fetchAssetsWithMediaType: PHAssetMediaTypeImage options: nil];
PHImageManager *manager = [PHImageManager defaultManager];
#autoreleasepool {
for (int i = 0; i <= totalImages - 1; i++) {
PHAsset *asset = assetsOfPhotos[i];
[manager requestImageForAsset: asset
targetSize: CGSizeMake(640, 480)
contentMode: PHImageContentModeDefault
options: requestOptions
resultHandler: ^void(UIImage *image, NSDictionary *info) {
image = nil;
}];
}
}
Setting size to 640x480 crash after about 200 images, 320x240 after about 800 images. As a 640x480 image needs 4 times memory then 320x240 image it seems that the app crashes after the same amount of memory that was allocated. So for me this means that I cannot show more images than 200 imags with 640x480 on the test device, because I cannot free allocated memory.
In order to make your #autoreleasepool work you need to set requestOptions.synchronous to YES, and use your own async queue if you want to make the request operation asynchronously.
Please use #autoreleasepool inside the for loop.
for (int i = 0; i <= totalImages - 1; i++) {
#autoreleasepool {
//Your code
}
}
If you want load all photos that you have in Photos.app and you didn't want iCloud. You can do:
That example works with a collection view.
#interface GalleryViewModel ()
#property (strong, nonatomic) NSMutableArray<PHAsset *> *assets;
#property (strong, nonatomic) PHImageManager *imageManager;
#property (strong, nonatomic) PHImageRequestOptions *requestOptions;
#property (strong, nonatomic) NSMutableArray<UIImage *> *imagesList;
#end
#implementation GalleryViewModel
- (instancetype) initWithContext:(ITXAppContext *)context {
self = [super initWithContext:context];
if (self) {
_assets = [[NSMutableArray alloc] init];
_imageManager = [PHImageManager defaultManager];
_requestOptions = [[PHImageRequestOptions alloc] init];
_imagesList = [[NSMutableArray alloc] init];
}
return self;
}
#pragma mark - Public methods
// ==================================================================================
// Public methods
- (void) viewModelDidLoad {
[self obtainAllPhotos];
}
#pragma mark - Private methods
// ==================================================================================
// Private methods
- (void) obtainAllPhotos {
self.requestOptions.resizeMode = PHImageRequestOptionsResizeModeExact;
self.requestOptions.deliveryMode = PHImageRequestOptionsDeliveryModeHighQualityFormat;
self.requestOptions.synchronous = YES;
self.requestOptions.networkAccessAllowed = NO;
PHFetchOptions *fetchOptions = [[PHFetchOptions alloc] init];
fetchOptions.sortDescriptors = #[[NSSortDescriptor sortDescriptorWithKey:#"creationDate" ascending:NO]];
PHFetchResult<PHAsset *> *result = [PHAsset fetchAssetsWithMediaType:PHAssetMediaTypeImage options:fetchOptions];
__weak GalleryViewModel *weakSelf = self;
[result enumerateObjectsUsingBlock:^(PHAsset * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
[weakSelf.assets addObject:obj];
if (idx >= ([result count] - 1)) {
[weakSelf.viewDelegate setupView];
}
}];
}
#pragma mark - Get data from object
// ==================================================================================
// Get data from object
- (NSInteger) sizeGallery {
if (self.assets) {
return [self.assets count];
}
return 0;
}
- (UIImage *) imagesFromList:(NSInteger) index {
__block UIImage *imageBlock;
[self.imageManager requestImageForAsset:[self.assets objectAtIndex:index] targetSize:CGSizeMake(200, 200) contentMode:PHImageContentModeAspectFit options:self.requestOptions resultHandler:^(UIImage * _Nullable result, NSDictionary * _Nullable info) {
if (result) {
imageBlock = result;
}
}];
return imageBlock;
}
#end

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;

access an UIImage instance variable and display it in UIImageView

I'm trying to access an UIImage instance variable and display it in UIImageView. When I try to NSLog the path I get null. I can manually display a pic through the IB, but I want to do this strictly through code
#import "Deck.h"
#import "Card.h"
#implementation Deck
#synthesize cards;
- (id) init
{
if(self = [super init])
{
cards = [[NSMutableArray alloc] init];
NSInteger aCount, picNum = 0;
for(int suit = 0; suit < 4; suit++)
{
for(int face = 1; face < 14; face++, picNum++)
{
NSString *fileName = [NSString stringWithFormat:#"card_%d", picNum];
NSString *path = [[NSBundle mainBundle] pathForResource:fileName
ofType:#"png"inDirectory:#"/cards"];
NSLog(#"%#", path); //outputs correctly
UIImage *output = [UIImage imageNamed:path];
NSLog(#"%#", output); //outputs null
Card *card = [[Card alloc] initWithFaceValue:(NSInteger)face
countValue:(NSInteger)aCount
suit:(Suit)suit
cardImage:(UIImage *)output];
[cards addObject:card];
}
}
}
return self;
}
I've added a link to show where the pics are found
Link
You don't have to include all the path to the image, if you just put the name, Xcode will automatically look for it.

how to fix uipageviewcontroller exc_bad_access if page turn is not completed?

I'm getting an exc_bad_access in my app and I can't figure out how to fix it.
I just upgraded to Xcode 4.5 and I'm targeting IOS 5.0. This is also my first time using UIPageViewController. I prefer to use storyboard as much as possible.
What I'm trying to do is recreate a golf course flip book that contains an image on which you can scroll and zoom. I've basically got a mashup now of several tutorials that is mostly working;
1) I have setup a UIPageviewController, which loads a UIScrollview, which adds a imageView to it.
2) flipping by gesture or tap is working, scrolling is working, I have pinch zoom working and a custom single and two finger tapping working for zoom in / out.
3) The crash appears when you start to flip the page with a sliding gesture, but then release your finger. This basically cancels the flip but then a msg gets send to a zombied object.
Here is my 'GuideViewController.h', it acts as the datasource as well as the root.
#import <UIKit/UIKit.h>
#import "YardageHoleViewController.h"
#interface GuideViewController : UIViewController <UIPageViewControllerDataSource>
#property (strong, nonatomic) UIPageViewController *pageController;
#property (strong, nonatomic) NSArray *pageContent;
- (YardageHoleViewController *)viewControllerAtIndex:(NSUInteger)index storyboard (UIStoryboard *)storyboard;
- (NSUInteger)indexOfViewController:(YardageHoleViewController *)viewController;
#end
And here is the Implementation
#import "GuideViewController.h"
#import "GolfCourseAppDelegate.h"
#import "Hole.h"
#interface GuideViewController ()
#end
#implementation GuideViewController
#synthesize pageContent = _pageContent;
#synthesize pageController = _pageController;
- (void)viewWillDisappear:(BOOL)animated
{
[[[GolfCourseAppDelegate sharedDelegate] locationManager] stopUpdatingLocation];
}
- (void)viewDidLoad
{
[super viewDidLoad];
[[[GolfCourseAppDelegate sharedDelegate] locationManager] startUpdatingLocation];
[self createContentPages];
NSDictionary *options =
[NSDictionary dictionaryWithObject:
[NSNumber numberWithInteger:UIPageViewControllerSpineLocationMin]
forKey: UIPageViewControllerOptionSpineLocationKey];
self.pageController = [[UIPageViewController alloc]
initWithTransitionStyle:UIPageViewControllerTransitionStylePageCurl navigationOrientation:UIPageViewControllerNavigationOrientationVertical options: options];
//self.pageController.delegate = self;
self.pageController.dataSource = self;
[[self.pageController view] setFrame:[[self view] bounds]];
YardageHoleViewController *initialViewController = [self viewControllerAtIndex:0 storyboard:self.storyboard];
NSArray *viewControllers = [NSArray arrayWithObject:initialViewController];
[self.pageController setViewControllers:viewControllers
direction:UIPageViewControllerNavigationDirectionForward
animated:NO
completion:NULL];
[self addChildViewController:self.pageController];
[[self view] addSubview:[self.pageController view]];
[self.pageController didMoveToParentViewController:self];
}
- (YardageHoleViewController *)viewControllerAtIndex:(NSUInteger)index storyboard:(UIStoryboard *)storyboard
{
NSLog(#"getting data view controller at index: %d", index);
// Return the data view controller for the given index.
if (([self.pageContent count] == 0) || (index >= [self.pageContent count])) {
return nil;
}
// Create a new view controller and pass suitable data.
YardageHoleViewController *yardageHoleViewController = [storyboard instantiateViewControllerWithIdentifier:#"YardageHoleViewController"];
yardageHoleViewController.dataObject = [self.pageContent objectAtIndex:index];
return yardageHoleViewController;
}
- (NSUInteger)indexOfViewController:(YardageHoleViewController *)viewController
{
// Return the index of the given data view controller.
// For simplicity, this implementation uses a static array of model objects and the view controller stores the model object; you can therefore use the model object to identify the index.
NSLog(#"returning indexOfViewController : %d", [self.pageContent indexOfObject:viewController.dataObject]);
return [self.pageContent indexOfObject:viewController.dataObject];
}
#pragma mark - Page View Controller Data Source
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController
{
NSLog(#"getting view controller before view controller");
NSUInteger index = [self indexOfViewController:(YardageHoleViewController *)viewController];
if ((index == 0) || (index == NSNotFound)) {
return nil;
}
index--;
return [self viewControllerAtIndex:index storyboard:viewController.storyboard];
}
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController
{
NSLog(#"getting view controller After view controller");
NSUInteger index = [self indexOfViewController:(YardageHoleViewController *)viewController];
if (index == NSNotFound) {
return nil;
}
index++;
if (index == [self.pageContent count]) {
return nil;
}
return [self viewControllerAtIndex:index storyboard:viewController.storyboard];
}
- (void) createContentPages
{
NSLog(#"creating content Pages");
int totalHoles = [[[GolfCourseAppDelegate appData] objectForKey:#"holes"] count];
NSMutableArray *holeData = [[NSMutableArray alloc] init];
for (int i = 1; i < totalHoles+1; i++)
{
Hole *newHole = [[Hole alloc] initWithHoleNumber:i imageUrl:[NSString stringWithFormat:#"hole%#%d.jpg", (i < 10) ? #"0" : #"", i]];
NSLog(#"Hole image url:%#",newHole.imageUrl);
//int holeNumber = i;
//NSString *imageUrl = [NSString stringWithFormat:#"hole%#%d.jpg", (i < 10) ? #"0" : #"", i];
[holeData addObject:newHole];
}
self.pageContent = [[NSArray alloc] initWithArray:holeData];
NSLog(#"count of holeData %d", self.pageContent.count);
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
Now for the view we are flipping through 'YardageHoleViewController.h'
#import <UIKit/UIKit.h>
#import <MediaPlayer/MediaPlayer.h>
#import <CoreLocation/CoreLocation.h>
#interface YardageHoleViewController : UIViewController <UIScrollViewDelegate, CLLocationManagerDelegate>
#property (strong, nonatomic) IBOutlet UIScrollView *scrollView;
#property (assign, nonatomic) int hole;
#property (assign, nonatomic) int totalHoles;
#property (strong, nonatomic) id dataObject;
#property (strong, nonatomic) IBOutlet UILabel *frontLabel;
#property (strong, nonatomic) IBOutlet UILabel *middleLabel;
#property (strong, nonatomic) IBOutlet UILabel *backLabel;
- (IBAction)nextPage:(id)sender;
- (IBAction)previousPage:(id)sender;
- (IBAction)infoPage:(id)sender;
- (IBAction)homePage:(id)sender;
- (void)updateDistanceDisplay;
- (NSString *)formatDistance:(NSNumber *)distance;
#end
Here you can see I've got a few things going on. There is some sub views to show distance to cup based on location etc. You also see some outlets, I wanted to have buttons at the top to navigate in addition to the gestures, right now that's not working because the gestures are overriding the button taps (another question for later).
So here is the meat and potatoes 'YardageHoleViewController.m'
#import "YardageHoleViewController.h"
#import "GolfCourseAppDelegate.h"
#import "Hole.h"
#interface YardageHoleViewController ()
#property (nonatomic, strong) UIImageView *imageView;
- (void)centerScrollViewContents;
- (void)scrollViewDoubleTapped:(UITapGestureRecognizer*)recognizer;
- (void)scrollViewTwoFingerTapped:(UITapGestureRecognizer*)recognizer;
#end
#implementation YardageHoleViewController
#synthesize scrollView = _scrollView;
#synthesize hole = _hole;
#synthesize totalHoles = _totalHoles;
#synthesize imageView = _imageView;
#synthesize frontLabel = _frontLabel;
#synthesize middleLabel = _middleLabel;
#synthesize backLabel = _backLabel;
#synthesize dataObject = _dataObject;
/* The point of this method is to get around a slight annoyance with UIScrollView, which is: if the scroll view content size is smaller than its bounds, then it sits at the top-left rather than in the center. This method positions the image view such that it is always in the center of the scroll view’s bounds.
*/
- (void)centerScrollViewContents {
CGSize boundsSize = self.scrollView.bounds.size;
CGRect contentsFrame = self.imageView.frame;
if (contentsFrame.size.width < boundsSize.width) {
contentsFrame.origin.x = (boundsSize.width - contentsFrame.size.width) / 2.0f;
} else {
contentsFrame.origin.x = 0.0f;
}
if (contentsFrame.size.height < boundsSize.height) {
contentsFrame.origin.y = (boundsSize.height - contentsFrame.size.height) / 2.0f;
} else {
contentsFrame.origin.y = 0.0f;
}
self.imageView.frame = contentsFrame;
}
- (void)scrollViewDoubleTapped:(UITapGestureRecognizer*)recognizer {
CGPoint pointInView = [recognizer locationInView:self.imageView];
CGFloat newZoomScale = self.scrollView.zoomScale * 1.5f;
newZoomScale = MIN(newZoomScale, self.scrollView.maximumZoomScale);
CGSize scrollViewSize = self.scrollView.bounds.size;
CGFloat w = scrollViewSize.width / newZoomScale;
CGFloat h = scrollViewSize.height / newZoomScale;
CGFloat x = pointInView.x - (w / 2.0f);
CGFloat y = pointInView.y - (h / 2.0f);
CGRect rectToZoomTo = CGRectMake(x, y, w, h);
[self.scrollView zoomToRect:rectToZoomTo animated:YES];
}
- (void)scrollViewTwoFingerTapped:(UITapGestureRecognizer*)recognizer {
// Zoom out slightly, capping at the minimum zoom scale specified by the scroll view
CGFloat newZoomScale = self.scrollView.zoomScale / 1.5f;
newZoomScale = MAX(newZoomScale, self.scrollView.minimumZoomScale);
[self.scrollView setZoomScale:newZoomScale animated:YES];
}
- (UIView*)viewForZoomingInScrollView:(UIScrollView *)scrollView {
// Return the view that you want to zoom
return self.imageView;
}
- (void)scrollViewDidZoom:(UIScrollView *)scrollView {
// The scroll view has zoomed, so you need to re-center the contents
[self centerScrollViewContents];
}
- (void)viewDidLoad {
[super viewDidLoad];
Hole *hole = (Hole*)self.dataObject;
self.hole = hole.holeNumber;
UIImage *image = [UIImage imageNamed:hole.imageUrl];
self.imageView = [[UIImageView alloc] initWithImage:image];
self.imageView.frame = (CGRect){.origin=CGPointMake(0.0f, 0.0f), .size=image.size};
[self.scrollView addSubview:self.imageView];
self.scrollView.contentSize = image.size;
//Here you’re setting up two gesture recognizers: one for the double-tap to zoom in, and one for the two-finger-tap to zoom out.
UITapGestureRecognizer *doubleTapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(scrollViewDoubleTapped:)];
doubleTapRecognizer.numberOfTapsRequired = 2;
doubleTapRecognizer.numberOfTouchesRequired = 1;
[self.scrollView addGestureRecognizer:doubleTapRecognizer];
UITapGestureRecognizer *twoFingerTapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(scrollViewTwoFingerTapped:)];
twoFingerTapRecognizer.numberOfTapsRequired = 1;
twoFingerTapRecognizer.numberOfTouchesRequired = 2;
[self.scrollView addGestureRecognizer:twoFingerTapRecognizer];
[[[GolfCourseAppDelegate sharedDelegate] locationManager] setDelegate:self];
[self updateDistanceDisplay];
self.totalHoles = [[[GolfCourseAppDelegate appData] objectForKey:#"holes"] count];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(didRotate:) name:#"UIDeviceOrientationDidChangeNotification" object:nil];
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
CGRect scrollViewFrame = self.scrollView.frame;
CGFloat scaleWidth = scrollViewFrame.size.width / self.scrollView.contentSize.width;
self.scrollView.minimumZoomScale = scaleWidth;
self.scrollView.maximumZoomScale = 1.5f;
self.scrollView.zoomScale = scaleWidth;
[self centerScrollViewContents];
}
- (void) didRotate:(NSNotification *)notification {
UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
if (orientation == UIDeviceOrientationLandscapeLeft || orientation == UIDeviceOrientationLandscapeRight) {
[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleBlackTranslucent];
NSString *moviePath = [[NSBundle mainBundle] pathForResource:[NSString stringWithFormat:#"hole%#%d", (self.hole < 10) ? #"0" : #"", self.hole] ofType:#"mp4"];
MPMoviePlayerViewController *viewController = [[MPMoviePlayerViewController alloc] initWithContentURL:[NSURL fileURLWithPath:moviePath]];
viewController.moviePlayer.controlStyle = MPMovieControlStyleNone;
viewController.view.backgroundColor = [UIColor blackColor];
[self presentMoviePlayerViewControllerAnimated:viewController];
} else {
[self dismissMoviePlayerViewControllerAnimated];
}
}
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {
[self updateDistanceDisplay];
}
- (void) updateDistanceDisplay {
CLLocation *userLocation = [[GolfCourseAppDelegate sharedDelegate] userLocation];
if (userLocation != nil) {
NSMutableDictionary *holeLocations = [[[GolfCourseAppDelegate appData] objectForKey:#"holes"] objectForKey:[NSString stringWithFormat:#"hole%d", self.hole]];
if (round([[[holeLocations objectForKey:#"front"] objectForKey:#"lat"] floatValue]) == 0) {
self.frontLabel.text = #"---";
} else {
CLLocation *frontLocation = [[CLLocation alloc] initWithLatitude:[[[holeLocations objectForKey:#"front"] objectForKey:#"lat"] floatValue] longitude:[[[holeLocations objectForKey:#"front"] objectForKey:#"lng"] floatValue]];
if (([frontLocation distanceFromLocation:userLocation]/1000)>1000){
self.frontLabel.text = #"Out of Range";
}else{
self.frontLabel.text = [self formatDistance:[NSNumber numberWithFloat:([frontLocation distanceFromLocation:userLocation]/1000)]];
}
}
if (round([[[holeLocations objectForKey:#"middle"] objectForKey:#"lat"] floatValue]) == 0) {
self.middleLabel.text = #"---";
} else {
CLLocation *middleLocation = [[CLLocation alloc] initWithLatitude:[[[holeLocations objectForKey:#"middle"] objectForKey:#"lat"] floatValue] longitude:[[[holeLocations objectForKey:#"middle"] objectForKey:#"lng"] floatValue]];
self.middleLabel.text = [self formatDistance:[NSNumber numberWithFloat:([middleLocation distanceFromLocation:userLocation]/1000)]];
}
if (round([[[holeLocations objectForKey:#"back"] objectForKey:#"lat"] floatValue]) == 0) {
self.backLabel.text = #"---";
} else {
CLLocation *backLocation = [[CLLocation alloc] initWithLatitude:[[[holeLocations objectForKey:#"back"] objectForKey:#"lat"] floatValue] longitude:[[[holeLocations objectForKey:#"back"] objectForKey:#"lng"] floatValue]];
self.backLabel.text = [self formatDistance:[NSNumber numberWithFloat:([backLocation distanceFromLocation:userLocation]/1000)]];
}
}
}
- (NSString *) formatDistance:(NSNumber *)distance {
NSNumber *displayDistance;
NSString *unitSuffix = #"";
// Convert km to yards if prefs say so.
NSUserDefaults *preferences = [NSUserDefaults standardUserDefaults];
if ([[preferences stringForKey:#"measurementUnit"] isEqualToString:#"meters"]) {
distance = [NSNumber numberWithFloat:([distance floatValue]*1000.0)];
if ([distance floatValue] < 1000.0) {
displayDistance = distance;
unitSuffix = #"";
} else {
displayDistance = [NSNumber numberWithFloat:([distance floatValue]/1000.0)];
unitSuffix = #"km";
}
} else {
distance = [NSNumber numberWithFloat:([distance floatValue]*1.0936133*1000.0)];
if ([distance floatValue] < 1760.0) {
displayDistance = distance;
unitSuffix = #"";
} else {
displayDistance = [NSNumber numberWithFloat:([distance floatValue]/1760.0)];
unitSuffix = #"mi";
}
}
NSNumberFormatter *decimalStyle = [[NSNumberFormatter alloc] init];
[decimalStyle setFormatterBehavior:NSNumberFormatterBehavior10_4];
[decimalStyle setNumberStyle:NSNumberFormatterDecimalStyle];
[decimalStyle setRoundingMode:NSNumberFormatterRoundFloor];
[decimalStyle setRoundingIncrement:[NSNumber numberWithFloat:1.0]];
NSString *finalDistance = [decimalStyle stringFromNumber:displayDistance];
return [NSString stringWithFormat:#"%#%#", finalDistance, unitSuffix];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
- (void)viewDidUnload {
[self setImageView:nil];
[self setScrollView:nil];
[self setFrontLabel:nil];
[self setBackLabel:nil];
[self setBackLabel:nil];
[self setFrontLabel:nil];
[self setMiddleLabel:nil];
[super viewDidUnload];
}
- (IBAction)nextPage:(id)sender {
//TODO
// [((UIPageViewController*)self.parentViewController) setViewControllers:
// target direction:UIPageViewControllerNavigationForward completion:nil];
}
- (IBAction)previousPage:(id)sender {
//TODO
// [((UIPageViewController*)self.parentViewController) setViewControllers:<#(NSArray *)#> direction:UIPageViewControllerNavigationDirectionReverse animated:true completion:nil];
}
- (IBAction)infoPage:(id)sender {
//TODO
}
- (IBAction)homePage:(id)sender {
[self.navigationController popViewControllerAnimated:YES];
}
#end
Whew! lots of reading. So what I tried first was to set the exception break point. No luck. Then I added lots of NSlog statements to see where we crash, and then finally looked for zombies in instruments. Here we see on the malloc "An Objective-C message was sent to a deallocated object (zombie) at address: 0x1386e0e0" in the YardageHoleViewController.
From my NSLog statements I can see that a successful page turn looks like this;
2012-12-16 13:33:52.280 BAP Template[1365:13a03] getting data view controller at index: 0
//started flip right here >
2012-12-16 13:34:06.289 BAP Template[1365:13a03] getting view controller After view controller
2012-12-16 13:34:06.290 BAP Template[1365:13a03] returning indexOfViewController : 0
2012-12-16 13:34:06.292 BAP Template[1365:13a03] getting data view controller at index: 1
and here is what happens when you start to flip then release
2012-12-16 13:36:18.613 BAP Template[1365:13a03] getting data view controller at index: 0
//started flip then released
2012-12-16 13:36:21.828 BAP Template[1365:13a03] getting view controller After view controller
2012-12-16 13:36:21.829 BAP Template[1365:13a03] returning indexOfViewController : 0
2012-12-16 13:36:21.831 BAP Template[1365:13a03] getting data view controller at index: 1
So in some sense it is trying to act like it completed the flip, but we didn't, and then that's when we have a bad time =(
I've set everything to strong, and I really don't know what to try next?
Any suggestions on my code in general would really be appreciated. Thanks in advance!
UPDATE I looked at the crash log in organizer
Thread 0 name: Dispatch queue: com.apple.main-thread
Thread 0 Crashed:
0 libobjc.A.dylib 0x3737bf78 objc_msgSend + 16
1 CoreLocation 0x3405ddc0 -[CLLocationManager onClientEventLocation:] + 1136
2 CoreLocation 0x3405d77e -[CLLocationManager onClientEvent:supportInfo:] + 194
3 CoreLocation 0x34057e38 __CLClientInvokeCallback_block_invoke_0 + 48
I've since commented out the two lines that start/stop updating location in viewDidLoad / willDisappear in guideViewController.
No more crashing, but why?
Since location updating is started when the view loads, the method
- (void)locationManager:didUpdateToLocation:fromLocation:
gets called continuously in the simulator, like every second, however on the device it only gets called when it detects movement. Inside that method was the call [self updateDistanceDisplay] and that is why it was crashing. The fix I've implemented works well on the device now but it is not bulletproof.
First, in the appDelegate, we want to fix the locationManager.distanceFilter so that not every minor movement triggers the delegate method.
self.locationManager.distanceFilter = 1.0f;
Next, a modification to the didUpdateToLocation method to only update the display if there was a change in the latitude or longitude.
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {
if ((newLocation.coordinate.latitude!=oldLocation.coordinate.latitude)||(newLocation.coordinate.longitude!=oldLocation.coordinate.longitude))
[self updateDistanceDisplay];
}
Again, not bulletproof. There is a case where say user is traveling in a golf cart fast enough and trying to flip pages, we could get into the crash state.

xcode calculator app method

I'm doing a calculator app base on a online tutorial, itunes.apple. com/itunes-u/ipad-iphone-application- development/ id473757255 (tut 2)
I followed every step closely and all was fine, until the finishing step of a method call performOperation. When I build and run, the numbers and enter function work fine. Only the operation method is not working. So I presume that the main trouble is with the operation method.
BrainCalculator.h
#interface CalculatorBrain : NSObject
-(void) pushOperand: (double)operand;
-(double) performOperation: (NSString*) operation;
#end
BrainCalculator.m
#import "CalculatorBrain.h"
#interface CalculatorBrain()
#property (nonatomic, strong) NSMutableArray* _operandStack;
#end
#implementation CalculatorBrain
#synthesize _operandStack;
-(NSMutableArray *)operandStack
{
if (!_operandStack){
_operandStack= [[NSMutableArray alloc ]init];
}
return _operandStack;
}
-(void)pushOperand:(double)operand{
NSNumber *operandObject = [NSNumber numberWithDouble:operand];
[self.operandStack addObject:operandObject];
}
-(double)popOperand{
NSNumber *operandObject= [self.operandStack lastObject];
if (operandObject) [self.operandStack removeLastObject];
return [operandObject doubleValue];
}
-(double)performOperation:(NSString *)operation
{
double result = 0;
if ([operation isEqualToString:#"+"]){
result=[self popOperand] + [self popOperand];
}else if ([#"*" isEqualToString:operation]){
result = [self popOperand] * [self popOperand];
}else if ([operation isEqualToString:#"-"]){
double subtrahend = [self popOperand];
result = [self popOperand] - subtrahend;
}else if( [operation isEqualToString:#"/"]){
double divisor = [self popOperand];
if (divisor) result = [self popOperand] / divisor;
}
[self pushOperand:result];
return result;
}
#end
Initially, it seem to me that the performOperation method was pretty fishy, so I tried fiddling the
}else if ([#"*" isEqualToString:operation]){
to
}else if ([operation isEqualToString:#"*"]){
hoping it would work, but sadly it didn't.
Just for additional information
viewcontroller.m
#import "CalculatorViewController.h"
#import "CalculatorBrain.h"
#interface CalculatorViewController ()
#property (nonatomic) BOOL userIsInTheMiddleOfEnteringANumber;
#property (nonatomic, strong) CalculatorBrain *brain;
#end
#implementation CalculatorViewController
#synthesize display;
#synthesize userIsInTheMiddleOfEnteringANumber;
#synthesize brain= _brain;
-(CalculatorBrain*)brain
{
if(!_brain)_brain = [[CalculatorBrain alloc]init];
return _brain;
}
- (IBAction)digitPressed:(UIButton *)sender {
NSString * digit= [ sender currentTitle];
if (userIsInTheMiddleOfEnteringANumber){
self.display.text = [self.display.text stringByAppendingString:digit];
}
else{
self.display.text=digit;
self.userIsInTheMiddleOfEnteringANumber = YES;
}
}
- (IBAction)enterPressed {
[self.brain pushOperand:[self.display.text doubleValue]];
self.userIsInTheMiddleOfEnteringANumber = NO;
}
- (IBAction)operationPressed:(UIButton *)sender {
if (self.userIsInTheMiddleOfEnteringANumber){
[self enterPressed];
}
NSString *operation = [sender currentTitle];
double result = [self.brain performOperation:operation];
self.display.text = [NSString stringWithFormat:#"%g", result];
}
#end
help will be greatly appreciated as I'm practising xcode to prepare myself for my final year major project.

Resources