I have table view navigation controller and the data is loaded from url. I created a thread in order to keep the user in table view in Second Level view controller. The problem is that the second level view controller loads url each time the view is opened, even if it has already been opened before. How can I make it load the data only once?
When you load the data within viewWillAppear: (or viewDidAppear:) then move the code to viewDidLoad.
You can also set a timestamp within your loading procedure.
self.lastUpdateDate = [NSDate date];
When the program enters the loading code, you can check after the last update date:
if([[NSDate date] timeIntervalSinceDate:self.lastUpdateDate] <= 300) {
//if the lastupdate was no longer then 5 minutes ago, don't update
}
else {
// do some web loading stuff
}
Related
I am fetch data from the server to display in a picker view. If the API has not returned i display Loading... in the picker view.
Here is my use case:
API Call made to the server
User clicks button to show picker view.
Picker View is shown and displays the text LOADING...
API call returns. i call [self.seasonEpiPickerView reloadAllComponents];
Nothing happens. Basically since the picker view is already loaded and being shown on the screen it is not updating its data. How can i update the data of a picker view that is already being displayed?
You need
In .h file
#interface ViewController : UIViewController
<UIPickerViewDataSource,UIPickerViewDelegate>
In .m file set delegate and datasource
yourpicker.delegate = self;
yourpicker.datasource = self;
may this help you
Solved it: My response from the server was not on the main thread. I had put a test thread to delay UI updates for testing. I removed the test code and executed the updateUI lines from the main thread and everything works with the same above code.
I have a view controller that has a web view. What I'm wanting to know is if it is possible to keep the current page info available so that if the user goes elsewhere in my app and comes back to the web view, it will show the last page the user was on and not always load the url defined in viewDidLoad.
Save your current url in view will disappear to lets say NSUserDefaults like:
-(void)viewWillDisappear:(BOOL)animated{
NSString *currentURL = currentWebView.request.URL.absoluteString;
//save to NSUserDefaults
}
then in viewWillAppear get this url and load webview
I'm using a UIPageViewController for displaying images, and I download the images asynchronously and throw them into the gallery when they're ready (but you're still able to go to the page in the gallery where the image will be).
My issue is that, when the image does load, I need to supply it for the view controller it corresponds to. Right now, I add it to an NSCache instance, and when UIPageViewController's data source method viewControllerAfterViewController: is called, I check if the image for the view controller being requested has already been downloaded (is in the cache) and if it is, I call initWithImage: on the specific view controller, passing the downloaded image.
My issue is with when viewControllerAfterViewController: is called and the image hasn't been downloaded yet. Right now I just load the view controller without the image, and when the imageDidFinishDownloading: delegate method is called, I try to supply the view controller with the image.
However, it seems that even though UIPageViewController asks for the next view controller the previous gets displayed (i.e.: when I get to the 3rd page, it requests the 4th page's view controller) I'm not able to access this requested view controller that I hand off.
If I access pageViewController.viewControllers, the count of the NSArray is never more than 1. So even though it seems like it should have 2 (the currently showing view controller and the next one that it requested), it only ever has one, the currently visible view controller.
The problem is that I need the other one. The image finishes downloading, and I really only have two options.
Give it to my NSCache, so when the init method is called for the view controller, it is handed off.
In the event that it has already been inited, hand it off to the view controller that's already been inited.
But 1 doesn't always work, as sometimes the init method is called when there's no image yet (it hasn't finished downloading), so we'd sometimes depend on 2, but pageViewController.viewControllers only holds the current visible view controller.
So there's basically this third situation where the view controller has already been inited without an image, and the image finishes downloading and I want to assign it to that view controller, but I'm currently on the one before it, and my only reference to the view controllers in the UIPageViewController is on the current visible one.
How do I handle 3? I need to assign something to a view controller but I can't find any way to access that view controller.
I'd use something like a future from PMConcurrency to do this. A future is just an object that promises to return your image as soon as it's available. So by giving the future to your view controller instead of the image, you don't need to worry about when the image arrives.
Your view controller's initializer would look something like this:
- initWithImageFuture:(PMFuture *)future
{
if (self = [super init]) {
[[future onMainThread] onComplete:^(id result, NSError *error) {
if (!error) {
_image = result;
}
}];
}
return self;
}
By adding an onComplete block to the future, the view controller will receive the image as soon as it's available (immediately if the image is already downloaded). Futures run in the background by default, so you'd use onMainThread to have the result returned on the main thread.
So instead of populating your cache with images, you'd populate it with image futures. There are several ways to create futures, but the block-based one is pretty simple:
PMFuture *imageFuture = [PMFuture futureWithBlock:^id{
UIImage *image = ...; // Your image fetching code here
return image;
}];
[myCache setObject:imageFuture forKey:myImageKey];
I have a similar sort of function but I load the image in the view controller that is displaying the image (the VC handed to the UIPageViewController). In your context I guess I'd had the image ID info and the NSCache object to the VC. That isn't what you want to do of course, but just something to think about.
Originally I tried to handle image loading in the parent like you are. As I recall, I kept a weak pointer to the target view controller. When the image arrived if the VC was still alive I could set the UIImageView property.
I have a UIPageViewController that contains view controllers that are instantiated from data fetched over the network. When the user is paging and gets within a few pages of the end, I kick off a new network operation and put the resulting new view controllers onto the end of my UIPageViewControllerDataSource's mutable array of view controllers. In general this all works pretty well.
But if the network is slow (or the user is fast), it's possible to reach the final page before the network operation has completed. Since pageViewController:viewControllerAfterViewController: gets called when the user reaches the last page, it has already returned nil ("to indicate that there is no next view controller").
Is there a good way to force the UIPageViewController to call pageViewController:viewControllerAfterViewController: again (so that when the network operation completes and there are now new pages, I can force this call so the user can page to the new pages)? Or is there a workaround (maybe preventing the user from paging to the last page, but still showing the bounce animation)?
See here: Refresh UIPageViewController - reorder pages and add new pages
When you get your data back from the network, call setViewControllers: direction: animated: completion: and set the current view to the current one again. That forces it to re-call pageViewController:viewControllerAfterViewController:
ssloan's answer was part of the key, but in my case simply calling setViewControllers:direction:animated:completion: in the network operation completion block wasn't enough (due to some edge cases).
My solution was to store the most recent view controller for which pageViewController:viewControllerAfterViewController: returned nil (I called it endPage) and to create a method...
- (void)fixAndResetEndPage {
if (self.endPage && self.endPage == self.currentPage) {
[self.pageController setViewControllers:#[self.currentPage] direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:nil];
self.endPage = nil;
}
}
...which I called whenever my NSMutableArray of content controllers was mutated (e.g., network operation completion) and in pageViewController:didFinishAnimating:previousViewControllers:transitionCompleted: whenever completed was YES.
I have a xib file with 2 date pickers in it a few labels and a segment control. I was loading the xib's associated view controller into a popover and it was extremely slow loading only on the first time you open it(~5 seconds to load). So I put a reference to the view of the date picker controller outside of the open popover method and in its parents init method and sure enough loading the parent view controller is slow on load now and the open popover method goes fast. So I came to the conclusion it is the xib file connecting outlets/loading that is slow.
Removing everything from the date picker controller's viewDidLoad and init methods did not help.
Removing 1 date picker improved the speed considerably and adding more date pickers didn't appear to effect the speed at all.
Happens on both ios 5.1 and 6.0.
I can get around it by just dispatching another thread to load it on start but does anyway have a clue why this would be slow? I have seen other apps use multiple date pickers in one view with no trouble.
Solution was just to load it when the app starts up and keep reusing the same controller it will cause a small start up delay but its better than unresponsive controls, so in your app delegate call something like this:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
//only use this one controller, allocing another one will be just as slow
self.datecontrollerwith2pickers = [[datecontrollerwith2pickers alloc] init];
//forces xib to load
self.datecontrollerwith2pickers.view;
}
The slow loading is most definitely a bug in Apple's code somewhere.