I have a view, when it starts, the array of objects is read from memory and the Table View is completed with the objects. When I go to another view and write another objects to file and get back to the first view, the objects are loaded into the array from file as it should but the doesn't reload somehow... [myTableView reloadData] doesn't work, neither does setNeedsDisplay.
-(void)viewWillAppear:(BOOL)animated
{
contactsToLayNow=[NSKeyedUnarchiver unarchiveObjectWithFile:fPath()];
NSLog(#"NOW IN VIEW WILL APPEAR");
for(Contact *cn in contactsToLayNow)
{
NSLog(#"%#", cn.fName);
}
//[self.view setNeedsDisplay];
[quickDialTableView reloadData]; //MOREOVER EXCEPTION HERE IS THROWN
[super viewWillAppear:YES];
}
you will need to retain contactsToLayNow - I guess it's being autoreleased sometime during your tableViews's drawing :)
contactsToLayNow = [[NSKeyedUnarchiver unarchiveObjectWithFile:fPath()] retain];
PS It's not required but you should put your call to super at the top of this method ;)
Move your [super viewWillAppear:animated]; to first line of viewWillAppear: method.
Related
I have a plist that is populated by the user within my app. I am trying to display the contents of that plist in a UITableView and it wont display until after the app is relaunched which means its not updating. Here is what I have:
- (void)viewDidLoad
{
[super viewDidLoad];
// Uncomment the following line to preserve selection between presentations.
// self.clearsSelectionOnViewWillAppear = NO;
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem;
NSString *path = [[NSBundle mainBundle] pathForResource:#"foodList" ofType:#"plist"];
arrayFood = [[NSMutableArray alloc]initWithContentsOfFile:path];
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self.tableView reloadData];
NSString *path = [[NSBundle mainBundle] pathForResource:#"foodList" ofType:#"plist"];
arrayFood = [[NSMutableArray alloc]initWithContentsOfFile:path];
}
Thanks in advance!
From what i see, you should be calling
[self.tableView reloadData]
after you load the data from plist to foodArray in viewDidAppear...
Whenever you update the pList file simply call the self.tableView reloadData. Also, make sure that the data source you are using for your UITableView is updated. This datasource can be an NSDictionary, NSArray, NSMutableArray etc. The reloadData method of the tableView will trigger the cellforRowIndexPath method where you will create the UITableViewCell.
The best way to update the UITableView with newest data is to overwrite the setter method of data array.
For example your data array is arrayFood so you may have its property as strong or say retain
#property(nonanatomic,stron) NSMutableArray *arrayFood;
then you should overwrite its setter method.
-(NSMutableArray *)setArrayFood:(NSMutableArray *)array
{
//set your data array here and simply call reloadData
[self.tableView reloadData];
}
in that way your tableView always get update whenever your data array changed.
Also use NSNotifications for plist change and update your arrayFood there.
Using NSNotificationCenter may help you here.
In the ViewController that updates your plist, place this code when this update happens:
[[NSNotificationCenter defaultCenter] postNotificationName:#"plistDidUpdate" object:self];
In the ViewController you'll want to add this code to viewDidLoad:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(plistDidUpdate:) name:#"plistDidUpdate" object:nil];
Also, add this function in the same ViewController:
-(void)plistDidUpdate:(id)sender{
[self.tableView reloadData];
}
I am trying to update some of my views when they appear, so I naturally found myself using the viewDidAppear: and viewWillAppear: methods. However, I have experienced two problems with using these methods:
When I only implement one of the methods, the changes that I am looking to make are not completely there, so in order for everything to work, I implemented both methods with the same code.
Even after implementing both methods with the same code, there is a 0.5 to 1 second delay when updating the view's content.
Here is my code for my custom made table view controller:
-(void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self.tableView reloadData];
}
-(void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[self.tableView reloadData];
}
For some reason, I must call the reloadData method twice to completely update my table view.
Here is my code for my custom made normal view controller:
-(void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
navItem.title = #"Name1";
nameLabel.text = #"Name1";
nameField.hidden = YES;
}
-(void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
navItem.title = #"Name1";
nameLabel.text = #"Name1";
nameField.hidden = YES;
}
Thank you!
You should only use the viewWillAppear method.
I'm using Cocoanetic's pull-to-reload but with a twist: I would like the UITableView to be able to pull up, as it were, to load more data.
I customized the classes and have managed to adjust all functionality to support this. What got me stumped, basically, is where in my code to create and add the extra view.
I first tried to add it in viewDidLoad of Cocoanetic's class (a UITableViewController):
- (void)viewDidLoad
{
[super viewDidLoad];
refreshHeaderView = [[EGORefreshTableHeaderView alloc] initWithFrame:CGRectMake(0.0f, 0.0f - self.view.bounds.size.height, 320.0f, self.view.bounds.size.height)];
refreshFooterView = [[EGORefreshTableFooterView alloc] initWithFrame:CGRectMake(0.0f, 0.0f + self.tableView.contentSize.height, 320.0f, 20.0f)];
[self.tableView addSubview:refreshHeaderView];
[self.tableView addSubview:refreshFooterView];
self.tableView.showsVerticalScrollIndicator = YES;
}
This does not work, as self.tableView.contentSize.height is zero at this point, because the table hasn't loaded it's data yet.
Not to worry, I thought, and tried to add it in the viewDidLoad of the UITableViewController subclass I made:
- (void)viewDidLoad
{
[super viewDidLoad];
// stuff
self.model = [ListingModel listingModelWithURL:#"avalidurl" delegate:self];
refreshFooterView = [[EGORefreshTableFooterView alloc] initWithFrame:CGRectMake(0.0f, 0.0f + self.tableView.contentSize.height, 320.0f, 20.0f)];
[self.tableView addSubview:refreshFooterView];
}
Note I set the model first, but that also didn't work, for the same reason. I assume the table hasn't been layed-out yet. In frustration I gave my class a BOOL property and an addFooter method (the BOOL to make sure it's only called once) called from tableView:cellForRowAtIndexPath: which obviously is a far cry from The Right Way™
So what would, given this scenario, be The Right Way™?
The solution was easier than I thought and 7KV7 actually gave me the hint I needed.
- (void)viewDidLoad
{
[super viewDidLoad];
// stuff
self.model = [ListingModel listingModelWithURL:#"avalidurl" delegate:self];
/*
We're forcing a reload of the table here, that way the table has a
contentSize, so adding the footerView now works correctly.
*/
[self.tableView reloadData];
[self addRefreshFooter];
}
From this previous SO question Get notified when UITableView has finished asking for data? subclassing UITableView's reloadData is the best approach :
- (void)reloadData {
NSLog(#"BEGIN reloadData");
[super reloadData];
NSLog(#"END reloadData");
}
reloadData doesn't end before the table has finish reload its data. So, when the second NSLog is fired, the table view has actually finish asking for data.
If you've subclassed UITableView to send methods to the delegate before and after reloadData. It works like a charm.
I have a strange problem with a UIView :
I want to show an Activity Indicator View that I created with Interface Builder to indicate long running activity.
In the viewDidLoad function of my principal viewController I init the ActivityIndicator View like this :
- (void)viewDidLoad {
[super viewDidLoad];
appDelegate = (MyAppDelegate *)[[UIApplication sharedApplication] delegate];
load = [[ActivityIndicatorViewController alloc] init];
...
When I push a button it call this IBAction :
- (IBAction)LaunchButtonPressed{
// Show the Activity indicator view.
[self.view addSubview:load.view];
// eavy work
[self StartWorking];
// Hide the loading view.
[load.view removeFromSuperview];
}
In the StartWorking function, I ask a request to an internet serveur and parse the XML file that it return me.
The problem is that if I Call my StartWorking function, the application do not start by showing the Activity Indicator view but with the StartWorking function.
Whereas if I remove the call to StartWorking function, the view is shown.
Is someone able to explain me why? :s
Have you tried to call the StartWorking method on a different thread?
Maybe its heavy process prevents other instructions to take place.
Look at the NSThread class, especially the detachNewThreadSelector:toTarget:withObject: method.
EDIT: About the pool problem, you need to create a pool in your StartWorking method, if it's called on a different thread:
- ( void )StartWorking
{
NSAutoreleasePool * pool = [ [ NSAutoreleasePool alloc ] init ];
/* Code here... */
[ pool release ];
}
Replace :
[self.view addSubview:load.view];
With :
[self performSelector:#selector(addLoadingSubview) afterDelay:0.1f];
And create the method :
-(void)addLoadingSubview{[self.view addSubview:load.view];}
Ok, I found a solution based on santoni answer :
- (IBAction)LaunchButtonPressed{
// Show the Activity indicator view.
[self performSelector:#selector(ShowActivityIndicatorView) withObject:nil afterDelay:0];
// eavy work
[self performSelector:#selector(StartWorking) withObject:nil afterDelay:2];
// Hide the loading view.
[load.view removeFromSuperview];
}
The Activity Indicator view is dislayed before the call to the eavy function.
Thank's for answering.
I have an application that has a UITableView. This UITableView is populated by an NSMutableArray being held (as a property) in the appDelegate. You can think of this as an email window. It lists messages in a subclassed UITableViewCell. When a new message appears, I have all the code done which downloads the message, adds the data to the appDelegate's NSMutableArray which holds all of the messages. This code is working fine.
Now, once the new message is downloaded and added to the array, I am trying to update my UITableView using the following code, however - the UITableView's delegate functions do not get called.
The odd thing is when I scroll my UITableView up and down, the delegate methods finally get called and my section headers DO change (they show the message count for that section). Shoudn't they update in real-time and not wait for my scrolling to trigger the refresh? Also, the new cell is never added in the section!!
Please Help!!
APPDELEGATE CODE:
[self refreshMessagesDisplay]; //This is a call placed in the msg download method
-(void)refreshMessagesDisplay{
[self performSelectorOnMainThread:#selector(performMessageDisplay) withObject:nil waitUntilDone:NO];
}
-(void)performMessageDisplay{
[myMessagesView refresh];
}
UITableViewController Code:
-(void) refresh{
iPhone_PNPAppDelegate *mainDelegate = (iPhone_PNPAppDelegate *)[[UIApplication sharedApplication] delegate];
//self.messages is copied from appDelegate to get (old and) new messages.
self.messages=mainDelegate.messages;
//Some array manipulation takes place here.
[theTable reloadData];
[theTable setNeedsLayout]; //added out of desperation
[theTable setNeedsDisplay]; //added out of desperation
}
As a sanity check, have you verified that theTable is not nil at that point?
You could try putting a delay on the reloadData call - I had a similar problem when I was trying to get my tableview to update when reordering cells, except that the app crashed if I called reloadData during it.
So something like this might be worth a try:
Refresh method:
- (void)refreshDisplay:(UITableView *)tableView {
[tableView reloadData];
}
and then call it with (say) a 0.5 second delay:
[self performSelector:(#selector(refreshDisplay:)) withObject:(tableView) afterDelay:0.5];
Hope it works...
If you call reloadData from within a dispatched method, make sure to execute it on the main queue.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND,0), ^(void) {
// hard work/updating here
// when finished ...
dispatch_async(dispatch_get_main_queue(), ^(void) {
[self.myTableView reloadData];
});
});
..same in method form:
-(void)updateDataInBackground {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND,0), ^(void) {
// hard work/updating here
// when finished ...
[self reloadTable];
});
}
-(void)reloadTable {
dispatch_async(dispatch_get_main_queue(), ^(void) {
[myTableView reloadData];
});
}
Have you tried setting a breakpoint in your refresh method just to be sure your messages array has the correct content before calling reloadData?