I am trying to implement the XYPieCharts but am currently unable to get it to display. While debugging I placed some log statements in the data source methods but none of these are being fired.
I have included the delegate (not being used) and datasource as such
#interface DwinklySalaryViewController : UIViewController <UITextFieldDelegate, XYPieChartDelegate, XYPieChartDataSource>
#property (nonatomic, strong) IBOutlet XYPieChart *salaryPieChart;
I have also checked that I have linked up this IBOutlet to the XYPieChart element in the Interface Builder. There is no delegate/datasource outlet in the Interface Builder so I have added that programatically as in the demo. This, and the rest of the options are set in the viewDidLoad method of the a view controller that contains the XYPieChart view.
[_salaryPieChart setDelegate:self];
[_salaryPieChart setDataSource:self];
I have then implemented the required methods for the datasource protocol:
- (NSUInteger)numberOfSlicesInPieChart:(XYPieChart *)pieChart
{
NSLog(#"Number of slices: %d", pieChartSlices.count);
return pieChartSlices.count;
}
- (CGFloat)pieChart:(XYPieChart *)pieChart valueForSliceAtIndex:(NSUInteger)index
{
NSLog(#"Value slice: %d", [[pieChartSlices objectAtIndex:index] intValue]);
return [[pieChartSlices objectAtIndex:index] intValue];
}
- (UIColor *)pieChart:(XYPieChart *)pieChart colorForSliceAtIndex:(NSUInteger)index
{
NSLog(#"Returning %#", [sliceColors objectAtIndex:(index % sliceColors.count)]);
return [sliceColors objectAtIndex:(index % sliceColors.count)];
}
Could anyone tell me what I've missed out that causes the data source methods not to trigger?
It appears that with the way XYPieChart works, you have you call reloadData on it at startup. It works ok in either viewDidLoad or viewDidAppear.
Related
I'm making an app that pulls images from both Flickr, and Imgur using their API.
After populating my model that stores the image URL and the title of the image, I want to reload the UI so that, the images populate the collectionview, but when its called, collectionview is nil.
This delegate method is called in the class responsible for fetching images using the APIs.
-(void)refreshUIOnMainThread
{
photosFromWeb = [libraryAPI getPhotos];
if([self.delegate respondsToSelector:#selector(reloadUIAfterImageDownload:)]) {
[self.delegate reloadUIAfterImageDownload:photosFromWeb];
}
}
The delegate method is defined in ViewController.m, the class where the UICollectionView delegate functions should be called upon a call to reloadData.
-(void)reloadUIAfterImageDownload:(NSArray*)photosFromWeb
{
allPhotos = photosFromWeb;
NSLog(#"reloadUIAfterDelegate: Number of Photos in Photo Model: %lu\n",
(unsigned long)[allPhotos count]);
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(#"about to reload collectionview...\n");
//collectionview is nil, so reloadData is not called???????
[self.collectionView reloadData];
});
}
Originally I thought the photo Model array was 0, meaning 0 cells would populate.
-(NSInteger)collectionView:(UICollectionView *)collectionView
numberOfItemsInSection:(NSInteger)section
{
NSLog(#"numberofItemsInSection: Number of Photos in Photo Model: %lu\n",
(unsigned long)[allPhotos count]);
return ([allPhotos count]);
}
but that's not the case.
NSLog(#"reloadUIAfterDelegate: Number of Photos in Photo Model: %lu\n",
(unsigned long)[allPhotos count]);
returns a count of 128 indicating that Photo objects are there, and in the debugger I find the collectionview has a nil value within the delegate method definition. Why could this be?
Repo: https://github.com/danielv775/Flickr-Image-Gallery/tree/master/Photo%20Gallery
These functions are in FlickrClient.m and ViewController.m
Ah I see the issue now. It lies in this line of LibraryAPI.m:
flickrClient = [[FlickrClient alloc]init];
vc = [[ViewController alloc]init];
flickrClient.delegate = vc;
You're creating a new instance of your view controller, so none of the IBOutlets are set up on this new instance. Instead, you need to set your delegate from ViewController.m like so:
- (void)viewDidLoad {
[super viewDidLoad];
LibraryAPI *libraryAPI = [LibraryAPI sharedInstance];
libraryAPI.flickrClient.delegate = self;
}
This assumes you have a flickrClient property on your LibraryAPI. You could also add a delegate property on LibraryAPI if you wanted.
NOTE: You also want to change your delegate property on flickrClient like so:
#property (weak, nonatomic) id <FlickrClientDelegate> delegate;
Delegates should not maintain strong references.
I created a singleton in ios7 like this:
SharedData.h
#interface SharedData : NSObject
{
}
+ (id)sharedInstance;
#property (strong, nonatomic) NSMutableArray *list;
#end
SharedData.m
#import "SharedData.h"
#implementation SharedData
#synthesize list;
// Get the shared instance thread safe
+ (SharedData *)sharedInstance {
static dispatch_once_t once = 0;
static SharedData *sharedInstance = nil;
dispatch_once(&once, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
- (id)init {
self = [super init];
if (self) {
//initialize
list = [[NSMutableArray alloc] init];
}
return self;
}
#end
I always use this code to access this class:
SharedData *sharedData = [SharedData sharedInstance];
The problem is now when I switch the view in my viewDidLoad method the list is empty but in my viewDidAppear method everything is fine. Any ideas?
EDIT:
This is the code how I change the views:
SharedData *sharedData = [SharedData sharedInstance];
//clear feed and add new feed
[sharedData.list removeAllObjects];
[sharedData.list addObjectsFromArray:newList];
//show new gui
[self.navigationController performSegueWithIdentifier:#"goToMain" sender:self];
NOTE: I push from a normal ViewController to a TabBarController -> NavigationController -> TableViewController to display the list.
I guess you have the confusion between these two viewcontroller methods:
-(void)viewDidLoad{
//
}
&
-(void) viewDidAppear{
//
}
viewDidAppear is the method which is called each time your view changes but viewDidLoad is the method which is not necessarily called each time your view changes.
ViewDidLoad method is called when view loads for the first time, after that it doesn't get called until the views are removed/released.
P.S: I suggest you to put the breakpoint in your viewDidLoad and viewDidAppear method and feel it. Your answer lies there.
Hope this helps you alot.
Good Luck.
The problem was i created a segue which went from the button to the next view. Because of this the viewDidLoad gets earlier called than the list assigned. I just changed the segue to go from view to view.
How are you changing from one viewController to the other? Wich classes are the parents of your destination ViewController?,
If you are modifying properties of the view in the prepareForSegue method... you are forcing the view to load.
For example, you are setting the list of your singleton in prepareForSegue, but before setting the list you are modifying a property of your destination viewController. (doing something like destVC.view = XXX or destVC.viewControllers = XX if you are subclassing a UITabBarViewController...) Then you are triggering the viewDidLoad method , and it's executing before you have set the list to the correct value.
Or maybe you are seguing in two different places to the destinationViewController. And when the viewDidLoad happens, you still have not updated the list on the singleton.
Here is the transcription of the chat with the poster of the question: https://chat.stackoverflow.com/transcript/55218
I have a custom NSObject class, doing some FTP work (display, transfer...).
This FTP class has a downloadFiles: method, which is launch inside a NSOperationQueue.
And I would like that method to provide some results as a data source in a UITableView. So I decided to delegate those results to the UITableViewController.
So the protocol for the FTP class :
#protocol FTPDelegate;
..
#interface FTP...
#property (nonatomic, weak) id <FTPDelegate> delegate;
...
#protocol FTPDelegate
-(void)FTPMessageReceived: (NSString *)message;
#end
Then I declared the custom UITableViewController as delegate of the FTP class:
self.myFTP = (FTP *) [self FTPManager];
self.myFTP.delegate = self;
So when the downloadFiles: method from the FTP class wants to display some message in the UITableView, it calls:
[[self delegate] FTPMessageReceived:#"dir created"];
The UITableViewController implements that method :
-(void)FTPMessageReceived: (NSString *)message {
NSUInteger index;
NSArray * indexArray;
[[self logLine] addObject: message]; // logLine is my data source
index = [[self logLine] count]-1;
NSLog(#"%u", index);
indexArray = [NSArray arrayWithObject:[NSIndexPath indexPathForRow:index inSection:0]];
[[self logView] insertRowsAtIndexPaths:indexArray withRowAnimation:UITableViewRowAnimationRight];
}
The insert works, but the new row takes around 5 seconds to be displayed in the UITableView !!
And when I put some log inside the tableView:cellForRowAtIndexPath: method, it's display immediately.
And when I put the FTPMessageReceived code inside a button (for example), the UITableView displays the new row immediately !
Any suggestion?
It sounds like FTPMessageReceived: is being called on a background thread. Try calling it on the main thread with:
dispatch_async(dispatch_get_main_queue(), ^{
[[self delegate] FTPMessageReceived:#"dir created"];
});
for my table view I have the following going on (paraphrased)
.h
#interface OptionsView : UIView <UITableViewDelegate, UITableViewDataSource>
#property (nonatomic, retain) NSArray *dataSource;
.m
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self) {
...
self.dataSource = [NSArray arrayWithObjects:options, sections, sponsor, nil];
}
return self;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if(self.dataSource) {
NSArray *ds = [self.dataSource objectAtIndex:indexPath.section];
NSDictionary *d = [ds objectAtIndex:indexPath.row];
ActionBlock a = [d objectForKey:ACTION]; // <-- Thread 1: EXC_BAD_ACCESS (code=2, address=0x802)
if(a != nil) a();
}
}
You can see that in my didSelectRowAtIndexPath I'm getting an EXC_BAD_ACCESS but I'm not sure why. Because I'm using arc it isn't a zombie problem (already checked).
Using breakpoints I see that the self.dataSource exists after it's initialized, but not later when it's needed. It doesn't show up as <nil> either, it's just white space. Also, this works in debug but not in release so what does that mean?
** EDIT adding a screenshot **
You will notice that neither ds or d show up.. odd?
So there's two places I can see that might cause the problem. First, what is ACTION? Is it some sort of NSString? Just want to make sure that you're using a valid key object. Second, (and more likely the problem), it looks like ActionBlock is some kind of code block you're storing in a collection array. Are you copying that block before you store it in the array/dictionary? You must copy any block you intend on keeping around (storing) longer than the scope it was created in. This is easy to do. For example:
void (^NewBlock)(void) = [^{
....code....
} copy];
Alternately:
void (^NewBlock)(void) = ^{
....code....
};
[dictionary setObject:[NewBlock copy] forKey:ACTION]; //Assuming #define ACTION #"action"
This copies it onto the heap so that it can be stored. If you don't copy it, you'll get BAD_EXC_ACCESS anytime you try to reference the block outside the scope it was created in.
It seems you´re using a UIViewController instead of a UITableViewController. If I remember correctly, you have to go to the inspector and drag the delegate and datasource from the tableView to the UIViewController. I don´t remember exactly and I´m not on my computer but I´m sure I did it several times before I began to use a UITableViewController for every tableView I have.
Oh, and i wouldn´t use dataSource as a name for your array. Just to prevent naming conflicts.
I am trying to create the UIPickerview as described in the iPhone development book by Dave Mark. I have a NSArray which is declared as a property in the h file which will store the data for the UIPickerview. So here is what I have:
in the .h file:
#interface RootViewController : UIViewController {
NSArray *dateForPicker;
}
#property (nonatomic, retain) NSArray *dateforPicker;
#end
In the .m file viewDidLoad method (I do have #synthesize for thedateForPicker property at the beginning of the .m file):
NSArray *tempArray = [[NSArray alloc] initWithObjects:#"1",#"2",#"3",#"4",#"5", nil];
self.dateforPicker = tempArray;
[tempArray release];
When the UIPickerview comes up, it comes up with "?" in all the rows. So when I used a breakpoint to inspect the values of tempArray and dateForPicker in the viewDidLoad method, I find that the tempArray is fine but the dateForPicker never gets the values from the tempArray. Xcode says "Invalid Summary" for the dateForPicker array and has "out of scope" as the values for the five rows. What is going on? As described by the book, this should work.
Here is the code for the UIPickerView:
#pragma mark -
#pragma mark picker data source methods
-(NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView{
return 1;
}
-(NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component{
return [dateforPicker count];
}
#pragma mark picker delegate methods
-(NSString *)pickView:(UIPickerView *)pickerView titleForRow:(NSInteger)row
forComponent:(NSInteger)component{
return [dateforPicker objectAtIndex:row];
}
#end
Some problems with your code. I'm not clear if you've typed this into the question manually or copied and pasted from your actual code:
You are setting self.dateforPicker and not self.dateForPicker, there is a difference in capitalisation between your ivar and your property. In iOS the compiler will have synthesized a dateforPicker ivar when you declared your property, which was set in your viewDidLoad, but in your other methods you may be referring to the dateForPicker ivar, which is never touched.
Your RootViewController does not declare that it implements the UIPickerViewDataSource or UIPickerViewDelegate protocols
Your declaration of the titleForRow method is wrong - yours begins with pickView rather than pickerView so will not get called.
If you have the correct number of rows in your component (you said multiple question marks, how many?), so it looks like the data source is wired up properly, but you also need to connect the delegate, as this is what actually supplies the values for each row. The datasource, confusingly, only supplies the number of components and the number of rows per component.