Three20 TTTableViewController reload data - objective-c

How can I reload the data in the TTTableViewController? I tried to
call reloadData but it doesn't display the new data.
I have created the datasource file, which is a subclass of
TTListDataSource. Basically, the init function looks like this.
- (id) initWithObject:(NSArray *)objects {
if (self = [super init]) {
NSMutableArray *priceItems = [[[NSMutableArray alloc] init] autorelease];
for (int i = 0; i < [objects count]; i++) {
[priceItems addObject:
[PriceItem itemWithName:
[[objects objectAtIndex:i] objectAtIndex:0]
lastDone:[[objects objectAtIndex:i] objectAtIndex:1] change:[[objects objectAtIndex:i] objectAtIndex:2]]];
}
self.items = [NSArray arrayWithArray:priceItems];
}
return self; }
After the view is loaded, i start to streaming some data, thus the
objects passed to initWithObject is changed, so I call 'reload' in the
subclass of the TTTableViewController in order to update the data, but
the data is not updated. It doesn't work for refresh method too. Do I need to implement any other method in
the subclass of TTListDataSource?

Try
[self invalidateModel];
That should do the trick.

invalidateModel will rebuild the model, that's not efficient.
Try [TTTableViewController refresh], your table will be reloaded.

Related

Use the variable of a for loop in a property name

I want to use the variable of a for loop in a property name, example:
#interface
{
Player *rival1, *rival2, *rival3;
}
int number;
for (number=1; number<=3; number++){
rival+number = [[Player alloc] init]; //The compiler accepts this.
rival+number.name = #"";
//^This line gives the error: "use of undeclared identifier 'rival'"
//For the first loop, I want it work like: rival1.name = #"";
}
This isn't a particularly clean way of approaching this, but to answer the question as you've posed it:
- (void)generateRivals
{
for (int i = 1; i < 4; i++)
{
NSString *propertySetString = [NSString stringWithFormat:#"setRival%d", i];
Rival *rival = [[Rival alloc] init];
[self performSelector:#selector(NSSelectorFromString(propertySetString)) withObject:rival];
}
}
We create a selector (I haven't tested this, but it should work in theory) that corresponds to the getter for the property instance represented by i.
Based on comments you've added, it seems like what you really want is a variable number of players (perhaps not exceeding a certain number) with the ability to reference them individually.
Architecturally, rather than create properties pointing to each respective rival, put them all in an array (and keep it as a property on your class). So you'd create them like this:
- (void)generateRivals:(NSUInteger)numberOfRivals
{
NSMutableArray *rivalsArray = [[NSMutableArray alloc] initWithCapacity:numberOfRivals];
for (int i = 0; i < numberOfRivals; i++)
{
Rival *rival = [[Rival alloc] init];
[rivalsArray insertObject:rival atIndex:i];
}
[self setRivalsArray:rivals];
}
Then, when you need to access a particular rival, call a method like this, which will return the rival at the index number you pass:
- (Rival *)rivalWithNumber:(NSUInteger)number
{
return [[self rivalsArray] objectAtIndex:number];
}
you cant create the properties variable names on the fly and use it, as the names get lost during compilation
you can put them in an array and enumerate over it
Player rival1 = [[Player alloc] init];
Player rival2 = [[Player alloc] init];
Player rival3 = [[Player alloc] init];
for (Player *p in #[rival1, rival2, rival3]) {
p.name = #"";
}
probably the best way would be to maintain the rivales in the array from the beginning
#interface …
#property(strong) NSArray *rivals;
#end
#implementation …
//in some useful method, like initializer
_rivals = [NSMutableArray array]
for (int i = 0; i < 3 ++i){
//create player and add to array
}
#end
You might want to look into Key Value Coding.
You need to create properties for your rivals
#interface …
#property(strong) Player *rival1;
#property(strong) Player *rival2;
#property(strong) Player *rival3;
#end
#implementation …
-(id)init…
{
if(self = [super init…]){
for (int i = 1; i < 4; ++i) {
Player *rival = [[Player alloc] init];
rival.name = [NSString stringWithFormat:#"rival%d", i];
[self setValue: rival forKey:[NSString stringWithFormat:#"rival%d", i]];
}
}
return self;
}
#end
Instead of running a for loop to set the names of the players to #"" just add the line
name = #"" in your init method of your Player class. It's faster and more practical than to write a for loop. You can also create a method like
-(id)initWithName:(NSString *)initName{
self = [super init];
if (self){
name = initNane;
}
return self;
}
to set the name of your player at the moment it's created. Although if you want absolutely a for loop, you can use the method posted by vikingosegundo previously.

Add cell to UITableView

Can't handle simple problem - adding cells to UITableView.
I have single-view application, with added from Objects - Table View and simple NSArray (deseriliazed json from internet-grabbed data).
- (void) didLoadMusicList:(APIDownload *)request
{
NSLog(#"Music list loaded");
CJSONDeserializer *deserializer = [CJSONDeserializer new];
NSDictionary *dict = [deserializer deserializeAsDictionary:request.downloadData error:nil];
NSArray *response = [dict objectForKey:#"response"];
NSArray *audios = [response subarrayWithRange:NSMakeRange(1, response.count-1)];
for(int i = 0; i < audios.count; i++)
{
NSDictionary *audio = [audios objectAtIndex:i];
// add a cell?
}
}
So, how do I add cell for each element?
You need to implement the UITableViewDatasource on your view controller. You can use the same array to provide the data to the cells, and return them using the cellForRowAtIndexPath datasource method. You also need to provide with the count of cells on your tableView.
Check this documentation: http://developer.apple.com/library/ios/#documentation/uikit/reference/UITableViewDataSource_Protocol/Reference/Reference.html

Objective-C Array of Objects

Although experienced with OOP, I am an absolute newbie with Objective-C. I have the following code:
// header files have been imported before this statement...
CCSprite *treeObstacle;
NSMutableArray *treeObstacles;
#implementation HelloWorldLayer {
}
-(id) init
{
// create and initialize our seeker sprite, and add it to this layer
treeObstacles = [NSMutableArray arrayWithObjects: nil];
for (int i=0; i<5; i++) {
treeObstacle = [CCSprite spriteWithFile: #"Icon.png"];
treeObstacle.position = ccp( 450-i*20, 100+i*20 );
[self addChild:treeObstacle];
[treeObstacles addObject: treeObstacle];
}
NSLog (#"Number of elements in array = %i", [treeObstacles count]);
return self;
}
- (void) mymethod:(int)i {
NSLog (#"Number of elements in array = %i", [treeObstacles count]);
}
#end
The first NSLog() statement returns "Number of elements in array = 5". The problem is that (although treeObstacles is a file-scope variable) when calling the method "mymethod", I'll get an EXC_BAD_ACCESS exception.
Can anybody please help me?
Thanks a lot
Christian
you created treeObstacles by
treeObstacles = [NSMutableArray arrayWithObjects: nil];
which will return an autoreleased object, and you didn't retain it so it will be released soon
you have to retain it by calling retain on it
[treeObstacles retain];
of simple create it by
treeObstacles = [[NSMutableArray alloc] init];
and you need to remember to release it when done like
- (void)dealloc {
[treeObstacles release];
[super dealloc];
}
you need to read more about management in Objective-C
https://developer.apple.com/library/mac/#documentation/General/Conceptual/DevPedia-CocoaCore/MemoryManagement.html
or use ARC so no need to worry retain/release anymore
http://developer.apple.com/library/ios/#releasenotes/ObjectiveC/RN-TransitioningToARC/Introduction/Introduction.html
another problem, you need to call [super init] in your init method
- (id)init {
self = [super init];
if (self) {
// your initialize code
}
}
otherwise your object won't initialize properly

Adding to NSDictionary in while loop

I'm adding Annotations to a map, and in order to call them I need to store their Database ID value in an Dictionary
int i = 0;
...
while(sqlite3_step(statement) == SQLITE_ROW) {
int thisvenueid = sqlite3_column_int(statement, 4);
NSNumber *vid = [[NSNumber alloc] initWithInt:thisvenueid];
if (dict == nil) {
dict = [[NSMutableDictionary alloc] init];
}
[dict setObject:vid forKey:[NSNumber numberWithInt:i]];
}
And then I'm using it like this:
[self.mapView selectAnnotation:[dict objectForKey:selectedVenue] animated:YES];
However, this just crashes (Sigbart .. how I don't like Sigbart). I'm very thankful for any advise.
you are trying to select Annotation and passing NSNumber instead of object of MKPlacemark
please refer to documentation:
selectAnnotation:animated:
Selects the specified annotation and displays a callout view for it.
- (void)selectAnnotation:(id < MKAnnotation >)annotation animated:(BOOL)animated
Parameters
*annotation*
The annotation object to select.
animated
If YES, the callout view is animated into position.
Discussion
If the specified annotation is not onscreen, and therefore does not have an associated annotation view, this method has no effect.
Why are you using setObject? From what it looks like, you should be using addObject:
[dict addObject: vid forKey: [NSNumber numberWithInt:i]];
Forgive me if this is not the problem - haven't worked with Objective-C for some time.

My NSMutableArray loses its objects outside of function scope even after alloc / init

I'm totally stumped on this one. I have an NSMutableArray which is declared in my header and set as a property, synthesized etc. I then call a function that allocates and initializes the array, and I add custom objects to it. I do a for each loop after the objects are added to ensure that they are actually contained within the array and they are. Once the program goes outside of this function scope, though, suddenly the array is empty.
header file:
#interface ScheduleViewController : UITableViewController {
NSString *login_id;
NSMutableArray *events;
}
- (id)initWithID:(NSString*)l_id;
- (void)grabURLInBackground; // ASIHTTP example method
#property (nonatomic, retain) NSString *login_id;
#property (nonatomic, retain) NSMutableArray *events;
#end
implementation:
#synthesize events;
- (void)requestFinished:(ASIHTTPRequest *)request
{
// Use when fetching text data
NSString *response = [request responseString];
SBJsonParser *parser = [[SBJsonParser alloc] init];
NSArray *eventDics = [parser objectWithString:response error:nil]; // an array of dictionaries of events
NSDateFormatter *dateForm = [[NSDateFormatter alloc] init];
// Allocate empty event object and initialize the mutable array
Event* event = [[Event alloc] init];
self.events = [[NSMutableArray alloc] initWithCapacity:[eventDics count]];
// loop through the array of dictionaries
for (int i = 0; i < [eventDics count]; i++)
{
NSDictionary *dict = [eventDics objectAtIndex:i];
for(NSString *key in dict) {
// for the sake of readability i wont include the code
// but the event is populated here
}
[self.events addObject:event];
[event release];
}
NSLog(#"Array Count: %i", [self.events count]);
for (Event *e in events) {
NSLog(#"eventid: %i, type: %#, price: %f, name: %#", e.event_id, e.type, e.price, e.name);
}
[parser release];
[dateForm release];
}
So the above code works fine and prints out the variables from the Event objects that are stored in the events mutable array.
What I want to do is use the events array in another function now, and when I try to, the count is 0 and also no objects are stored in the array when I look at it.
In viewDidUnload I set self.events = nil; and in dealloc I do [self.events release]
You are doing your alloc/init for the Event *event object outside of your for loop. This means you are adding the same object every time you add it to the array. You should move this line:
Event* event = [[Event alloc] init];
To the inside of your
for (int i=0 ... loop.
Not sure that would explain the symptoms you are seeing, but it could, since the following statement:
[event release]
is also releasing that one allocated object once for every time through the loop - so you are releasing the object multiple times. If you move the Event alloc to the inside of the loop then the release will be ok. (adding the object to the array will retain it so its ok to release it, but you need to allocate a new Event each time through the loop).
Basically your code should look like this: (note I've also added an autorelease to your array alloc).
// Allocate empty event object and initialize the mutable array
self.events = [[[NSMutableArray alloc] initWithCapacity:[eventDics count]] autorelease]; // assigning to the retain property will retain it, so autorelease it or it will be retained twice. Could also have used the arrayWithCapacity convenience method here instead and then wouldn't need to autorelease.
// loop through the array of dictionaries
for (int i = 0; i < [eventDics count]; i++)
{
Event* event = [[Event alloc] init]; // Allocate a new Event each time through the loop so you are adding a unique object to the array each time.
NSDictionary *dict = [eventDics objectAtIndex:i];
for(NSString *key in dict) {
// for the sake of readability i wont include the code
// but the event is populated here
}
[self.events addObject:event];
[event release];
}
I see many problems with this code. These include the fact that you are releasing objects at inappropriate times, and that you are getting confused about the scope of different objects. It seems to me that one of the biggest problems that you are having is re-allocating your events array every time the requestFinished: method is called. In your init method, you should do something like this:
- (id)init {
if ((self = [super init])) {
// Since it is a retain property, we should autorelease it when
// assigning to it, thus preventing an extra retain.
self.events = [[[NSMutableArray alloc] initWithCapacity:[eventDics count]] autorelease];
}
}
With that being said, here is how I would rewrite your requestFinished: method, as well as your dealloc method:
- (void)requestFinished:(ASIHTTPRequest *)request {
NSString *response = [request responseString];
SBJsonParser *parser = [[SBJsonParser alloc] init];
NSArray *eventDics = [parser objectWithString:response error:nil]; // an array of dictionaries of events
NSDateFormatter *dateForm = [[NSDateFormatter alloc] init];
// Clear the already allocated events array
[self.events removeAllObjects];
for (int i = 0; i < [eventDics count]; i++) {
// note how I assign event in here
Event *event = [[Event alloc] init];
NSDictionary *dict = [eventDics objectAtIndex:i];
for (NSString *key in dict) {
// Do whatever it is you do here
}
[self.events addObject:event];
[event release];
}
NSLog(#"Array Count: %i", [self.events count]);
for (Event *e in events) {
NSLog(#"eventid: %i, type: %#, price: %f, name: %#", e.event_id, e.type, e.price, e.name);
}
[parser release];
[dateForm release];
}
Finally, you can simply set the events property to nil in the dealloc method:
- (void)dealloc {
self.events = nil;
[super dealloc];
}
The only reason that I can think of for the array being empty is that a) it's contents are being deallocated, or b) it itself is being deallocated and set to nil. The pieces of your code that I fixed could possibly cause both of these. Try the changes that I have made, and see if they make a difference.
So I figured out the problem and it's due to an error on my part. After stepping through the function calls more closely, it turns out that the table view delegate method
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
was being called before populating the array with requestHasFinished. I was calling requestHasFinished indirectly through viewDidLoad but I think that the table view delegate method was being called when the view controller is initialized. Init was being called before viewDidLoad because the view controller is actually handled within a tab view controller which initializes all of the view controllers for each tab at the time when itself is initialized. That's another matter to get into.. I'm not sure if I like everything being initialized and setup before the views are even displayed but.. something to research.
Anyways thanks again for the help.