TableViewCells deleted after leaving the page - iphone

I have a UITableView and you can add/delete cells if you like. There are 3 pages. The second page allows the user to add cells to the table view, which is on the third page. The first page is just a navigation page. If i add any number of cells to the table view, i can see them on the 3rd page fine! I can return to the second page, and then return back to the 3rd page. And the cells will still be there, but if i go to the 2nd page, then the 1st, and back to the table view, all the cells are gone!! How can i fix this? My Code for navigating through the pages is as follows:
-(IBAction)button1:(id)sender{
RootViewController * black = [[BlackFacePlateViewControlller alloc]init];
black.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentModalViewController:black animated:YES];
[black release];
}
and:
- (IBAction)back:(id)sender {
[self dismissModalViewControllerAnimated:YES];
}
Thanks in advance!

Kurt, you have to understand something about persistence on not just the iPhone, but any system. You most likely have an array that is loaded into the table, but when the controller class containing that array is dismissed (as you have it with [self dismissModalViewControllerAnimated:YES];), the chance is more likely than not that dealloc (or in the case of ARC, a mass release of your objects) will be called on that controller, and your array will be set to nil and reclaimed but the system. You need a storage mechanism, whether that's a plist, a specific file format, or XML, you need some means of retrieving the datasource array even after it's been destroyed.
I myself am now huge advocate of the plist route, as they're just so convenient and easy to use, and they can be edited with absolutely massive amounts of data with little side effects.

Related

UINavigationController suggestion

I have read that UINavigationController are best option when you want to jump from n number of screen to first screen. It takes following code to do so:
NSMutableArray *array=[[NSMutableArray alloc]initWithArray:self.navigationController.viewController];
[array removeObjectAtIndex:1];
[array removeObjectAtIndex:1];
[array removeObjectAtIndex:1];
self.navigationController.ViewController=array;
[self.navigationController popViewController:YES];
by using this code I can go directly from fourth screen to first screen directly .
If I don't using navigation controller then also by making the object of firstSCreen in fourth screen I can achieve the same thing within couple of lines.Then why one should go for navigation controller? If answer is for memory optimization then we are autoreleasing the the object of firstViewController and now we are using auto referencing.
first thing this is wrong approach to pop. true one is
[self.navigationController popToRootViewController:YES];
and second thing is that if you are at 4 screen, then by poping to a specific viewcontroller will no pop all screens. here is memory issue.
another issue is that some times you don't want to lose parent screens data. if you use method of pop to specific viewcontroller, the data will be lost as the object having data is released. you made a new one.
point is that this depends on your conditions what is suit able in your scenario. but the normal and usual approach is that don't use specific popout techniqu as it may cause problems
You can use
[self.navigationController popToViewController:(UIViewController *) animated:(BOOL)];
You just need to pass ViewController's object on which you want to move directly... And between view controller's will pop-out automatically
Hope this helps you...
agreed with 'The Saad'
one more thing is that if you have various screens with the data coming from server.
it will be quite difficult to render(user interection) screen second while loding data on first.
same with all other sibling views.

Refreshing the content of TabView

Ok I am trying to refresh the tab content of each of my tabs after a web call has been made, and I have tried soo many different methods to do this that I have lost count. Could someone please tell me how this is possible?
The web call just calls JSON from a server and uses it to update the content of the tabs. For testing purposes I have a button set up inside my settings class. Settings class is a view within the home tab which has a button called refresh. When clicked this takes JSON stored on the device which is different to the one called from the web call on application start up. This saves me having to change the JSON on the server.
I will take you through some of the techniques I have tried and would be grateful if someone could tell me what I am doing wrong.
I tried making an instance of the class and calling the refresh method like this
DashboardVC *db = [[DashboardVC alloc] init];
[db refreshMe];
The refresh method in dashboard class is this
-(void) refreshMe
{
[self loadView];
[self viewDidLoad];
}
However no luck. This method will work if I call it inside the Dashboard class, but wont work if I call it from another class. I think it is become I am instantiating a new class and calling refresh on that. So I dropped that technique and moved onto the next method
This loops through all the tabBars and changes the tabTitles without any issues, so it I know it is definitely looping through the ViewControllers properly.
I also tried every varient of the view methods like ViewDidAppear, viewWillAppear etc, no luck.
I also tried accessing the refreshMe method I made in the dashBoard class through the tabController like this
[[[self.tabBarController viewControllers] objectAtIndex:0] refreshMe];
But again no luck, this just causes my application to crash.
I read through this guide
https://developer.apple.com/library/ios/#documentation/WindowsViews/Conceptual/ViewControllerPGforiOSLegacy/TabBarControllers/TabBarControllers.html
on the apple website but it doesn't seem to cover how to refresh individual tab content.
All I want is to have each individual tab refresh its content after the web call is made, and have spent ages trying to figure this out, but nothing is working.
So would be very grateful if someone could show me what I am doing wrong?
Thanx in advance....
EDIT:
Expand on what I have tried
After discussion with Michael I realised you should never call loadView as against Apple guidelines. So I removed any references to LoadView. I have now placed a method in all the main ViewControllers called RefreshMe which sets up the views, images texts etc in the class. And this method is placed inside the ViewDidLoad. Now I want to be able to call these methods after a web call has taken place, so effectively refreshing the application.
My viewDidLoad now looks like this in all my the main classes.
- (void) viewDidLoad {
[super viewDidLoad];
[self refreshMe];
}
And then the refreshMe method contains the code which sets up the screen.
The JSON data pulled from the web call will set up the content of each of the 5 tabs, so need them all to be refreshed after web call.
I tried looping through the viewControllers and calling viewDidLoad, which should in turn call the refreshMe method which sets up the class, however nothing happens. Code I used was this
NSArray * tabBarViewControllers = [self.tabBarController viewControllers];
for(UIViewController * viewController in tabBarViewControllers)
{
[viewController viewDidLoad];
}
For the time being I have also included
NSLog(#"Method called");
in the viewDidLoad of each class to test if it is being called. However the message is only being printed out when I first load the application or if I re-enter the application. This method should be called after I click the refresh button in the settings screen but it isn't and I have no idea why.
Anyone have any idea why this is not working?
From the question and your comments, it sounds like there are at least two problems:
You're having trouble accessing the view controllers managed by your app's tab bar controller.
You seem to be working against the normal operation of your view controllers.
The first part should be straightforward to sort out. If you have a pointer to an object, you can send messages to that object. If the corresponding method doesn't execute, then either the pointer doesn't point where you think it does or the object doesn't have the method that you think it does. Let's look at your code:
NSArray * tabBarViewControllers = [self.tabBarController viewControllers];
for(UIViewController * viewController in tabBarViewControllers)
{
[viewController viewDidLoad];
}
This code is supposed to call -viewDidLoad on each of the view controllers managed by some tab bar controller. Leaving aside the wisdom of doing that for a moment, we can say that this code should work as expected if self.tabBarController points to the object that you think it does. You don't say where this code exists in your app -- is it part of your app delegate, part of one of the view controllers managed by the tab bar controller in question, or somewhere else? Use the debugger to step through the code. After the first line, does tabBarViewControllers contain an array of view controllers? Is the number of view controllers correct, and are they of the expected types? If the -viewDidLoad methods for your view controllers aren't being called, it's a good bet that the answer is "no," so figure out why self.tabBarController isn't what you think.
Now, it's definitely worth pointing out (as Michael did) that you shouldn't be calling -viewDidLoad in the first place. The view controller will send that method to itself after it has created its view (either loaded it from a .xib/storyboard file or created it programmatically). If you call -viewDidLoad yourself, it'll either run before the view has been created or it'll run a second time, and neither of those is helpful.
Also, it doesn't make much sense to try to "refresh" each view controller's view preemptively. If your app is retrieving some data from a web service (or anywhere else), it should use the resulting data to update its model, i.e. the data objects that the app manages. When a view controller is selected, the tab bar controller will present its view and the view controller's -viewWillAppear method will be called just before the view is displayed. Use that method to grab the data you need from the model and update the view. Doing it this way, you know that:
the view controller's view will have already been created
the data displayed in the view will be up to date, even if one of the other view controllers modified the data
you'll never spend time updating views that the user may never look at
Similarly, if the user can make any changes to the displayed data, you should ensure that you update the model either when the changes are made or else in your view controller's -viewWillDisappear method so that the next view controller will have correct data to work with.
Instead of refreshing your view controllers when updating your tab bar ordering, why not simply refresh your views right before they will appear by implementing your subclassed UIViewController's viewWillAppear: method?
What this means is that each time your view is about to appear, you can update the view for new & updated content.

Reloading a table's data

I have 5 view controllers, which are in a navigation controller hierarchy. When I reach my last view controller, there's a button that lets me go back to the "Home" view controller (named CardWalletViewController) which is a table view controller. Here's the method I have in my last VC (PointsResultsVC)
- (IBAction)homePressed:(id)sender {
NSArray *VCs = [self.navigationController viewControllers];
[self.navigationController popToViewController:[VCs objectAtIndex:0] animated:YES];
}
CardWalletVC's cells is being filled up by values coming from the saved instances of a Card in NSUserDefaults and it works fine.
Now, what I want is to update the value in my CardWalletViewController, which is the points of a card coming from my PointsResultsVC. Note that this points is saved in NSUserDefaults.
Upon the process of trying to update of the value shown in my CardWalletVC, I placed [self.tableView reloadData]; inside -viewDidLoad, -viewWillAppear, and -viewDidAppear of the said class. I tried placing it one by one in each of this methods, yet it doesn't seem to work.
Advice Please.
EDIT: problem solved
This one served as my guide Save data from one tab and reloadData in another tab.
And as I have discovered, -viewDidLoad of a certain class will only be called once, all throughout runtime of app. Whereas -viewWillAppear, it will be called every time the view appears.
So, I just moved the way I am loading the values saved in NSUserDefaults from -viewDidLoad to -viewWillAppear. Also, inside -viewWillAppear I placed the [self.tableView reloadData]. Then there, problem solved.
There's a lot of things that could be going wrong so you'll need to provide more info to narrow it down.
First of all, viewDidLoad will not be called so you can remove it from here. Both viewWillAppear and viewDidAppear will be called, so you only need it one of these 2 methods.
Next, you need to determine what the actual issue is.
1 - is self.tableView a proper reference to the table? Are you properly maintaining the reference?
2 - is the table actually reloading but using the old data, or is it not reloading at all? You can log the willDisplayCell method to see what is going on when the view appears.
With that info, the root cause can be narrowed down to a solvable problem.

Apple Lazy Loading Images within a nav controller, reverts back to placeholder images

I implemented the LazyTableImages project (link) by Apple, but in my version I used RestKit to obtain the data and my UItableviewcontroller was push onto navigation stack.
So I eschew whatever apple does in the app delegate to get the xml. I dont think that's the problem. My problem is that when you back out of the UITableviewcontroller using either the nav back button or accessing another tabbar item and coming back, the images that were loaded there previously show up, but immediately it loads the placeholder image. Basically, the opposite happens.
It's like the UITableview cached data, so when you come back it interferes with the Lazy Table Images. I need to know has anyone implemented this code where they had to back out?
EDIT:
Looks like imageDownloader is not nil the second time, which prevents the image from loading. I'm still figuring out how to bypass it. Of course, I can just take out the condition, but I dont know if that is "bad" for performance.
imageDownloadsInProgress, a mutable dictionary, still has all of its data even if you back out. It has become a different question now, how do I delete imageDownloadsInProgress if a user hits back or strays from the current view.
imageDownloadsInProgress is retained, but I added [imagesDownloadsInProgress release] in the dealloc method, however I don't think that runs.
-(void)startEventImageDownload:(WhatsonEvent *)eventRecord forIndexPath:(NSIndexPath *)indexPath
{
EventImageDownloader *imageDownloader = [imageDownloadsInProgress objectForKey:indexPath];
if(imageDownloader == nil)
{
NSLog(#"%#",eventRecord.title);
imageDownloader = [[EventImageDownloader alloc] init];
imageDownloader.eventRecord = eventRecord;
imageDownloader.indexPathInTableView = indexPath;
imageDownloader.delegate = self;
[imageDownloadsInProgress setObject:imageDownloader forKey:indexPath];
[imageDownloader startDownload];
[imageDownloader release];
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
}
}
The way I do it is to build my own cache and save the images in the user documents directory. When I call [tableView reloadData] (you are calling that, right?) it first checks for each cell if the image is there locally, otherwise it will lazily load them from the feed. Tell me if you need code for this.
The problem was that the self.imageDownloadsInProgess = [NSMutableDictionary dictionary] was placed in the ViewDidLoad method with the intention of resetting the dictionary every time. However, if you place the code within a view pushed onto a navigation controller, the ViewDidLoad only executes the first time (I'm not positive that is the case). I added the line to ViewWillAppear since it runs every time the view is placed on screen.

Trying to pass data from one view controller to another in a tab bar controller

so I am trying to create an array of recently viewed pictures. Basically I have a TableViewController that pushes a picture view in a navigation controller, located in one tab and then am trying to have a navigation controller that records what pictures have been recently viewed, lists them in a TableViewController and then also pushes that to a new view displaying the picture. I have set up both navigation controllers within the tab bar controller programmatically. Pretty much everything is set up the way I want it but I am just not able to pass the recently viewed picture information to the table view controller that will display the list.
Here is how I am trying to pass the data when a user clicks on a particular table cell:
(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
RecentsTableViewController *rtvc = [[RecentsTableViewController alloc] init];
[rtvc.recents addObject:[self dictionaryAtIndex:indexPath]];
[rtvc release];
}
recents is a mutable array that I created in the RecentsTableViewController. It is getting initialized ok, checked using NSLog, but every time a user clicks on the table cell in the other view controller, the dictionary is not being passed to the array. I also know the dictionary information is correct and set correct because it works passing on to the view that displays the image. I guess I am just confused because I can do it for a NavigationController, I am just struggling to find the answer in passing data in a TabBarController.
Sorry if this post may be long/confusing but I have really searched everywhere for an answer with no luck. I have heard about using the app delegate to store like a global variable but I am under the impression this is bad practice and I am trying to write my code correctly.
Here is the updated code for the dictionaryAtIndex method:
- (NSDictionary *)dictionaryAtIndex:(NSIndexPath *)indexPath {
NSArray *flickrPics = [FlickrFetcher photosAtPlace:self.placeID];
NSDictionary *returnedDictionary = [flickrPics objectAtIndex:indexPath.row];
return returnedDictionary;
}
indexPath is a struct which contains two properties, row and index, and cannot be used directly to access an object in an array. You probably want to do this instead:
[rtvc.recents updateRecents:[self dictionaryAtIndex:indexPath.row]];
You're creating a new RecentsTableViewController (it is not the same as the one in the tab bar), setting its "recent items", and then releasing it. Nothing else has a pointer to it, so nothing else will see its "recent" items.
There are at least three easy-ish ways to update the "recents" controller:
Access it directly from the tab bar controller (incredibly icky layering violation and breaks when you change the tab order or the navigation structure or...): [(RecentsTableViewController*)[self.tabBarController.viewControllers objectAtIndex:...]) updateRecentsWithDictionary:...]
Post a notification, which you listen for in RecentsTableViewController: [[NSNotificationCenter defaultCenter] postNotificationWithName:MyViewedPictureNotificationName object:nil userInfo:...]
Save it to NSUserDefaults. Load it from NSUserDefaults in -[RecentsTableViewController viewWillAppear:]. The advantage here is that it persists across app launches. (There is the implicit assumption that the defaults will not change while the "Recents" view is visible".)