Objective C why are the objects from my NSMutableArray disappearing? - ios

I'm new to Objective-C and iOS. I'm having trouble with something that is probably easily corrected, but I have no idea why it's happening.
I'll try to give a brief description of what I'm doing and hopefully it'll have enough info to spot the error.
PolygonView.h is a UIView type class, and it declares the NSmutablearray like this:
#property(nonatomic, retain) NSMutableArray *polys;
I init it in PolygonView.m's initWithFrame:(CGRect)Frame method like this:
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
polys = [[NSMutableArray alloc]init];
[self loadCsv];
return self;
}
inside loadCSV I load a csv use it to create a few poly objects and do some other stuff, but here's the gist of it:
poly* p;
p = [[poly alloc] init];
// here I set a few properties on p from the csv file and then add p to polys like this:
[polys addObject:p];
At the end of the method I use nslog to print the count like this:
NSLog(#"polys size = %i", [polys count]);
This prints "polys size = 100" on the console.
And then I decided to do my own drawRect. and the beginning of it use the same nslog to print the size/count, but "polys size = 0" is printed on the console.
What am I doing wrong? how can I access the objects I added to this array from the drawRect method?
edit:
here's the full polygonView.h file:
#import <UIKit/UIKit.h>
#interface PolygonView : UIView{
NSMutableArray *polys;
}
#property(nonatomic, retain) NSMutableArray *polys;
-(void)loadCSV;
#end
edit 2: the full polygon
//
// PolygonView.m
// Polygon2
//
#import "PolygonView.h"
#import "poly.h"
#implementation PolygonView
#synthesize polys;
-(id)init
{
NSLog(#"regular init");
self = [super init];
if (self) {
self.polys = [[NSMutableArray alloc]init];
[self loadCsv];
}
return self;
}
- (id)initWithFrame:(CGRect)frame
{
NSLog(#"init with frame");
self = [super initWithFrame:frame];
if (self) {
self.polys = [[NSMutableArray alloc]init];
[self loadCsv];
}
return self;
}
-(void)loadCsv
{
poly* p;
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"DivisoesEstaduais" ofType:#"csv"];
NSString *testString = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];
NSString *newString = [testString stringByReplacingOccurrencesOfString:#"," withString:#"."];
if (newString) {
NSMutableArray *arrr = [newString componentsSeparatedByString:#"\n"];
for (int i = 0; i < [arrr count]; i++){
if ([[arrr objectAtIndex:i] length] > 0){
p = [[poly alloc] init];
NSMutableArray *arrayatual = [[arrr objectAtIndex:i] componentsSeparatedByString:#";"];
[p setPolyid:[arrayatual objectAtIndex:0]];
[p setPartid:[arrayatual objectAtIndex:1]];
[p setPointid:[arrayatual objectAtIndex:2]];
[p setx:[arrayatual objectAtIndex:3]];
[p sety:[arrayatual objectAtIndex:4]];
[self.polys addObject:p];
NSLog(#"polys size = %i", [self.polys count]); // this increases from 1 to 100
}
}
NSLog(#"polys size = %i", [self.polys count]); // this prints 100
}else{
NSLog(#"not");
}
}
- (void)drawRect:(CGRect)rect{
srandom( time( NULL ) );
CGContextRef context= UIGraphicsGetCurrentContext();
CGContextSetRGBStrokeColor(context, random() % 250, random() % 250, random() % 250, 1.0);
CGContextSetFillColorWithColor(context, [UIColor redColor].CGColor);
CGContextFillPath(context);
int oldtemp = 0;
NSLog(#"drawRect polys pointer = %#", self.polys); // this is null
NSLog(#"polys size = %i", [self.polys count]); // this is zero
for (int i = 0; i < [polys count]; i++){
poly *p = [polys objectAtIndex:i];
int temp = p.getPolyid.intValue;
if (temp == oldtemp){
CGFloat hue = ( arc4random() % 256 / 256.0 ); // 0.0 to 1.0
CGFloat saturation = ( arc4random() % 128 / 256.0 ) + 0.5; // 0.5 to 1.0, away from white
CGFloat brightness = ( arc4random() % 128 / 256.0 ) + 0.5; // 0.5 to 1.0, away from black
UIColor *color = [UIColor colorWithHue:hue saturation:saturation brightness:brightness alpha:1];
CGContextSetFillColorWithColor(context, color.CGColor);
CGContextFillPath(context);
NSLog(#"movetopoint");
CGContextMoveToPoint(context, [p.getx floatValue]+160, [p.gety floatValue]+160);
oldtemp++;
}else{
CGContextAddLineToPoint(context, [p.getx floatValue]+160, [p.gety floatValue]+160);
NSLog(#"addlinetopoint");
}
}
CGFloat hue = ( arc4random() % 256 / 256.0 ); // 0.0 to 1.0
CGFloat saturation = ( arc4random() % 128 / 256.0 ) + 0.5; // 0.5 to 1.0, away from white
CGFloat brightness = ( arc4random() % 128 / 256.0 ) + 0.5; // 0.5 to 1.0, away from black
UIColor *color = [UIColor colorWithHue:hue saturation:saturation brightness:brightness alpha:1];
CGContextSetFillColorWithColor(context, color.CGColor);
CGContextFillPath(context);
}
#end
and here's viewController.m:
//
// ViewController.m
// Polygon2
//
//
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.view.multipleTouchEnabled = YES;
PolygonView * pv = [[PolygonView alloc] init];
[self.view addSubview:pv];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
Edit3:
George Sachin's solution worked: use viewDidAppear instead of viewDidLoad. But why was viewDidLoad causing me trouble? Here's the zipped project in case anyone wants to take a look and try to figure out.
http://www.mediafire.com/download/b9ueqkqbmxswd7a/Polygon2_3.zip
Seems to me ViewDidload is the one I should be using. Should I never use it?

Final Edit:
When you create an initializer for a UIView you should always call super's initWithFrame: or initWithCoder: when you use a .xib
Change your code to this:
-(id)init
{
NSLog(#"regular init");
self = [super initWithFrame:CGRectMake(0, 0, 20, 20)]; // Replace with the dimensions you want
if (self) {
self.polys = [[NSMutableArray alloc]init];
[self loadCsv];
}
NSLog(#"(init)polys size = %i", [self.polys count]);
return self;
}

Try to do this:
1) When you're setting #property(nonatomic, retain) myObj to the object you should do in the .m file #synthesize myObj
2) as HSNN says you should access it via self.myObj
3)It's better to do init part in between this part
if (self) {
// Initialization code
}
phrase "initialization code" was put there not accidentally))
Most likely that root of the problem is forgotten #synthesize but it's better to cover all 3 steps.
good luck. feel free to ask if this won't help)

Related

How to fix Coreplot Pie Chart Problems in 64 bit version vs 32 bit version

The h file for the relevant view controller
#import <UIKit/UIKit.h>
#import "StudentModel.h"
#import "CorePlot-CocoaTouch.h"
#interface AttendenceViewController : UIViewController <UITabBarDelegate,UITableViewDataSource,CPTLegendDelegate,CPTPieChartDataSource,CPTPieChartDelegate>
#property (strong,nonatomic) StudentModel *studentA;
#property (strong,nonatomic) NSDictionary *studentAttendanceDetails;
#property (weak, nonatomic) IBOutlet UIView *graphContainer;
The m file for the relevant view controller
#import "AttendenceViewController.h"
#interface AttendenceViewController ()
#property (nonatomic,strong) CPTGraphHostingView *hostView;
#property (nonatomic,strong) CPTTheme *selectedTheme;
-(void)initPlot;
-(void)configureHost;
-(void)configureGraph;
-(void)configureChart;
-(void)configureLegend;
#end
#implementation AttendenceViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
// float attendanceTotal = [self.studentA.percentageAttendance floatValue];
// float authorisedAbsences = [self.studentA.authorisedAbsences floatValue];
// float unathorisedAbsences = [self.studentA.unautherisedAbsences floatValue];
self.studentAttendanceDetails = [NSDictionary dictionaryWithObjectsAndKeys:self.studentA.percentageAttendance,#"totalAttendance",self.studentA.authorisedAbsences,#"authorisedAbsences",self.studentA.unautherisedAbsences,#"unauthorisedAbsences", nil];
NSLog(#"%#",self.studentAttendanceDetails);
}
-(void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[self initPlot];
}
#pragma mark Core Plot Initialisation Methods
-(void)initPlot
{
[self configureHost];
[self configureGraph];
[self configureChart];
[self configureLegend];
}
-(void)configureHost;
{
CGRect parentRect = self.graphContainer.bounds;
self.hostView = [(CPTGraphHostingView*)[CPTGraphHostingView alloc]initWithFrame:parentRect];
self.hostView.allowPinchScaling=NO;
[self.graphContainer addSubview:self.hostView];
}
-(void)configureGraph;
{
//create and initialise the graph
CPTGraph *graph = [[CPTXYGraph alloc]initWithFrame:self.hostView.bounds];
self.hostView.hostedGraph=graph;
graph.paddingLeft=0.0f;
graph.paddingTop=0.0f;
graph.paddingRight=0.0f;
graph.paddingBottom=0.0f;
graph.axisSet=nil;
//set up the text styles
CPTMutableTextStyle *textStyle = [CPTMutableTextStyle textStyle];
textStyle.color= [CPTColor grayColor];
textStyle.fontName =#"Helvetica-Bold";
textStyle.fontSize=16.0f;
//configure the title
NSString *title = #"Student Attendance";
graph.title=title;
graph.titleTextStyle=textStyle;
graph.titlePlotAreaFrameAnchor= CPTRectAnchorTop;
graph.titleDisplacement= CGPointMake(0.0f, -12.0f);
self.selectedTheme = [CPTTheme themeNamed:kCPTPlainWhiteTheme];
[graph applyTheme:self.selectedTheme];
}
-(void)configureChart;
{
//get reference to graph
CPTGraph *graph = self.hostView.hostedGraph;
//create chart
CPTPieChart *pieChart = [[CPTPieChart alloc]init];
pieChart.delegate=self;
pieChart.dataSource=self;
pieChart.pieRadius=(self.hostView.bounds.size.height*0.9)/3;
pieChart.identifier= graph.title;
pieChart.startAngle = M_PI_4;
pieChart.sliceDirection=CPTPieDirectionClockwise;
//gradient
CPTGradient *overlayGradient = [[CPTGradient alloc]init];
overlayGradient.gradientType=CPTGradientTypeRadial;
overlayGradient=[overlayGradient addColorStop:[[CPTColor blackColor]colorWithAlphaComponent:0.0] atPosition:0.9];
overlayGradient=[overlayGradient addColorStop:[[CPTColor blackColor]colorWithAlphaComponent:0.4] atPosition:1.0];
pieChart.overlayFill = [CPTFill fillWithGradient:overlayGradient];
[graph addPlot:pieChart];
}
-(void)configureLegend;
{
}
#pragma mark Core Plot Datasource Methods
-(NSUInteger)numberOfRecordsForPlot:(CPTPlot *)plot
{
return [self.studentAttendanceDetails count];
}
-(NSNumber*)numberForPlot:(CPTPlot *)plot field:(NSUInteger)fieldEnum recordIndex:(NSUInteger)idx
{
if (CPTPieChartFieldSliceWidth == fieldEnum)
{
NSArray *values = [self.studentAttendanceDetails allValues];
return [values objectAtIndex:idx];
}
return [NSDecimalNumber zero];
}
-(CPTLayer*)dataLabelForPlot:(CPTPlot *)plot recordIndex:(NSUInteger)idx
{
static CPTMutableTextStyle *labelText = nil;
if (!labelText) {
labelText= [[CPTMutableTextStyle alloc] init];
labelText.color = [CPTColor grayColor];
}
NSString *labelValue = nil;
switch (idx) {
case 0:{
NSString *unauthorised= [self.studentAttendanceDetails objectForKey:#"unauthorisedAbsences"];
NSLog(#"%#",unauthorised);
if ([unauthorised isEqualToString:#"0.00"]) {
labelText=nil;
}
labelValue = [NSString stringWithFormat:#"%#",[self.studentAttendanceDetails objectForKey:#"unauthorisedAbsences"]];
}
break;
case 1:
labelValue = [NSString stringWithFormat:#"%#",[self.studentAttendanceDetails objectForKey:#"totalAttendance"]];
break;
case 2:
labelValue = [NSString stringWithFormat:#"%#",[self.studentAttendanceDetails objectForKey:#"authorisedAbsences"]];
break;
}
return [[CPTTextLayer alloc] initWithText:labelValue style:labelText];
}
-(NSString*)legendTitleForPieChart:(CPTPieChart *)pieChart recordIndex:(NSUInteger)idx
{
return #"Student Attendance";
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
Image of result when simulator is set to iPhone 5 and run (i.e. 32 bit)
Image of result when simulator is set to iPhone 5s or later (i.e 64bit)
The problem is how to I adjust the code in the implementation file for the view controller to ensure the output is the same for 32 bit and 64 bit runs?
Dictionaries don't have a defined ordering. The allValues array is probably different on each platform. For this application, you don't need to stash the values in a dictionary at all. Just return authorisedAbsences or unautherisedAbsences from the datasource based on the index.

How to solve the error (?memory related) on navigating between view controllers

I have two view controllers (CreateReport & ViewReport). and an NSObject class (PDFRenderer).
The infoArray(NSMutableArray)is declared as extern in PDFRenderer(NSObject class) & accessed by import # PDFRenderer.h in CreateReport class. The user selects tableview rows desired and passess the selected items to infoArray(NSMutableArray) and push ViewReport class on navigation stack to view the pdf generated using the items in the array.
CreateReport--(PDFRenderer)-->ViewReport
PROBLEM: After viewing the pdf first time without problem - now I want to navigate back to CreateReport from ViewReport - select or deselect desired table cells - update the infoArray - and push ViewReport class to see the pdf regenerated with items in updated infoArray - I am unable to pass any new changes to the infoArray(NSMutableArray). I am guessing the array is getting released or there is some memory problem. Can some one help me with this?
CreateReport.m
#import "ViewReport.h"
#import "PDFRenderer.h"
-(void)preparePDF{
//self.draftArrray has data for CreateReport tableView cells and gets updated on tableView Cells selection
NSDictionary*id_dict=[self.draftArray objectAtIndex:0];
NSArray* id_arr= [id_dict objectForKey:#"Rows"];
NSMutableArray*id_array=[[NSMutableArray alloc]init];
id_arr= [id_dict objectForKey:#"Rows"];
NSDictionary*dict=[[NSDictionary alloc]init];
for (dict in id_arr) {
NSString*id_string=[dict objectForKey:#"title"];
[id_array addObject:id_string];
}
infoArray=id_array; //pass id_array to infoArray declared in PDFRenderer
}
-(void)IBAction{
[self performSegueWithIdentifier:#"finalViewReport" sender:self];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"finalViewReport"]) {
//ViewReport*destViewController = segue.destinationViewController;
[self preparePDF];
}
}
-*-
PDFRenderer.h
#import <Foundation/Foundation.h>
extern NSMutableArray*infoArray;
#interface PDFRenderer : NSObject
#property (nonatomic,strong,retain) NSMutableArray*infoArray;
+ (PDFRenderer *)sharedInstance;
-*-
PDFRenderer.m
#import "PDFRenderer.h"
NSMutableArray*infoArray;
#implementation PDFRenderer
#synthesize infoArray;
+(PDFRenderer*) sharedInstance{
static PDFRenderer* _shared = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_shared = [[self alloc] init];
_shared.infoArray = [[NSMutableArray alloc] init];
});
return _shared;
}
+ (void)drawPageNumber:(NSInteger)pageNum
{
NSString *pageString = [NSString stringWithFormat:#"Page %d", pageNum];
UIFont *theFont = [UIFont systemFontOfSize:12];
CGSize maxSize = CGSizeMake(612, 72);
CGSize pageStringSize = [pageString sizeWithFont:theFont
constrainedToSize:maxSize
lineBreakMode:NSLineBreakByClipping];
CGRect stringRect = CGRectMake(((612.0 - pageStringSize.width) / 2.0),
720.0 + ((72.0 - pageStringSize.height) / 2.0),
pageStringSize.width,
pageStringSize.height);
[pageString drawInRect:stringRect withFont:theFont];
}
+(void)drawPDF:(NSString*)fileName
{
UIGraphicsBeginPDFContextToFile(fileName, CGRectZero, nil);
NSInteger currentPage = 0;
BOOL done = NO;
do {
for ( int i = 0 ; i < 4 ; i++ )
{
// Mark the beginning of a new page.
UIGraphicsBeginPDFPageWithInfo(CGRectMake(0, 0, 612, 792), nil);
// Draw a page number at the bottom of each page.
currentPage++;
[self drawPageNumber:currentPage];
//[self drawText:#"Hello World" inFrame:CGRectMake(0, 0, 300, 50)];
int xOrigin= 50;
int yOrigin= 50;
int rowHeight = 50;
int columnWidth = 512;
int numberOfRows = 7;
int numberOfColumns = 1;
if (!i>0) {
[self drawLabels];
xOrigin= 50;
yOrigin= 300;
CGPoint from = CGPointMake(10, 60);
CGPoint to = CGPointMake(602, 60);
[PDFRenderer drawLineFromPoint:from toPoint:to];
CGPoint from1 = CGPointMake(10, 140);
CGPoint to1 = CGPointMake(602, 140);
[PDFRenderer drawLineFromPoint:from1 toPoint:to1];
}
UIGraphicsEndPDFContext();
}
+(void)drawLabels
{
int i;
NSString*s=[[NSString alloc]init];
int x=20;
int y=47;
for (i=0;i<infoArray.count;i++) {
if (i==0|i==3|i==6|i==9|i==12|i==15) {
x=20;
y+=15;
}
s=[infoArray objectAtIndex:i];
[self drawText:s inFrame:CGRectMake(x, y, 100, 25)];
x+=185;
y+=0;
}
}
-*-
ViewReport.m
-(void)viewDidLoad
{
NSString* fileName = [self getPDFFileName];
[PDFRenderer drawPDF:fileName];
NSURL *url = [NSURL fileURLWithPath:fileName];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[detailview setScalesPageToFit:YES]; //UIWebView*detailview
[detailview loadRequest:request];
//[self.view addSubview:detailview];
}
-(NSString*)getPDFFileName
{
NSString* fileName = #"newReport.PDF";
NSArray *arrayPaths =
NSSearchPathForDirectoriesInDomains(
NSDocumentDirectory,
NSUserDomainMask,
YES);
NSString *path = [arrayPaths objectAtIndex:0];
NSString* pdfFileName = [path stringByAppendingPathComponent:fileName];
return pdfFileName;
}
Remove this line NSMutableArray*infoArray; Since you are using #synthesise you don't need this one.
Also, I don't see where you are using this array after all?

coreplot pie chart example

I am trying to follow the tutorial at http://www.raywenderlich.com/13269/how-to-draw-graphs-with-core-plot-part-1
I am working on ios 7 and latest core chart library (1.3)
I am trying to draw a pie chart. My example compiles fine, I get the title header but I do not get any pie chart drawn. What I see is shown below:
My main source code that contains the logic is given below. Kindly help
//
// CPDFirstViewController.m
// CorePlotDemo
//
// Created by R Menon on 9/19/13.
// Copyright (c) 2013 4stepfinance Inc. All rights reserved.
//
#import "CPDPieChartViewController.h"
#interface CPDPieChartViewController ()
#property (weak, nonatomic) IBOutlet UIToolbar *toolbar;
#property (weak, nonatomic) IBOutlet UIBarButtonItem *themeButton;
#property (strong, nonatomic) CPTGraphHostingView *hostView;
#property (strong, nonatomic) CPTTheme *selectedTheme;
- (void)initPlot;
-(void)configureHost;
-(void)configureGraph;
-(void)configureChart;
-(void)configureLegend;
#end
#implementation CPDPieChartViewController
#pragma mark - Chart behavior
- (void)initPlot {
[self configureHost];
[self configureChart];
[self configureGraph];
[self configureLegend];
}
-(void)configureHost {
// Set up view frame
CGRect parentRect = self.view.bounds;
CGSize toolbarSize = self.toolbar.bounds.size;
parentRect = CGRectMake(parentRect.origin.x, (parentRect.origin.y + toolbarSize.height), parentRect.size.width, (parentRect.size.height - toolbarSize.height));
// Create host view
self.hostView = [(CPTGraphHostingView *) [CPTGraphHostingView alloc] initWithFrame:parentRect];
self.hostView.allowPinchScaling = NO;
[self.view addSubview:self.hostView];
}
-(void)configureGraph {
//Create and initialize graph
CPTGraph *graph = [[CPTXYGraph alloc] initWithFrame:self.hostView.bounds];
self.hostView.hostedGraph = graph;
graph.paddingLeft = 0.0f;
graph.paddingTop = 0.0f;
graph.paddingRight = 0.0f;
graph.paddingBottom = 0.0f;
graph.axisSet = nil;
// Set up text stile
CPTMutableTextStyle *textStyle = [CPTMutableTextStyle textStyle];
textStyle.color = [CPTColor grayColor];
textStyle.fontName = #"Helvetica-Bold";
textStyle.fontSize = 16.0f;
// Configure title
NSString *title = #"Portfolio Prices: May 1, 2012";
graph.title = title;
graph.titleTextStyle = textStyle;
graph.titlePlotAreaFrameAnchor = CPTRectAnchorTop;
graph.titleDisplacement = CGPointMake(0.0f, -12.0f);
// Set theme
self.selectedTheme = [CPTTheme themeNamed:kCPTPlainWhiteTheme];
[graph applyTheme:self.selectedTheme];
}
-(void)configureChart {
// 1 - Get reference to graph
CPTGraph *graph = self.hostView.hostedGraph;
// 2 - Create chart
CPTPieChart *pieChart = [[CPTPieChart alloc] init];
pieChart.dataSource = self;
pieChart.delegate = self;
pieChart.pieRadius = (self.hostView.bounds.size.height * 0.7) / 2;
pieChart.identifier = graph.title;
pieChart.startAngle = M_PI_4;
pieChart.sliceDirection = CPTPieDirectionClockwise;
// 3 - Create gradient
CPTGradient *overlayGradient = [[CPTGradient alloc] init];
overlayGradient.gradientType = CPTGradientTypeRadial;
overlayGradient = [overlayGradient addColorStop:[[CPTColor blackColor] colorWithAlphaComponent:0.0] atPosition:0.9];
overlayGradient = [overlayGradient addColorStop:[[CPTColor blackColor] colorWithAlphaComponent:0.4] atPosition:1.0];
pieChart.overlayFill = [CPTFill fillWithGradient:overlayGradient];
// 4 - Add chart to graph
[graph addPlot:pieChart];
}
-(void)configureLegend {
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:YES];
[self initPlot];
}
- (IBAction)themeTapped:(id)sender {
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - CPTPlotDataSource methods
- (NSUInteger)numberOfRecordsForPlot:(CPTPlot *)plot
{
return [[[CPDStockPriceStore sharedInstance] tickerSymbols] count];
}
- (NSNumber *)numberForPlot:(CPTPlot *)plot field:(NSUInteger)fieldEnum recordIndex:(NSUInteger)idx
{
if (CPTPieChartFieldSliceWidth == fieldEnum)
{
return [[[CPDStockPriceStore sharedInstance] dailyPortfolioPrices] objectAtIndex:idx];
}
return [NSDecimalNumber zero];
}
- (CPTLayer *)dataLabelForPlot:(CPTPlot *)plot recordIndex:(NSUInteger)idx {
static CPTMutableTextStyle *labelText = nil;
if (!labelText) {
labelText= [[CPTMutableTextStyle alloc] init];
labelText.color = [CPTColor grayColor];
}
// 2 - Calculate portfolio total value
NSDecimalNumber *portfolioSum = [NSDecimalNumber zero];
for (NSDecimalNumber *price in [[CPDStockPriceStore sharedInstance] dailyPortfolioPrices]) {
portfolioSum = [portfolioSum decimalNumberByAdding:price];
}
// 3 - Calculate percentage value
NSDecimalNumber *price = [[[CPDStockPriceStore sharedInstance] dailyPortfolioPrices] objectAtIndex:idx];
NSDecimalNumber *percent = [price decimalNumberByDividingBy:portfolioSum];
// 4 - Set up display label
NSString *labelValue = [NSString stringWithFormat:#"$%0.2f USD (%0.1f %%)", [price floatValue], ([percent floatValue] * 100.0f)];
// 5 - Create and return layer with label text
return [[CPTTextLayer alloc] initWithText:labelValue style:labelText];
}
-(NSString *)legendTitleForPieChart:(CPTPieChart *)pieChart recordIndex:(NSUInteger)index {
return #"";
}
#pragma mark - UIActionSheetDelegate methods
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
}
#end
Never mind. I had a typo:
Following method definition
- (void)initPlot {
[self configureHost];
[self configureChart];
[self configureGraph];
[self configureLegend];
}
should change to
- (void)initPlot {
[self configureHost];
[self configureGraph];
[self configureChart];
[self configureLegend];
}
In other words, I should invoke configureGraph before configureChart method.

CATiledLayer and not rendered slices

The problem:
I've to render a big image 7148x15000px (a custom map), so i've looking around for something usefull and i've found BitmapSlice, but the problem is that the very first time i run the app (on device and simulator) several slices aren't loaded correctly and i see the image with large black holes.
Code:
BitmapSliceViewController.h
#import <UIKit/UIKit.h>
#interface BitmapSliceViewController : UIViewController<UIScrollViewDelegate>
#property (nonatomic, retain) UIImageView *_zoomView;
#property (nonatomic, retain) IBOutlet UIScrollView *scrollView;
- (void)saveTilesOfSize:(CGSize)size forImage:(UIImage*)image toDirectory
(NSString*)directoryPath usingPrefix:(NSString*)prefix;
#end
BitmapSliceViewController.m
#import "BitmapSliceViewController.h"
#import "TileView.h"
#implementation BitmapSliceViewController
#synthesize scrollView;
- (void)dealloc
{
[super dealloc];
}
- (void)viewDidUnload
{
[super viewDidUnload];
}
- (void)viewDidLoad
{
[super viewDidLoad];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *directoryPath = [paths objectAtIndex:0];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
UIImage *big = [UIImage imageNamed:#"map.jpg"];
[self saveTilesOfSize:(CGSize){256, 256} forImage:big toDirectory:directoryPath usingPrefix:#"map_"];
dispatch_async(dispatch_get_main_queue(), ^{
[scrollView setNeedsDisplay];
});
});
TileView *tv = [[TileView alloc] initWithFrame:(CGRect){{0,0}, (CGSize){7148,15000}}];
[tv setTileTag:#"map_"];
[tv setTileDirectory:directoryPath];
[scrollView addSubview:tv];
[scrollView setContentSize:(CGSize){7148,15000}];
[scrollView setDelegate:self];
}
- (void)saveTilesOfSize:(CGSize)size
forImage:(UIImage*)image
toDirectory:(NSString*)directoryPath
usingPrefix:(NSString*)prefix
{
CGFloat cols = [image size].width / size.width;
CGFloat rows = [image size].height / size.height;
int fullColumns = floorf(cols);
int fullRows = floorf(rows);
CGFloat remainderWidth = [image size].width - (fullColumns * size.width);
CGFloat remainderHeight = [image size].height - (fullRows * size.height);
if (cols > fullColumns) fullColumns++;
if (rows > fullRows) fullRows++;
CGImageRef fullImage = [image CGImage];
for (int y = 0; y < fullRows; ++y) {
for (int x = 0; x < fullColumns; ++x) {
CGSize tileSize = size;
if (x + 1 == fullColumns && remainderWidth > 0) {
// Last column
tileSize.width = remainderWidth;
}
if (y + 1 == fullRows && remainderHeight > 0) {
// Last row
tileSize.height = remainderHeight;
}
CGImageRef tileImage = CGImageCreateWithImageInRect(fullImage,
(CGRect){{x*size.width, y*size.height},
tileSize});
NSData *imageData = UIImageJPEGRepresentation([UIImage imageWithCGImage:tileImage], 1);
CGImageRelease(tileImage);
NSString *path = [NSString stringWithFormat:#"%#/%#%d_%d.png",
directoryPath, prefix, x, y];
[imageData writeToFile:path atomically:NO];
}
}
}
#end
TileView.h
#interface TileView : UIView
#property (nonatomic, copy) NSString *tileTag;
#property (nonatomic, copy) NSString *tileDirectory;
- (UIImage*)tileAtCol:(int)col row:(int)row;
#end
TileView.m
#import "TileView.h"
#implementation TileView
#synthesize tileTag;
#synthesize tileDirectory;
+ layerClass
{
return [CATiledLayer class];
}
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (!self) return nil;
return self;
}
- (void)dealloc
{
[super dealloc];
}
- (void)drawRect:(CGRect)rect {
//CGContextRef context = UIGraphicsGetCurrentContext();
CGSize tileSize = (CGSize){256, 256};
int firstCol = floorf(CGRectGetMinX(rect) / tileSize.width);
int lastCol = floorf((CGRectGetMaxX(rect)-1) / tileSize.width);
int firstRow = floorf(CGRectGetMinY(rect) / tileSize.height);
int lastRow = floorf((CGRectGetMaxY(rect)-1) / tileSize.height);
for (int row = firstRow; row <= lastRow; row++) {
for (int col = firstCol; col <= lastCol; col++) {
UIImage *tile = [self tileAtCol:col row:row];
if (tile)
{
CGRect tileRect = CGRectMake(tileSize.width * col, tileSize.height * row, tileSize.width, tileSize.height);
tileRect = CGRectIntersection(self.bounds, tileRect);
[tile drawInRect:tileRect];
// [[UIColor whiteColor] set];
// CGContextSetLineWidth(context, 6.0);
// CGContextStrokeRect(context, tileRect);
}
}
}
}
- (UIImage*)tileAtCol:(int)col row:(int)row
{
NSString *path = [NSString stringWithFormat:#"%#/%#%d_%d.png", tileDirectory, tileTag, col, row];
return [UIImage imageWithContentsOfFile:path];
}
#end
This is the main code of the app, you can download the entire example from the site linked on the top of the post.
As i said the main problem is the rendering of some slices that fail in the first run of the app, other runs seem works correctly.
modifing a bit - (UIImage*)tileAtCol:(int)col row:(int)row
- (UIImage*)tileAtCol:(int)col row:(int)row
{
NSString *path = [NSString stringWithFormat:#"%#/%#%d_%d.png", tileDirectory, tileTag, col, row];
UIImage *img = [UIImage imageWithContentsOfFile:path];
if (img) {
NSLog(#"good");
}
else {
NSLog(#"bad");
}
return img;
}
The problem seems to be here...
Any ideas to fix it?
Thanks in advance

Core Plot : add a legend to pieChart with "legendTitleForPieChart" doesn't work

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];
}

Resources