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];
}
Related
I want to add a refresh bar button in my view controller
I put in the viewdidload() this code:
UIBarButtonItem *refreshButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemRefresh target:self action:#selector(refreshTable)];
self.navigationItem.leftBarButtonItem = refreshButton;
and the refreshtable function is :
- (void) refreshTable
{
[self.tableView reloadData];
NSLog(#"table is refreshing ....");
}
this is a screenshot of the refresh button:
but it doesn't work, shall i add something else in the refreshtable function?, the table view doesn't refreshed!, the message "table is refreshing ...." appears everytime i click on the refresh button, but the table doesn't load new data!
when refreshing, can I have this icon somewhere?
If I had two table view in my view controller, and I want when I click on the refresh button, just the first table to be reloaded,
I put the same you code that you suggested, but it doesn't work here! should I do something else?
The issue is you added the data fetching logic in your viewDidLoad (according to your comments).
The viewDidLoad method will be called when the view is loading. It' won't be called unless you dismiss the view and present it again(it won't be calles if you call the reloadData it's just for reloading the UITableView).
When you call the reloadData the data source is still with old data, it is not updated.
So implement your refreshTable method like:
- (void) refreshTable
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:#"http://....../fastnews.php"]];
[self performSelectorOnMainThread:#selector(fetchedData:) withObject:data waitUntilDone:YES];
});
}
-(void)fetchedData:(NSData *)responseData
{
NSError *error;
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:responseData options:kNilOptions error:&error];
fastResults = [json objectForKey:#"nodes"];
[self.tableView reloadData];
}
try this .
[self.tableView beginUpdates];
[tableView reloadRowsAtIndexPaths:[tableView indexPathsForVisibleRows]
withRowAnimation:UITableViewRowAnimationNone];
[self.tableView endUpdates];
You need to update your dataSourceAaaay in refreshTable method before reload table.
I need to change a data (a label) from the app's delegate method ApplicationDidEnterForeground without allocating a new view. The view is called "Reminder", so I imported it into the delegate and I can access its data only if I allocate it (Reminder *anything = [Reminder alloc...etc), but since I want to change the current view loaded I need to have direct access to the view that's already loaded.
How would I do to change the main view's label from the delegate as soon as my application enters foreground?
obs: I know I can do it on -(void)ViewDidLoad or -(void)ViewWillAppear but it won't solve my problem, since it won't change the label if, for example, the user opens the app through a notification box (slide icon when phone is locked). In that case, none of the above methods are called if the app was open in background.
I don't know if I was clear, hope I was. Thank you in advance.
IF you are using storyboards, you can do this to access the current view being seen
- (void)applicationDidEnterBackground:(UIApplication *)application
{
UINavigationController *a=_window.rootViewController;
Reminder *rem = a.topViewController;
rem.label.text=#"test";
}
IF not using story boards
When I create views that I need to access later, I define them as a property, like this
on AppDelegate.h
//#interface SIMCAppDelegate : UIResponder <..........>
//{
//Some variables here
//}
//Properties here
#property (strong, nonatomic) Reminder *reminder;
//Some method declaration here
//eg: -(void) showSomething;
on AppDelegate.m
//#implementation AppDelegate
#synthesize reminder;
so when I alloc/init the view like this
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
//codes before here
self.reminder = [[Reminder alloc] init];
self.reminder.label.text = #"OLD LABEL";
//codes after here
}
I will be able to access it again after allocation on other methods, like this
- (void)applicationWillEnterForeground:(UIApplication *)application
{
self.reminder.label.text = #"NEW LABEL";
}
just send a notification from your ApplicationDidEnterForeground: method and receive it on that class where you want to update the label... Like this..
//Your ApplicationDidEnterForeground:
[[NSNotificationCenter defaultCenter] postNotificationWithName:#"UpdateLabel" withObject:nill];
and add observer in it viewDidLoad: of that controller where you want to update label
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(updateLabel:)
name:#"UpdateLabel"
object:nil];
made your method in same class ...
- (void)updateLabel:(NSNotification *)notification{
update label
}
Might be you can try following code -
NSMutableArray *activeControllerArray = [self.navigationController.viewControllers mutableCopy];
for (int i = 0; i< [activeControllerArray count]; i++) {
if ([[activeControllerArray objectAtIndex:i] isKindOfClass:[Reminder Class]) {
Reminder *object = [activeControllerArray objectAtIndex:i];
//Perform all the task here which you want.
break; //Once found break the loop to do further processing.
}
}
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.
I have ViewController (A) with TableView where each row is a directory name. Each row is pushing a new ViewController (B). Inside ViewController B I am running method to delete the currently opened directory. However, when I pop the view and go back to TableView I obviously still have this directory on list. How can I fix it?
How can I refresh the TableView while going back in navigation?
And no, simple [self.tableView reloadData] inside -viewWillAppear doesn't really work.
Code:
My -viewWillAppear:
- (void)viewWillAppear:(BOOL)animated {
NSString *docDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
self.arrFolders = [[NSMutableArray alloc] init];
[self allFilesInDocuments:docDir];
}
-allFilesInDocuments is just adding items to array ([arrFolders addObject:folder];).
Inside -tableView:cellForRowAtIndexPath: I add data with
cell.textLabel.text = [arrFolders objectAtIndex:indexPath.row];
Use a NSNotification.
In the viewDidLoad of ViewController (A) register for a notification:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(deletedRow:) name:#"deletedRow" object:nil];
Also add the selector function:
-(void) deleteRow:(NSNotification *)notif{
[self.tableView reloadData];
}
Finally in the child view controllers, in your Delete function post the notification:
[[NSNotificationCenter defautCenter] postNotificationName:#"deletedRow" object:nil userInfo:nil];
Hope it helps
An edit to the datasource and a reload in -viewWillAppear should of worked though failing this you could perform the update via an parent view controller call or an NSNotification.
I have a TabBarController with 2 tabs, in one is a MapView and in the other one a simple TableView in a NavigationController. Both display Data from the same source. If any Data in the table is tapped, I add a DetailViewController to the NavigationController and show more details. Now on the MapView I also want to open this DetailViewController when the Data is tapped in the map. What's the best way to do this? I tried some with Notification but this doesn't work well because the TableViewController is finished loading (and registered as an observer) after the Notification is sent.
Here's my code:
MapViewController:
- (IBAction)goToNearestEvent:(id)sender {
if (currentNearestEvent) {
[[self tabBarController] setSelectedIndex:1];
NSDictionary *noteInfo = [[NSDictionary alloc] initWithObjectsAndKeys:currentNearestEvent, #"event", nil];
NSNotification *note = [NSNotification notificationWithName:#"loadDetailViewForEvent" object:self userInfo:noteInfo];
[[NSNotificationCenter defaultCenter] postNotification:note];
[noteInfo release];
}
}
TableViewController:
- (void)viewDidLoad {
[super viewDidLoad];
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
[nc addObserver:self
selector:#selector(loadDetailViewForEvent:)
name:#"loadDetailViewForEvent"
object:nil];
}
- (void)loadDetailViewForEvent:(NSNotification *)note {
Event *e = [[note userInfo] objectForKey:#"event"];
[self loadEventDetailViewWithEvent:e];
}
So I'm very new to iOS / Cocoa programming. Maybe my approach is the wrong choice. So I hope anybody could tell me how to solve such things the right way.
I forgot to declare my structure clearly:
- UITabBarController
- MapView (1)
- NavigationControllerContainer
- NavigationControllerView (2)
- TableView
I want to push a new View from the MapView (1) to the NavigationControllerView (2).
If you're going to use notifications, the fix is to force the second tab to be "created" before it's displayed.
Something like:
UIViewController *otherController = [[[self tabBarController] viewControllers] objectAtIndex:1];
otherController.view; // this is magic;
// it causes Apple to load the view,
// run viewDidLoad etc,
// for the other controller
[[self tabBarController] setSelectedIndex:1];
I don't have access to my code, but I did something similar to:
[[self.tabBarController.viewControllers objectAtIndex:1] pushViewController:detailView animated:YES];
Give this a try and let me know.
I think the observer/notification pattern is the right one. However, you normally want "controllers" to observe "model" objects.
I would create a Model object that contains the selected Event.
When each viewController is loaded, it looks at the "Model" object and directs itself to the selected event.
When any of the viewControllers changes the selected event, it does so in the Model, and then the notification propagates to the other(s) controllers.