I'm starting to get brave and am attempting to populate a JBChartView (specifically a line chart) into my application for statistics tracking. I've never worked with a graphing library before but I did my best to look into them and this one seemed very robust. Problem is I'm having trouble getting the plots to work correctly from test data.
When I run the code as follows below, I get [__NSCFConstantString objectAtIndex:] unrecognized selector sent to instance. The offending line is at verticalValueforHorizontalIndex but I can't figure out another way to produce any data. Does anyone have any experience with this or can anyone help me figure out why I'm getting so much trouble here?
Implementation File
#import "waterStatsViewController.h"
#import "JBLineChartView.h"
#import "JBChartView.h"
#import "JBBarChartView.h"
#import "JBChartHeaderView.h"
#import "JBLineChartFooterView.h"
typedef NS_ENUM(NSInteger, JBLineChartLine){
JBLineChartLineSolid,
JBLineChartLineDashed,
JBLineChartLineCount
};
#interface waterStatsViewController ()
- (void)initData;
#end
#implementation waterStatsViewController
- (id)init
{
self = [super init];
if (self)
{
[self initData];
}
return self;
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self)
{
[self initData];
}
return self;
}
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self)
{
[self initData];
}
return self;
}
- (void) initData
{
// DEFINE ARRAYS
testArray1 = [[NSArray alloc] initWithObjects:#"1", #"2", #"3", #"4", #"5", #"6", #"7", #"8", #"9", #"10", nil];
testArray2 = [[NSArray alloc] initWithObjects:#"1", #"2", #"3", #"4", #"5", #"6", #"7", #"8", #"9", #"10", nil];
// CREATE MUTABLE ARRAY FOR LINES IN CHART
NSMutableArray *mutableLineCharts = [NSMutableArray array];
// AS LONG AS LINEINDEX IS LESS THEN THE LINE COUNT, INCREASE THE LINE COUNT AND EXECUTE CODE
for (int lineIndex = 0; lineIndex<JBLineChartLineCount; lineIndex++)
{
// CREATE MUTABLE ARRAY FOR DATA IN CHART
NSMutableArray *mutableChartData = [NSMutableArray array];
// AS LONG AS INT I IS LESS THEN THE COUNT OF OBJECTS IN TEST ARRAY 2; INCREASE COUNT AND EXECUTE CODE
for (int i = 0; i < testArray2.count; i++)
{
// ADD OBJECTS FROM TEST ARRAY 2 TO CHART DATA
[mutableChartData addObjectsFromArray:testArray2];
}
// TAKE OBJECTS FROM MUTABLE CHART DATA AND ADD THEM TO OHHHHHH FOR EACH ITEM YOU ADD ANOTHER LINE
[mutableLineCharts addObjectsFromArray:mutableChartData];
}
}
- (void)viewDidLoad
{
self.title = #"Water Quality";
_chartView = [[JBLineChartView alloc] init];
_chartView.delegate = self;
_chartView.dataSource = self;
_chartView.state = 0;
_chartView.backgroundColor = [UIColor blackColor];
_chartView.showsLineSelection = YES;
_chartView.showsVerticalSelection = YES;
_headerView = [[JBChartHeaderView alloc] initWithFrame:CGRectMake(0, 64, 320, 30)];
_chartView.frame = CGRectMake(0, 94, 320, 300);
_footerView = [[JBLineChartFooterView alloc] initWithFrame:CGRectMake(0, 404, 320, 30)];
_headerView.titleLabel.text = #"Alkalinity";
_headerView.titleLabel.textColor = [UIColor whiteColor];
_footerView.leftLabel.text = [testArray1 firstObject];
_footerView.rightLabel.text = [testArray1 lastObject];
_footerView.leftLabel.textColor = [UIColor whiteColor];
_footerView.rightLabel.textColor = [UIColor whiteColor];
_footerView.backgroundColor = [UIColor blackColor];
_footerView.sectionCount = [testArray1 count];
// THIS IS THE VIEW WHEN THE USER INTERACTS WITH THE CHART
/*
_informationView = [[JBChartInformationView alloc] initWithFrame:CGRectMake(0, 0, 40, 300)];
[_informationView setBackgroundColor:[UIColor grayColor]];*/
[_chartView setMinimumValue:1.0f];
[_chartView setMaximumValue:20.0f];
[self.view addSubview:_footerView];
[self.view addSubview:_headerView];
[self.view addSubview:_chartView];
// [self.view addSubview:_informationView];
[_chartView reloadData];
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
}
- (BOOL)lineChartView:(JBLineChartView *)lineChartView showsDotsForLineAtLineIndex:(NSUInteger)lineIndex;
{
return YES;
}
- (NSUInteger)numberOfLinesInLineChartView:(JBLineChartView *)lineChartView;
{
return 1;
}
- (NSUInteger)lineChartView:(JBLineChartView *)lineChartView numberOfVerticalValuesAtLineIndex:(NSUInteger)lineIndex;
{
return 1;
}
- (CGFloat)lineChartView:(JBLineChartView *)lineChartView verticalValueForHorizontalIndex:(NSUInteger)horizontalIndex atLineIndex:(NSUInteger)lineIndex;
{
return [[[testArray2 objectAtIndex:lineIndex] objectAtIndex:horizontalIndex] floatValue];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
A few issues:
Don't store numeric values as strings in your test arrays.
Remove the mutableLineCharts code; this is leftover from the demo and not needed for your purposes.
I assume you want two lines? One dashed and one solid? If so, return JBLineChartLineCount instead of 1 for numberOfLinesInLineChartView.
Return the numeric value for testArray1 or testArray2 in verticalValueForHorizontalIndex.
Initializing Data
- (void) initData
{
testArray1 = #[#(1), #(2), #(3), #(4), #(5), #(6), #(7), #(8), #(9), #(10)];
testArray2 = #[#(11), #(12), #(13), #(14), #(15), #(16), #(17), #(18), #(19), #(20)];
}
Line Count
- (NSUInteger)numberOfLinesInLineChartView:(JBLineChartView *)lineChartView;
{
return JBLineChartLineCount;
}
Data Count
- (NSUInteger)lineChartView:(JBLineChartView *)lineChartView numberOfVerticalValuesAtLineIndex:(NSUInteger)lineIndex;
{
if (lineIndex == JBLineChartLineSolid)
{
return [self.testArray1 count];
}
else
{
return [self.testArray2 count];
}
return 0;
}
Data Value
- (CGFloat)lineChartView:(JBLineChartView *)lineChartView verticalValueForHorizontalIndex:(NSUInteger)horizontalIndex atLineIndex:(NSUInteger)lineIndex;
{
if (lineIndex == JBLineChartLineSolid)
{
NSNumber *value = (NSNumber *)[self.testArray1 objectAtIndex:horizontalIndex];
return [value floatValue];
}
else
{
NSNumber *value = (NSNumber *)[self.testArray2 objectAtIndex:horizontalIndex];
return [value floatValue];
}
return 0;
}
Hope this helps.
Related
Here is my code I have tried
(void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor=[UIColor whiteColor];
NSLog(#"----------------------------%ld",self.studyArray.count);//value is nil
[self createUI];
}
(NSMutableArray *)studyArray{
if (!_studyArray) {
_studyArray = [[NSMutableArray alloc]init];
}
return _studyArray;
}
(void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
[self loadStudyData];
}
(void)loadStudyData{
if ([aa isEqualToString:#"10000"]) {
NSDictionary *info =[responseObj objectForKey:#"info"];
NSLog(#"%#",info);
for (NSDictionary * dict in info) {
StudyModel * studyModel = [StudyModel new];
[studyModel setValuesForKeysWithDictionary:dict];
[self.studyArray addObject:studyModel];
}
NSLog(#"-----------------%ld",self.studyArray.count);// got an value
}
}
I am using onegray's Radio button class in one of my projects. the one mentioned here: Best radio-button implementation for IOS
I am using these radio buttons for my answer choices on a quiz. when the user clicks the next button, the labels are populated with new choices. the only problem is that the old ones dont disappear. So when I click next, the new set of buttons are placed on top of the old ones.
what is the simplest way to first check to see if they already exist.. and if so.. delete them.. before displaying the new ones?
here is my code.
#interface LABViewControllerQuiz ()
#end
#implementation LABViewControllerQuiz
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
int counter =0;
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
_fileContents = [NSString stringWithContentsOfFile:[[NSBundle mainBundle]pathForResource:#"quizQuestions" ofType:#"txt"] encoding:NSUTF8StringEncoding error: nil];
_theScanner = [NSScanner scannerWithString:_fileContents];
_separator = [NSCharacterSet characterSetWithCharactersInString:#"~"];
_lineBreak =[NSCharacterSet characterSetWithCharactersInString:#"#"];
_alreadyGeneratedNumbers =[[NSMutableArray alloc]init];
_numQuestions =0;
_userAnswers = [[NSMutableArray alloc]init];
_answerKey = [[NSMutableArray alloc]init];
[self nextQuestion:nil];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
/*
#pragma mark - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/
- (IBAction)nextQuestion:(UIButton *)sender
{
_NextQuestionButton.enabled = YES;
_submitButton.enabled = NO;
NSLog(#"NumQuestion = %d", _numQuestions);
if (_numQuestions >9)
{
_NextQuestionButton.enabled = NO;
_submitButton.enabled = YES;
}else
{
int r = arc4random() %20;
while ([_alreadyGeneratedNumbers containsObject:[NSNumber numberWithInt:r]])
{
r = arc4random() %20;
}
[_alreadyGeneratedNumbers addObject:[NSNumber numberWithInt:r]];
while(![_theScanner isAtEnd])
{
NSLog(#"Location= %d", [_theScanner scanLocation]);
NSLog(#"Already Generated numbers:");
int i =0;
while (i < [_alreadyGeneratedNumbers count])
{
NSLog(#"%#", [_alreadyGeneratedNumbers objectAtIndex:i]);
i++;
}
NSString *line;
_lineArray = [[NSMutableArray alloc] init];
[_theScanner scanUpToCharactersFromSet:_lineBreak intoString:&line];
[_theScanner setCharactersToBeSkipped:_lineBreak];
NSScanner *inner = [NSScanner scannerWithString:line];
NSString *word;
int wordCount = 0;
NSLog(#"r = %d counter = %d", r, counter);
if (counter ==r)
{
while(![inner isAtEnd])
{
[inner scanUpToCharactersFromSet:_separator intoString:&word];
[inner setCharactersToBeSkipped:_separator];
[_lineArray insertObject:word atIndex:wordCount];
_questionText.text = [NSString stringWithFormat:#"Question %d \n %#", _numQuestions +1,[_lineArray objectAtIndex:0]];
wordCount++;
[_theScanner setScanLocation:0];
counter = 0;
}
[sender setHidden:YES];
NSMutableArray* buttons = [NSMutableArray arrayWithCapacity:4];
CGRect btnRect = CGRectMake(25, 420, 300, 30);
for (NSString* optionTitle in #[[_lineArray objectAtIndex:1], [_lineArray objectAtIndex:2], [_lineArray objectAtIndex:3], [_lineArray objectAtIndex:4]])
{
RadioButton* btn = [[RadioButton alloc] initWithFrame:btnRect];
[btn addTarget:self action:#selector(onRadioButtonValueChanged:) forControlEvents:UIControlEventValueChanged];
btnRect.origin.y += 40;
[btn setTitle:optionTitle forState:UIControlStateNormal];
[btn setTitleColor:[UIColor darkGrayColor] forState:UIControlStateNormal];
btn.titleLabel.font = [UIFont boldSystemFontOfSize:17];
[btn setImage:[UIImage imageNamed:#"unchecked.png"] forState:UIControlStateNormal];
[btn setImage:[UIImage imageNamed:#"checked.png"] forState:UIControlStateSelected];
btn.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft;
btn.titleEdgeInsets = UIEdgeInsetsMake(0, 6, 0, 0);
[self.view addSubview:btn];
[buttons addObject:btn];
}
[buttons[0] setGroupButtons:buttons]; // Setting buttons into the group
[buttons[0] setSelected:NO]; // Making the first button initially selected
NSLog(#"the question is = %#", [_lineArray objectAtIndex:0]);
//NSLog(#"Line arrayINDEX %d = %#", wordCount,[_lineArray objectAtIndex:wordCount]); _numQuestions ++;
break;
}else
{
counter ++;
}
}
}
[_answerKey addObject:[_lineArray objectAtIndex:5]];
}
-(void) onRadioButtonValueChanged:(RadioButton*)sender
{
// Lets handle ValueChanged event only for selected button, and ignore for deselected
if(sender.selected)
{
NSLog(#"Selected: %#", sender.titleLabel.text);
}
}
Save buttons as an instance variable. You're already adding all your buttons into the array, you just throw the array out for some reason.
#interface LABViewControllerQuiz ()
#property (strong) NSMutableArray *buttons;
#end
And then this line:
NSMutableArray* buttons = [NSMutableArray arrayWithCapacity:4];
Becomes these lines:
if (self.buttons) {
[self.buttons makeObjectsPerformSelector:#selector(removeFromSuperview)];
[self.buttons removeAllObjects];
} else {
self.buttons = [NSMutableArray arrayWithCapacity:4];
}
First off: My project is ARC enabled and I'm using storyboard.
I have a view controller that pushes a segue (modal),
[self performSegueWithIdentifier: #"goInitialSettings" sender: self];
there i'm setting some parameters and store them. When the parameters are stored (true a button tap), the app should return to the original viewcontroller.
This i am doing with this command:
[self.presentingViewController dismissViewControllerAnimated:NO completion:^{}];
I'm noticing that the viewcontroller that i dismiss, never deallocs. How does this come?
I'm adding the code of the 'presented viewcontroller' below:
#interface CenterChoiceController ()
{
UIView* _titleBackground;
UILabel* _lblTitle;
UIButton* _btnGaVerder;
UIPickerView* _myPickerView;
NSArray* _centers;
UILabel* _adresLine;
UILabel* _cityLine;
MKPointAnnotation* _point;
MKMapView* _mapView;
UIActivityIndicatorView* _indicator;
UIAlertView* _alert;
GCenter* _center;
DataManager* _dm;
}
#end
#implementation CenterChoiceController
-(void)dealloc
{
NSLog(#"Centerchoice deallocs");
_titleBackground = nil;
_lblTitle = nil;
_btnGaVerder = nil;
_myPickerView = nil;
_point = nil;
_mapView = nil;
_indicator = nil;
_alert = nil;
_centers = nil;
_adresLine = nil;
_cityLine = nil;
_center = nil;
_dm = nil;
}
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
_dm = [[DataManager alloc]init];
if([_dm hasConnectivity])
{
[_dm fetchCentersForController:self];
}
else
{
[self pushErrorMessage:NSLocalizedString(#"nointernetconnection", nil)];
}
CAGradientLayer *bgLayer = [BackgroundLayer blueGradient];
bgLayer.frame = self.view.bounds;
[self.view.layer insertSublayer:bgLayer atIndex:0];
_titleBackground = [[UIView alloc]initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 44)];
_titleBackground.backgroundColor = [GColor blueColor];
[self.view addSubview:_titleBackground];
_lblTitle = [[UILabel alloc]initWithFrame:CGRectMake(0, 0, self.view.frame.size.width - 10, 44)];
_lblTitle.textAlignment = NSTextAlignmentRight;
_lblTitle.textColor = [GColor whiteColor];
_lblTitle.text = NSLocalizedString(#"bioscoopkeuze", nil);
[self.view addSubview:_lblTitle];
_btnGaVerder = [[UIButton alloc]initWithFrame:CGRectMake(0, self.view.frame.size.height - 54, self.view.frame.size.width, 54)];
[_btnGaVerder setTitle:NSLocalizedString(#"gaverder", nil) forState:UIControlStateNormal];
_btnGaVerder.titleLabel.font = [_btnGaVerder.titleLabel.font fontWithSize:12];
_btnGaVerder.backgroundColor = [GColor blueColor];
[_btnGaVerder setTitleColor:[GColor whiteColor] forState:UIControlStateNormal];
[_btnGaVerder setShowsTouchWhenHighlighted:YES];
[_btnGaVerder addTarget:self action:#selector(gaVerder) forControlEvents:UIControlEventTouchUpInside];
_myPickerView = [[UIPickerView alloc]initWithFrame:CGRectMake(0, 44, self.view.frame.size.width, 200)];
}
-(void)showLoading
{
NSLog(#"shows loading");
_indicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
CGPoint cntr = self.view.center;
_indicator.center = cntr;
[_indicator startAnimating];
[self.view addSubview:_indicator];
}
-(void)hideLoading
{
NSLog(#"hides loading");
[_indicator removeFromSuperview];
_indicator = nil;
}
-(void)pushData:(NSArray *)data
{
[self.view addSubview:_btnGaVerder];
[self.view addSubview:_myPickerView];
_centers = data;
_myPickerView.delegate = self;
_myPickerView.dataSource = self;
_dm = [[DataManager alloc]init];
GSettings* settings = [_dm loadSettings];
if(settings == nil)
{
settings = [[GSettings alloc]init];
settings.chosenCenter = [_centers objectAtIndex:0];
settings.loadedCenter = [_centers objectAtIndex:0];
_center = settings.chosenCenter;
settings.notificationsEnabled = YES;
[self changeAddressLines];
}
/*if(settings != nil)
{
GCenter* loaded = settings.loadedCenter;
int i = 0;
BOOL found = NO;
while(i < [_centers count] && !found)
{
GCenter* center = (GCenter*)[_centers objectAtIndex:i];
if(settings.loadedCenter.iD == center.iD)
{
_center = center;
settings.chosenCenter = center;
[_dm storeSettings:settings];
found = YES;
}
i++;
}
//[self.myPickerView selectRow:i-1 inComponent:0 animated:NO];
loaded = nil;
[self changeAddressLines];
}
*/
}
-(void) pushErrorMessage: (NSString*) errorMessage
{
_alert = [[UIAlertView alloc] initWithTitle:NSLocalizedString(#"fout", nil) message:errorMessage delegate:self cancelButtonTitle:#"Ok" otherButtonTitles:nil];
_alert.delegate = self;
[_alert show];
}
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
if(buttonIndex == 0)
{
if(self.navigationController != nil)
{
[self.navigationController popViewControllerAnimated:YES];
}
else
{
//[self initializeData];
}
}
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(void)viewWillDisappear:(BOOL)animated
{
[_dm cancelCenterRequest];
/*if(self.tabBarController != nil)
{
dm = [[DataManager alloc]init];
settings = [dm loadSettings];
if([dm hasConnectivity])
{
settings.lastUpdated = nil;
[dm storeSettings:settings];
}
if(settings.loadedCenter.centerCode != settings.chosenCenter.centerCode)
{
UIStoryboard *mystoryboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
SplashScreenController *controller = [mystoryboard instantiateViewControllerWithIdentifier:#"root"];
[self presentViewController:controller animated:YES completion:nil];
}
dm = nil;
settings = nil;
}
*/
}
-(void)gaVerder
{
_dm = [[DataManager alloc]init];
GSettings* settings = [_dm loadSettings];
if(settings == nil)
{
settings = [[GSettings alloc]init];
settings.notificationsEnabled = YES;
}
if(_center != nil)
{
settings.chosenCenter = _center;
}
[_dm storeSettings:settings];
[_mapView removeFromSuperview];
_mapView = nil;
_titleBackground = nil;
_lblTitle = nil;
_btnGaVerder = nil;
_myPickerView = nil;
_point = nil;
_indicator = nil;
_alert = nil;
_centers = nil;
_adresLine = nil;
_cityLine = nil;
_center = nil;
_dm = nil;
[self.presentingViewController dismissViewControllerAnimated:NO completion:^{}];
//DEZE BLIJFT HELAAS IN HET GEHEUGEN HANGEN... GEEN OPLOSSING GEVONDEN
//[self.navigationController popViewControllerAnimated:NO];
}
//PICKERVIEWDELEGATE EN DATASOURCE
// returns the number of 'columns' to display.
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView
{
return 1;
}
// returns the # of rows in each component..
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component
{
return [_centers count];
}
- (UILabel *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(UIView *)view
{
GCenter* center = (GCenter*)[_centers objectAtIndex:row];
NSString* string = center.name;
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, pickerView.frame.size.width, 44)];
label.textColor = [GColor blueColor];
label.font = [label.font fontWithSize:18];
label.text = string;
label.textAlignment = NSTextAlignmentCenter;
return label;
}
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component
{
_center = (GCenter*)[_centers objectAtIndex:row];
[self changeAddressLines];
}
-(void)changeAddressLines
{
if (_mapView != nil)
{
[_mapView removeAnnotation:_point];
}
[_adresLine removeFromSuperview];
[_cityLine removeFromSuperview];
_adresLine = nil;
_cityLine = nil;
CGRect rctAdres = CGRectMake(0, _myPickerView.frame.origin.y + _myPickerView.frame.size.height -10, self.view.frame.size.width, 20);
_adresLine = [[UILabel alloc]initWithFrame:rctAdres];
_adresLine.textAlignment = NSTextAlignmentCenter;
_adresLine.textColor = [GColor greyColor];
_adresLine.text = _center.street;
CGRect rctCity = CGRectMake(0, rctAdres.origin.y + rctAdres.size.height, self.view.frame.size.width, 20);
_cityLine = [[UILabel alloc]initWithFrame:rctCity];
_cityLine.textAlignment = NSTextAlignmentCenter;
_cityLine.textColor = [GColor greyColor];
_cityLine.font = [_cityLine.font fontWithSize:14];
_cityLine.text = _center.city;
[self.view addSubview:_adresLine];
[self.view addSubview:_cityLine];
if(_mapView == nil)
{
double height;
height = _btnGaVerder.frame.origin.y - _cityLine.frame.origin.y - _cityLine.frame.size.height;
CGRect mapRect = CGRectMake(0, _cityLine.frame.origin.y+3 + _cityLine.frame.size.height, self.view.frame.size.width, height);
_mapView = [[MKMapView alloc]initWithFrame:mapRect];
[self.view addSubview:_mapView];
}
CLLocationCoordinate2D punt;
punt.latitude = _center.latitude;
punt.longitude = _center.longitude;
_point = [[MKPointAnnotation alloc] init];
[_point setCoordinate:punt];
_mapView.centerCoordinate = punt;
_point.title = _center.name;
[_mapView addAnnotation:_point];
[_mapView setCenterCoordinate:punt animated:YES];
MKCoordinateRegion theRegion = _mapView.region;
theRegion.span.longitudeDelta = 0.005;
theRegion.span.latitudeDelta = 0.005;
[_mapView setRegion:theRegion animated:YES];
}
#end
In my case it was a little more complicated. I don't have any variable that has strong reference to my view controller, and my view controller is not a strong delegate to any property/variable contained inside this class itself. After some hard thinking and trials, I found my issue was caused by a NSTimer object defined in the interface. The timer object itself is non-repeatable, but the method invoked by it will schedule the timer again at the end, which as you can imagine would reference this method defined in my view controller again, thus causing circular references. To break out of this loop, I had to invalidate the timer before I dismiss my view controller.
As a summary, these are cases when a view controller can be blocked from deallocating after it is dismissed:
The view controller is being strongly referenced by some outside object;
The view controller is a strong delegate referenced by some object defined within the view controller itself
The dismissViewControllerAnimated:completion: block may reference to self or it has some other code block that may cause a circular references
The view controller has NSTimer objects which can invoke some methods which re-schedules the timer
There could be more, but hopefully we can capture a lot of cases with the above cases.
If your view controller is not deallocated after it is dismissed, there's probably a strong reference to that view controller somewhere in your code. ARC will always deallocate objects that doesn't have strong reference anymore.
I'm new in IOS developpement and I have a problem drawing the legend of a pieChart with Core-Plot.The pie chart is well displayed but it doesn't display its legend. I've already read a lot about this issue (StackOverFlowResponseTopic) but I can't find why it doesn't work with me.
I've created a PieChartView that I initialse in my second ViewController of a tabBar application.
This is the code of my pie chart view:
//
// PieChartView.m
// ExerciceRecap
//
// Created by Alex on 02/03/12.
// Copyright (c) 2012 __MyCompanyName__. All rights reserved.
//
#import "PieChartView.h"
#import "ModeleDeDonnes.h"
#import "CorePlot-CocoaTouch.h"
#implementation PieChartView
#synthesize graph=_graph;
#synthesize hostingView = _hostingView;
#synthesize graphData = _graphData;
#synthesize myLabels = _myLabels;
- (id) initWithHostingView:(CPTGraphHostingView *)hostingView andData:(NSMutableArray *)data{
self = [super init];
if (self != nil){
self.hostingView = hostingView;
self.graph = nil;
// Manage data from dataController
_myLabels = [[[NSMutableArray alloc]init]autorelease];
NSMutableArray *myValues = [[[NSMutableArray alloc]init]autorelease];
for (NSUInteger i = 0; i < [data count]; i++) {
ModeleDeDonnes *theObject = [data objectAtIndex:i];
[_myLabels addObject:theObject.nom];
[myValues addObject:theObject.montant];
}
self.graphData = myValues;
}
return self;
}
-(void)initialisePieChart{
if ([_graphData count] == 0 ){
NSLog(#"No data");
return;
}
if((self.hostingView == nil) || (self.graphData == nil)){
NSLog(#" Cannot initialse hostingView");
return;
}
if (self.graph != nil){
NSLog(#" graph already exists");
return;
}
CGRect frame = [self.hostingView bounds];
self.graph = [[[CPTXYGraph alloc] initWithFrame:frame] autorelease];
//Tie the graph we have created with the hosting view
self.hostingView.hostedGraph = self.graph;
CPTPieChart * pieChart = [[[CPTPieChart alloc]init]autorelease];
pieChart.dataSource = self;
pieChart.pieRadius = 100.0;
pieChart.identifier = #"pieChart1";
pieChart.startAngle = M_PI_2;
pieChart.sliceDirection = CPTPieDirectionCounterClockwise;
_graph.title=#"My PieChart";
// Add legend
CPTLegend *theLegend = [CPTLegend legendWithGraph:_graph];
theLegend.numberOfColumns = 2;
theLegend.fill = [CPTFill fillWithColor:[CPTColor redColor]];
//theLegend.borderLineStyle = [CPTLineStyle lineStyle];
theLegend.cornerRadius = 5.0;
_graph.legend = theLegend;
_graph.legendAnchor = CPTRectAnchorBottom;
_graph.legendDisplacement = CGPointMake(0.0, 30.0);
[self.graph addPlot:pieChart];
}
-(NSUInteger)numberOfRecordsForPlot:(CPTPlot *)plot{
return [self.graphData count];
}
-(NSNumber *)numberForPlot:(CPTPlot *)plot field:(NSUInteger)fieldEnum recordIndex:(NSUInteger)index{
return [self.graphData objectAtIndex:index];
}
-(NSString *)legendTitleForPieChart:(CPTPieChart *)pieChart
recordIndex:(NSUInteger)index{
NSLog(#"LegendTitleForPieChart");
return [NSString stringWithFormat:#"Ma légende", index];
}
#end
And this is the code of the viewController
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
self.pieChart =[[PieChartView alloc] initWithHostingView:_myGraphView andData:_dataController.masterDataList];
[self.pieChart initialisePieChart];
}
Thanks a lot and sry for my bad english.
if you can't get CorePlot to do what you want it to, there are several alternatives you might consider:
What are the alternatives to Core-Plot for drawing graphs in iPhone SDK
I'd recommend ShinobiCharts personally, but admittedly I am biased!
Hope this helps :)
Try to reload data of the hosting view in the viewWillAppear method of your viewController:
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
self.pieChart =[[PieChartView alloc] initWithHostingView:_myGraphView andData:_dataController.masterDataList];
[self.pieChart initialisePieChart];
[self.pieChart.hostingView reloadData];
}
When my view is trying to load, the applications crashes and i get this stack:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[NSURL initFileURLWithPath:]: nil string parameter'
This is my code, i don't really know where to look when this crash is occurred:
#import "Itineraire.h"
#import "SBMapView.h"
#import "SBRouteAnnotation.h"
#import "City.h"
#import "UICGRoutes.h"
#import "SBCheckPointViewController.h"
//#import "SBRouteDetailView.h"
#interface Itineraire(Private)
-(void)customInitialization;
#end
#implementation Itineraire(Private)
-(void)customInitialization
{
// do the initialization of class variables here..
mDirections = [UICGDirections sharedDirections];
mDirections.delegate = self;
}
#end
#implementation Itineraire
#synthesize map = mMap;
#synthesize startPoint = mStartPoint;
#synthesize endPoint = mEndPoint;
#synthesize loadBtn = mLoadBtn;
#synthesize annotationArray = mAnnotationArray;
#synthesize travelMode = mTravelMode;
#synthesize destination;
#synthesize routes;
#synthesize mAnnotations;
#synthesize mRouteArray;
#synthesize mRouteDetail;
//Invoked when the class is instantiated in XIB
-(id)initWithCoder:(NSCoder*)aDecoder
{
self = [super initWithCoder:aDecoder];
if( self)
{
[self customInitialization];
}
return self;
}
// The designated initializer. Override if you create the controller programmatically and want to perform customization that is not appropriate for viewDidLoad.
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
if ((self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil])) {
[self customInitialization];
}
return self;
}
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
[super viewDidLoad];
self.title = #"Google Maps";
self.map = [[SBMapView alloc] initWithFrame:CGRectMake(0, 49, self.view.frame.size.width, self.view.frame.size.height)];
//self.map = [[SBMapView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 381)];
[self.view addSubview:mMap];
self.view.backgroundColor = [UIColor blackColor];
self.annotationArray = [[NSMutableArray alloc]init];
self.routes = [[UICGRoutes alloc]init];
if (mDirections.isInitialized) {
[self updateRoute];
}
}
/*
// Override to allow orientations other than the default portrait orientation.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
*/
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
- (void)viewDidUnload {
[super viewDidUnload];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:YES];
}
- (void)viewWillDisappear:(BOOL)animated;
{
[super viewWillDisappear:YES];
}
#pragma mark -
#pragma mark Instance Methods
- (void)updateRoute
{ /*
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
UICGDirectionsOptions *options = [[[UICGDirectionsOptions alloc] init] autorelease];
options.travelMode = mTravelMode;
City *mFirstCity = [[[City alloc]init] autorelease];
mFirstCity.mCityName = #"Paris";//mStartPoint;
//[mDirections loadWithStartPoint:mFirstCity.mCityName endPoint:destination options:options];
//added
NSMutableArray * DestinationCityArray = [[NSMutableArray alloc]init];
[DestinationCityArray addObject:#"Berlin"];
[mDirections loadWithStartPoint:mFirstCity.mCityName endPoint:destination options:options];
*/
//
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
UICGDirectionsOptions *options = [[UICGDirectionsOptions alloc] init] ;
options.travelMode = mTravelMode;
City *mFirstCity = [[City alloc]init];
mFirstCity.mCityName = #"Amsterdam";//mStartPoint;
NSMutableArray *dest=[[NSMutableArray alloc]init];
[dest addObject:#"Berlin"];
[mDirections loadWithStartPoint:mFirstCity.mCityName endPoint:dest options:options];
}
-(void)loadRouteAnnotations
{
self.mRouteArray = [mDirections routeArray];
NSLog(#"mRouteArray %#",mRouteArray);
self.mAnnotations = [[NSMutableArray alloc]init];
for (int idx = 0; idx < [mRouteArray count]; idx++) {
NSArray *_routeWayPoints1 = [[mRouteArray objectAtIndex:idx] wayPoints];
NSArray *mPlacetitles = [[mRouteArray objectAtIndex:idx] mPlaceTitle];
self.annotationArray = [NSMutableArray arrayWithCapacity:[_routeWayPoints1 count]-2];
mLoadBtn.title = #"OFF";
mLoadBtn.target = self;
mLoadBtn.action = #selector(removeRouteAnnotations);
for(int idx = 0; idx < [_routeWayPoints1 count]-1; idx++)
{
mBetweenAnnotation = [[SBRouteAnnotation alloc] initWithCoordinate:[[_routeWayPoints1 objectAtIndex:idx]coordinate]
title:[mPlacetitles objectAtIndex:idx]
annotationType:SBRouteAnnotationTypeWayPoint];
[self.annotationArray addObject:mBetweenAnnotation];
}
[mAnnotations addObject:mAnnotationArray];
[self.map.mapView addAnnotations:[mAnnotations objectAtIndex:idx]];
NSLog(#"map %#",mMap);
}
}
-(void)showCheckpoints
{
SBCheckPointViewController *_Controller = [[SBCheckPointViewController alloc]initWithNibName:#"SBCheckPoints" bundle:nil];
[self.navigationController pushViewController:_Controller animated:YES];
NSMutableArray *arr = [[mDirections checkPoint] mPlaceTitle];
_Controller.mCheckPoints = arr ;
}
//
-(void)removeRouteAnnotations
{
NSMutableArray *mTempAnnotation;// = [mAnnotations retain];
for (int idx = 0; idx < [mTempAnnotation count]; idx++) {
[mMap.mapView removeAnnotations:[mTempAnnotation objectAtIndex:idx] ];
}
mLoadBtn.title = #"ON";
mLoadBtn.target = self;
mLoadBtn.action = #selector(loadRouteAnnotations);
}
#pragma mark <UICGDirectionsDelegate> Methods
- (void)directionsDidFinishInitialize:(UICGDirections *)directions {
[self updateRoute];
}
- (void)directions:(UICGDirections *)directions didFailInitializeWithError:(NSError *)error {
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Map Directions" message:[error localizedFailureReason] delegate:nil cancelButtonTitle:nil otherButtonTitles:#"OK", nil];
[alertView show];
}
- (void)directionsDidUpdateDirections:(UICGDirections *)indirections {
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
UICGPolyline *polyline = [indirections polyline];
NSArray *routePoints = [polyline routePoints];
[mMap loadRoutes:routePoints]; // Loads route by getting the array of all coordinates in the route.
UIToolbar *tools = [[UIToolbar alloc]
initWithFrame:CGRectMake(0.0f, 0.0f, 103.0f, 44.01f)]; // 44.01 shifts it up 1px for some reason
tools.clearsContextBeforeDrawing = NO;
tools.clipsToBounds = NO;
tools.tintColor = [UIColor colorWithWhite:0.305f alpha:0.0f]; // closest I could get by eye to black, translucent style.
// anyone know how to get it perfect?
tools.barStyle = -1; // clear background
NSMutableArray *buttons = [[NSMutableArray alloc] initWithCapacity:2];
// Create a standard Load button.
self.loadBtn = [[UIBarButtonItem alloc]initWithTitle:#"ON"
style:UIBarButtonItemStyleBordered
target:self
action:#selector(loadRouteAnnotations)];
[buttons addObject:mLoadBtn];
// Add Go button.
UIBarButtonItem *mGoBtn = [[UIBarButtonItem alloc] initWithTitle:#"Go"
style:UIBarButtonItemStyleBordered
target:self
action:#selector(showCheckpoints)];
[buttons addObject:mGoBtn];
// Add buttons to toolbar and toolbar to nav bar.
[tools setItems:buttons animated:NO];
UIBarButtonItem *twoButtons = [[UIBarButtonItem alloc] initWithCustomView:tools];
self.navigationItem.rightBarButtonItem = twoButtons;
//Add annotations of different colors based on initial and final places.
SBRouteAnnotation *startAnnotation = [[SBRouteAnnotation alloc] initWithCoordinate:[[routePoints objectAtIndex:0] coordinate]
title:mStartPoint
annotationType:SBRouteAnnotationTypeStart];
SBRouteAnnotation *endAnnotation = [[SBRouteAnnotation alloc] initWithCoordinate:[[routePoints lastObject] coordinate]
title:mEndPoint
annotationType:SBRouteAnnotationTypeEnd];
[mMap.mapView addAnnotations:[NSArray arrayWithObjects:startAnnotation, endAnnotation,nil]];
}
- (void)directions:(UICGDirections *)directions didFailWithMessage:(NSString *)message {
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Map Directions" message:message delegate:nil cancelButtonTitle:nil otherButtonTitles:#"OK", nil];
[alertView show];
}
#pragma mark -
#end
I tried to put my code on a new project (empty application) and it worked fine, i didn't understand that error and its possible causes.
EDIT:
I tried to track the problem by elimination,and my code worked fine when i remove this method:
//Invoked when the class is instantiated in XIB
-(id)initWithCoder:(NSCoder*)aDecoder
{
self = [super initWithCoder:aDecoder];
if( self)
{
[self customInitialization];
}
return self;
}
However, when i run, my app doesn't display me the route between the two points (The purpose of the app). So this method seems important for the whole class because without it, i couldn't get the route and in the other side when it's there, the app crashes. How should i deal with this contradiction?
#Vince is putting you on the right track. Since the problem is in an NSURL method being called by a framework (Google Maps in this case) you need to debug what you are passing to the framework. You have the source for the framework also right? You can set breakpoints in loadWithStartPoint:endPoint:options:] to see what's going on.
One thing I did notice is you are passing an NSMutableArray as the endPoint param when I believe it expects an NSString.