Images not loading FIRST time a UIWebView is used - ios

My original plan was to use a split view controller to have a table view and a generic view beside it for an iPad app. Then I realized I could not use a split view controller as a child controller. So, I've been experimenting with using a table view controller instead, and I've got pretty good results so far. Essentially, I'm using the tableview's header view as a container for the various views I'll be using depending on which row is selected. The only unresolved issue is that images for a website are not displayed in the UIWebView the first time it is presented. If I tap another table row that displays a web view, it displays fine. And if I tap the original one again, it displays fine, too.
It doesn't matter which one I tap first. It's always the first one that doesn't display images. Instead of displaying the images, it displays the names of the image files.
When I tried the analogous thing with a split view controller, I didn't have this problem.
Any ideas what I should be looking for? Or is there something I need to be doing that I must not be doing?
If it matters, the page being loaded is just a thumbnail page on a remote server, created with Irfanview's HTML thumbnail page generator.
P.S. After further testing, it is really the very first presentation only that doesn't work as expected. By very first, I mean the first per app session. The UIWebview is presented as a subview of the table header. The tableview controller is presented as a popover from a button of another controller that is presented when a user taps an image. Even if I dismiss all these layers of controllers, etc., if I instantiate them again, the UIWebView displays properly.
This suggests possibly loading a dummy UIWebView before starting, but that seems kludgy.
I'm getting exactly one call to webViewDidStartLoad: and exactly one call to webViewDidFinishLoad: and no calls to webView:didFailLoadWithError:.
KLUDGE:
The kludge works. Here it is:
In my table view controller, I create a boolean property URLHasBeenRequested and set it to NO in viewDidLoad. Then I ask for a reload thus:
-(void)webViewDidFinishLoad:(UIWebView *)webView
{
if (!self.URLHasBeenRequested) {
//On first pass, try a second request.
[webView loadRequest:webView.request];
self.URLHasBeenRequested = YES;//Set this flag so a duplicate request happens only the first time.
}
}

I suspect this has more to do with the way the UITableView is loading the header and trying to allocate memory during that process, than with the UIWebView itself. For whatever reason, the UIWebView's loading seems to be getting interrupted while the rendering of the UITableView is still finishing ( just a guess ).
One thing you might try would be to go ahead and setup your table header by adding the UIWebView subview, but not calling "loadRequest" right away. You could, for example, wait until the rest of the visible cells of the table have been loaded, and then load the header view last. You can detect when that occurs like this:
-(void) tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
if([indexPath row] == ((NSIndexPath*)[[tableView indexPathsForVisibleRows] lastObject]).row){
[webView loadRequest:webView.request];
}
}
This is, admittedly, also a somewhat kludgy workaround, but at least you're not having the UIWebView load the content twice. I also haven't tested this myself, and without seeing your actual table loading code, it's hard to say if the root of the problem lies somewhere else, but let me know if this works!

Related

How to completely reset a UITableViewController while still on screen

In my app, I have a very custom UITableView. The cells are all statically defined in Interface Builder, but based on the data structure the table morphs in many various ways. For example, if some data doesn't exist, some cells (or entire sections) are not displayed, custom separator lines are added to account for missing cells, extra views are loaded into the cells, VoiceOver labels change, etc. Because all the cells are static, I set up the table layout in viewDidLoad because I always have the data available at that time. I have always presented this view controller modally, which has worked great. If the user wants to display different data in this table they have to dismiss the view controller and pick a different item to present it again, and it gets rendered appropriately in all cases.
But now I am converting this into a split view controller for iPad, so this UITableViewController never disappears off screen, but I need to set up the table again when the user taps an item. The problem is, because the table is never deallocated, its previous layout still exists when I load more data into it. It would be a lot of work (and an excellent opportunity for many difficult to reproduce bugs to pop up) to test all possible scenarios and try to reset it back to its "pre viewDidLoad state" or undo those previous layout changes if not relevant anymore, if not impossible because I don't have references to the many different custom separator lines generated.
My question is, is it possible to completely reset the table view controller every time a row is selected in the master view controller, therefore allowing it to properly set up the layout because it is not stuck with the previous layout?
I essentially need some way to completely wipe it clean as if it never did any setup, then instantiate it again to cause viewDidLoad get called (or I can move that code to its own method or viewWillAppear). I'm basically looking for a way to reset the tableView back to how it is defined in Interface Builder.
I believe this would result in a flash because the table would completely disappear then reappear in a different format, but that would be acceptable. If that can be animated that'd be nice. If this is really not recommended at all, how do you suggest I proceed to ensure the layout is always appropriate for the data it is presenting?
I was over-thinking this. There's really no need to completely throw away the table and generate a new one. It turned out to be simpler than I had thought to reset the table back to its default state. Just had to be sure to catch every possible thing that could change, including VoiceOver labels, and reset to nil or the default value. Then it can run through the reset code then the layout code every time the data changes and render an appropriate layout. The most difficult part was to remove the custom separator lines, which I solved by adding each one to an array when it's created, then index through it and remove each one from its superview then remove the Autolayout constraints associated with it. One can wrap all of this into a UIView animation block to get a nice fading effect. It's working quite well.

Show activity indicator while changing views (Segue) and load data for next view (API)

Umm I really need some help
I have a search view, when user clicks the cell (basically a game), the view changes to a Game View. Atm I was not using any async stuff so while the game view loads, the app kinda hangs for 2-3 secs on the search view after clicking the cell (since the viewDidLoad method of game view loads all data from web API)
What I wanted to do was when a user clicks game in searchView, the gameView instantly shows up with just a blank view and a Activity Indicator meanwhile the data is fetched from API.
Once data is fetched, the gameView (which is a tableView btw) reloads the data and shows it accordingly from the fetched data.
If it makes any diff. I am using static cells for gameView and thus not using cellAtRowIndex code.
I have attached images for some of my methods I tried to use as per my searching on SO for AFNetworking, async etc etc.
The table IB elements are filled from data in a separate function showGame, I am calling this function in rowsForSection since table will be reloaded after block completion. Excuse me but I wasnt sure what would be the best way to reload table or rather show the data.
Atm my app is showing me my default static content which I created in interface builder (and with only 1 row). AKA the table is not being updated. I used breakpoints to check and yeah the showGame is getting correct data, passing correct data to IB elements. Even the execution is reaching the point in code to return 5/6 rows BUT the actual app/view is not updating properly.
For those who want pastebin text code, here it is:
.m file where async code to fetch game from API is
http://pastebin.com/p2FETbku
.m file of my gameView class/tableView which shows the table view and data, this file also has the showGame method to show the game data fetched from above class
http://pastebin.com/cWtSKPw0
Please excuse my code or any small mistakes, I am still in learning phase and just started 3 weeks ago.
The static cells were working fine before I was using async method, somehow its getting messed up during/after that reloaddata
One point that I think you are missing is that execution of this statement
GDgameViewController *gdv = [[GDgameViewController alloc] init];
doesn't return the tableViewController hence the tableView that is currently presented.
So in your function +(void) fetchGame:(NSString *)gid what you are reloading is the tableView that is not presented.
To help you further I have created a project for you which use a bit different approach to do your work but it does it optimally. You can find project here. If you don't know how to use gitHub, don't worry just download the zip file. Please make this count as

Embedding UIViewController view inside another UIViewController

In my app, I have a login-register-forgotpasword wireframe. Each step of the process is implemented in a separate UIViewController, all of them inside the same Storyboard, and transitions between each controller are animated. So far so good.
Now we've changed the design, so all views have the same background elements and a header (not exactly a UINavigationBar), and I don't like the feel of the animation to a view that always looks to be actually the same, but just showing a different form. So I'm considering different approaches to, instead of pushing whole controllers, just showing/hiding its views, but staying in the same controller.-
1) My first try was instantiating the controller which view I want to show, and add it to the current view. Something like.-
- (IBAction)btnRegisterPressed:(id)sender {
_viewHome.hidden = YES;
RegisterController *controller = [self.storyboard instantiateViewControllerWithIdentifier:#"registerNewUser"];
[self.view addSubview:controller.view];
}
This one would be perfect, as I'm using static UITableViews for my forms, so as far as I know I'd need a UITableViewController for each one. But it seems IBOutlets and IBActions got broken. Is this approach possible in some way? If so, it's considered a bad practice?
2) My second option is just creating all the views inside one controller, and properly showing/hiding them. This would one be hard to maintain, and chances are I'd have to forget about static UITableViews.
Could anyone give me some advice of which option would be better, or point me to any other possible approach I'm missing?
Your option #1 is not appropriate as written but close to an approach you should probably consider. You can add nest the views of different UIViewControllers however when you do so you should use the methods described in Managing Child View Controllers in a Custom Container so that the parent controller correctly manages its child controllers.
You can use as many tableviews as you want for the same controller. The delegate methods contains the object that fired the method itself.
Following this approach (which i wouldn't recommend) you can probably tag your tableviews and then do:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;
{
if (tableView.tag == 1) {
doStuff;
} else if (tableView.tag == 2) {
doOtherStuff;
}
}
Well, personally, I feel we should keep things simple and straight forward for end user to quickly gel up with your app. Animations plays an important role in this. Tapping on cell of a tableview pushes another table view and there are different UI widgets like chevron that indicates the transition and user is not surprised.
Showing everything on single screen and hiding/unhiding them based on user's action is fine as long as you have proper animation. For instance, you might have seen TableView sections collapsing/expanding on tap.
You need to make a trade off and see what best suits based on your application. Whatever you decide my suggestion would be to add nice animations instead of simple hide/unhide.

UIPageViewController cache and swap view hierarchy for pages with similar content

My UIViewController has a UIPageViewController embedded in it. The pager can contain anywhere from 5-38 pages (each page is an instance of a UIViewController subclass) depending on the situation. I've noticed that depending how complicated I make the UI elements on each page, the app slows down considerably, and is very slow when swiping to go to the next page.
Here's the thing - the view on each page is identical, except for the values of a few UITextViews. I am building the view in each page's viewDidLoad method each time viewControllerAtIndex is called for a new page. I feel like there must be a way to re-use the same view for each page, and just swap the text values that are supposed to be different. Can anyone describe a strategy to do this?
Like I said, every page has an identical view hierarchy except the values for some of the text, so I'm really just looking for a way to maintain one basic view controller per page, but cache the view hierarchy to be re-used on each page, and swap out some simple text values depending on page number.
EDIT
Something I forgot to mention in the original post is that I'm building my view programmatically because the number of elements on the page is dependent on choices the user made on previous screens. i.e. there may be 5 TextViews or 10 TextViews, etc. depending on what the user selected on a previous screen (before coming to the UIPageViewController). With that said, I do not believe an xib based approach will work because the initial layout is dynamic.
Thanks in advance!
Have you tried using a xib based view, which you could pull out of the xib once, and keep a strong reference to it. In your controller's viewDidLoad, you could set its view to this xib base view, then populate the text view(s) with the proper text.

slow pushing when a tableview element is selected

In my app,i am using tableviews, populated through XML parsing, i get my XML from an ftp, the XML contains other xml urls for the next views and images urls and text, the problem is when an element of the tableview is selected it pushes to another view, But load for a very long time.
I run my app into the simulator, i never tested it on a device.
does anybody knows what may be the problem?
Is it a connection issue?
will it work fine on a device?
I would suggest testing your code on a device and not making assumptions about the peformace. I assume you are going to the server and ask for those images to display in the pushed view. In which case you should load the view with image placeholders and spin off the downloading images in a secondary thread (good habit for slow loading as to not block the UI) and update the image placeholders as the image data become available.
I will gladly update this answer if you need more specific help.
If you are loading a tableview in your pushed view, the hang will happen on the view before that pushed view (that is very terrible UX). So move the logic of the code that take a while into the subclass of the "pushed view", you can implement something like a spinner (I use MBProgressHud) while you crunch you data. This way you will get the view loaded before the lag begins.
Now the flow to improve UX can be as follows:
1. In the - (void)viewDidLoadof the pushed view you can hide the tableview (or not).
[self.myTable setHidden:YES];
2. In the -(void)viewDidAppear:(BOOL)animated of the pushed view throw up a spinner for the user to know you are doing work (work here being going to the server to look for the images from the urls) and do the work.
//your code here will vary
3. When you know that all your data is downloaded just load the table, hide the spinner and show the table. You can do this at the end of the -(void)viewDidAppear:(BOOL)animated method.
[MBProgressHUD hideHUDForView:self.view animated:YES];
[myTable reloadData];
I recommend sending all the expensive work into a secondary thread, because a user would hate to decide not to wait and have a blocked back button in the navigation bar. But for to answer you original question you pushed view should load immediately, but then wait to get the data.

Resources