UIWebView delegate not working the second time - iphone

I have an rss feed which returns a json containing a title, short text and full-size text. The title and part of he short text is displayed in a TableView.
I have 3 controllers with 3 views.
View 1 displays a TableView containing articles from the json( the title and short text ), each article has a "Read more" button, in the IBAction of the button, i push a viewcontroller(2) into the navigation controller, the controller has a UIWebView(second View), which loads a html string. The string contains links, when the user taps a link, i want to present a modal view controller with another WebView(3), this time using the request(keep in mind that both WebViews have separate controllers and nibs).
The delegates are set properly on both of them, i tried both from code and using visual tools. In the delegate method shouldStartLoadWithRequest in the first WebView i check to see if the request parameter contains "http" or "www", because only then i want to call the second WebView. The second WebView works well the first time, the view is presented modally fine, but when i dismiss it and tap the same link or another, the shouldStartLoadWithRequest delegate method is not called, but the link loads in the same WebView(the first one).
I googled this but didn't find a similar case to mine, or a solution. I don't have that much experience with ios app development, but a colleague is pretty good, and we didn't manage to find a solution to this problem. We took the code apart and analyzed it, but we didn't find a potential source for this problem, and he has a separate project where he has the same problem, only he uses only one controller and one WebView (our controllers and nibs where created independantly). We were pretty thorough with the research and the code examination

It seems that the solution was quite easy. The first WebView, aparently looses the connection to it's delegate, so, in the second WebView controller just add
- (void)viewWillDisappear:(BOOL)animated {
[self.parentViewController viewWillAppear:YES];
}
and in the first controller of the first WebView:
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
webView_.delegate = self;
}
I hope this helps someone

Related

Connect action of a XIB loaded from a custom class to a storyboard

I'm trying to create a reusable component to display some photo collection.
The basic flow is the following :
First view : View. It contains my so called library, designed programmatically and loaded from storyboard by assigning a custom class
I take a photo in a modal view, openend from the 'take picture' button
Once the photo is saved on disk, I ask PhotoLib to create a new PhotoCell from the photo path
I would like my PhotoCell to be touch enabled so when I tap it, it opens the second view in a modal way, but from what I read I cannot do this from my PhotoCell or the UIImageView inside (not a controller).
So how can I do ? View is embedded in a NavigationController, even if not shown in the screenshots below.
Thank you !
If you create Photocell in photolib, then photolib should implementing delegate methods from photocell. But photolib itself is not rootviewcontroller, so it should declare delegate methods itself, and the containing view should implement it.
Basically you pass Photocell from itself to Photolib (which implements delegate method
-(void) openPhotoCell:(Photocell*)cell
{
[self.delegate openPhotocell:(Photocell*)cell];
}
, then it passes it to View, which in its turn opens it.
It may seem like pulling a tooth from an ear, but actually it's quite working and if you write good self-explanatory code, it's not a problem. I'm currently working on some big project with tens views and controllers and it works pretty good and nobody has problem with that.
If you have more layers, then maybe you should look into NSNotification.
Hope it helped, I'd be glad to explain more.
UPD:
Links:
about delegates in cocoa fundamentals guide
delegation pattern in wikipedia

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.

Xcode 4.2 & Storyboard, how to pass data between views? Existing code error

I'm trying to learn how to pass data between views. Say set a label in the second view from text entered into a text field on the first view. I basically have tried making a string in the second view and then when switching from the first view to the second I set a string in the second view. Then when the second view loads its sets the text of a label to the same string. I NSLog right before and after the transition, before its fine, but when the second view loads it string gets erased. I'm not sure why this isn't working. Here is my project: http://www.mediafire.com/?83s88z5d06hhqb5
Thanks!
-Shredder2794
From my book (http://www.apeth.com/iOSBook/ch19.html#_storyboards):
Before a segue is performed, the source view controller is sent prepareForSegue:sender:. The view controller can work out what segue is being triggered by examining the segue’s identifier and destinationViewController properties, and the sender is the interface object that was tapped to trigger to the segue (or, if performSegueWithIdentifier:sender: was called in code, whatever object was supplied as the sender: argument). This is the moment when the source view controller and the destination view controller meet; the source view controller can thus perform configurations on the destination view controller, hand it data, and so forth.
(Of course another solution is "don't use a storyboard". Then the first view controller creates the second and can hand it data then and there.)
The reverse problem is much trickier; look at the Utility Application template for an example of how to use the delegate pattern.
StoryBoards are ready made things where you can reduce a lot of code you write.So consider controller A & B on storyboard.
Now for passing data From A to B you can connect them with a segue name its identifier and then you can use delegate methods in A as:
//This method gets called before transition from A to B.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"THE IDENTIFIER YOU NAMED"])
{
id *objectOfController_B = [segue destinationViewController];.
objectOfController_B.lblTextDisplayOfA = //Something...
}
}
Now You can Explicitly transition it by using button in controller A.
- (IBAction)buttonPressed:(id)sender
{
[self performSegueWithIdentifier:#"THE IDENTIFIER YOU NAMED" sender:sender];
}
So I guess you can try experimenting on this and you will get it how and when transition occurs with segue.
Hope this helps.
There appear to be more than a few things to explain. I think working through a few tutorials will give you the answers you need. See http://www.raywenderlich.com/tutorials
i've asked more or less the same question a few weeks/month ago. there were some very good answers, especially the one from zoul, who built a demo project that will show you how to create a factory pattern application that will provide the views with the needed objects.
my question can be found here: iOS: Initialise object at start of application for all controllers to use and have a look at the answer from 'zoul'. it got me through this problem =)
good luck trying it out =)
sebastian
I spent "countless hours" my self trying to find a way to pass data and understand delegates with no comprehension and very little success. This video did something that all the other references I checked didn't do : keep it as simple as possible while clearly showing what was needed. Thank you so very much Mr Rob Smythe. http://www.youtube.com/watch?v=XZWT0IV8FrI
Have a look at this: http://iosdevelopertips.com/objective-c/the-basics-of-protocols-and-delegates.html
Delegate pattern is a common way to achieve what you are trying to do.

NavigationViewController with WebView-containing View Controller

I have a problem with a navigation controller.
First of all, there is a navigationviewcontroller.
Also, there is A webviewcontroller-containing view controller, meaning that webview controller is loaded inside WEBcontroller.m
I made that when the WEBcontroller is loaded, it automatically loads google.com. The function is in the -viewDidLoad()
First, when the app is launched, navigationview loads WEBcontroller.m, then WEBcontroller loads google.com as intended.
Then, when I click any link in the google.com, navigationview pushes a new view with
[self.navController pushViewController:newWebController animated:YES];
[newWebController gotoUrl:[request.URL absoluteString]];
It, of course, works. The newly loaded(and alloc) WEBController.m loads gmail.com by calling "gotoUrl" function.
And, I click another links to go "gmail.com/help"
So,
google.com -> gmail.com -> gmail.com/help
Then, I close the app, and play some games... it makes iPhone free memory.
Launching the app again, the "gmail.com/help" webpage is shown. Then, I click the [Back] button which is at the navigationBar which calls [popViewController].
Then, the navigation controller properly go back to preceding WEBController.m which was showing "gmail.com" page.
BUT!! there is a problem. Because the memory was 'dealloc' by iPhone, the WEBController is loaded AGAIN with "google.com" page, not "gmail.com" page.
I've searched this problem but I couldn't get any.
Really thank you for reading and giving some interests to my problem.
I'm confused. You are using a UIWebView? If so, why don't you just let it handle the links/navigation? Why are you creating (and pushing) a new UIWebView for every link click? Technically speaking, a view controller needs to be able to handle being freed and restored from memory by IOS. This is done in viewDidLoad and viewDidUnload. But I don't think that's what you want/need here.

How to actually implement the paging UIScrollView from Apple's example?

I found an example of a paging UIScrollView in Apple's developer docs and it's just what I want for my application. I have a main view with a "Help" button that I want to present a view that users can page through to see all the help topics. The sample is at:
https://developer.apple.com/library/ios/#documentation/WindowsViews/Conceptual/UIScrollView_pg/ScrollViewPagingMode/ScrollViewPagingMode.html%23//apple_ref/doc/uid/TP40008179-CH3-SW1
And it works as advertised, but, as with so many of Apple's examples, they do all kinds of code right in the AppDelegate. I have my own AppDelegate, but then I have a NavigationController with a RootView that has this "Help" button on it. I can't seem to get the example code integrated into my own and have it work. I'm pretty sure I can't put the code they have in their AppDelegate in my own, but how to set it up eludes me.
Can someone point me to an example where they do what I'm talking about?
EDIT: I was able to create a new project and get it to work like Apple's by moving all the AppDelegate methods into the UIViewController that the template supplied and creating a new ContentPage UIViewController to hold the content. It can scroll from page to page, so I think I can insert this code into my other project ok.
I replaced the applicationDidFinishLaunching with an equivalent viewDidLoad and got rid of the AppDelegate stuff dealing with window and such. Then I changed Apple's initWithPageNumber: method to refer to my help pages rather than just creating instances of their generic views.
- (id)initWithPageNumber:(int)page {
NSLog(#"--initWithPageNumber:ContentPage");
if (self = [super initWithNibName:[NSString stringWithFormat:#"HelpPage%d", page] bundle:nil]) {
pageNumber = page;
}
return self;
}
Thanks for the help - sometimes it's good just to have someone tell you it can be done to keep going!
In the sample code, the applicationDidFinishLaunching: method sets up the UIScrollView (and a UIPageControl, but let's ignore that for now) and then "loads" the first two pages. "Loading" a page consists of loading that page's view controller into memory and caching it. It then adds that controller's view as a subview to the UIScrollView offset with an appropriate x based on what "page number" it is. You should be able to do all that in your viewDidLoad.
If you have a blank view when your view controller's view is shown, then you haven't added the subview correctly to your UIScrollView. Depending on what exactly you changed, that could be some combination of (a) not getting the x offset correct, (b) not setting the frame correctly, or (c) not adding it as a subview correctly.
If you post some of your own code we might be able to shed some light on the problem.