I am relatively new to Objective C/iPhone Development.
I am developing a simple app with two views using a Navigation controller.
The first view contains several buttons a and the second view contains a uiwebview.
The idea is that when a button is pressed it switches to the second view and the web view loads a local html file. Which html file is loaded depends on which button was pressed.
At the moment, the view successfully changes and I can specify a html file to load in the viewdidload method of the webviewcontroller. However this obviosly means that every button triggers the same html file.
Therefore I want to create a NSMutable string which can be accessed from both views(i.e set to a particular filename in the first view and then retrieved when the seoncd view loads).
I have searched the internet for hours and trued to use global variables, the singleton method and accessing a variable in the appdelegate. However no matter which method I try to implement, the uiwebview always displays the same html file(which is the first file alphabetically)
Thanks for any help given. I greatly appreciate any suggestions.
You don't have to use an NSMutableString for this.
Add a property url, just a normal NSString, to the second view controller. Before pushing the second view controller onto the navigation stack, you set that url property to the local url you want to load.
Then, in the second view controller class, implement:
- (void)setUrl:(NSString *)newUrl {
if(url != newUrl){
[url release];
url = [newUrl retain];
// [webView load.... load the new url in the webView.
}
}
This may be beyond you current knowledge, it contains some vital Objective-C code, like properties, setters and memory management, but just test it out and see what you can do to make it work.
EDIT You don't even need this property. Just implement a method like loadUrl: in the webViewController.
- (void)loadUrl:(NSString *)url {
// [webView load.... load the URL in the webView
}
Then, before pushing the webViewController, call this method with the URL you want to load. Avoids a property and gets rid of all the additional memory management code.
if (self.myWebViewController == nil) {
//initialise
webViewController *viewWeb = [[webViewController alloc] initWithNibName:#"webViewController" bundle:[NSBundle mainBundle]];
self.myWebViewController = viewWeb;
[viewWeb release];
}
[self.myWebViewController loadUrl:#"some-local-url"];
[self.navigationController pushViewController:self.myWebViewController animated:YES];
As a note, a good practice is to capitalize each classname. Now you have a class webViewController, but in Cocoa it's common to name the class WebViewController, then you can have a variable called webViewController. Also, you can clearly see whether you are dealing with object (variable) or just a class.
EDIT Updated
So I forgot about the fact that the webView will not have been loaded until your view gets loaded. To solve it you'll have to reintroduce the property I talked about earlier. This time some adjustments are made. In your webViewController class, add the following code:
- (void)viewDidLoad {
// viewDidLoad gets automatically called once the view has loaded. Here we want to load the webView.
[self loadWebView];
}
- (void)setUrl:(NSString *)newUrl {
if(url != newUrl){
[url release];
url = [newUrl retain];
// Update the webView with the newly set URL. This will not do anything if the view hasn't been loaded, since webView will still be nil. That's why we call loadWebView again when the view gets loaded, in viewDidLoad.
[self loadWebView];
}
}
- (void)loadWebView {
// Here you'll have to load the url. You can access it using `self.url`.
//[webView load...
}
Now, in the first view controller, update [self.myWebViewController loadUrl:#"some-local-url"]; to self.myWebViewController.url = #"some-local-url";.
I assume that the web view controller is initialised from the navigation controller, so why don't you just pass it that string when it is initialised. Then when a button is pressed it uses the correct string to instigate the web view controller.
Another idea, one I think you've tried, but I am not sure, is that the web view controller has a static method and value. When you press the button it then calls the static method on the web view controller. Then when web view controller is loading up, it calls a get static method of the value.
Related
I have a view controller that I need to refresh it self so, I basically reload it with the following code.
-(void)check{
GameController*myNewVC = [[GameController alloc] init];
[self presentModalViewController:myNewVC animated:NO];
}
I can call the method above in gamecontroller and it works fine, but in a button sub class I use the method below and it doesn't work because nothing happens.
.h
#interface CellButton : UIButton {
}
.m
GameController*myNewVC = [[GameController alloc] init];
[myNewVC check];
What can I do to get this working?
I have a view controller that I need to refresh it self so, I basically reload it
Don't do that. Your view controller isn't refreshing itself, it's replacing itself, and it's hard to think of a reason that it should need to do that.
Put the code the loads the data in a separate method, and call that method on the existing view controller instead of creating a whole new view controller. For example, many view controllers that manage a UITableView will call the table's -reloadData method to tell the table to discard any cells that are currently visible and request new ones. No matter what kind of view(s) your view controller manages, you can do something similar.
I can call the method above in gamecontroller and it works fine, but
in a button sub class I use the method below and it doesn't work
because nothing happens.
That's most likely because you say you're using the code in a UIButton subclass, and the code says:
[self presentModalViewController:myNewVC animated:NO];
So, the button is telling itself to present the view controller. However, UIButton doesn't have a presentModalViewController:animated: method. I'm surprised that "nothing happens" -- I'd expect an exception due to the unimplemented method. It should work fine if you replace self above with a pointer to your view controller. Or, much better, put the code in an IBAction method in the view controller, set the buttons action to that method, and its target to the view controller.
(from your comment...)
There is a function in the button class that will dictate weather or
not the view controller will refresh it self.
That sounds like a poor plan -- in a well designed MVC application, logic that controls whether the view controller will refresh belongs in the view controller. Have the view controller enable/disable or show/hide the button based on whatever conditions control the refreshing behavior.
I am developing one application.In that first page having one UIWebView.And i create a property for that one.And i created a object for that class in another class and give the html data for that webview like as below.But it didn't show the data on UIWebView.Please tell me how to do that one.
ViewController *view=[[ViewController alloc]init];
NSString *result1 = [[NSString alloc] initWithData:_data encoding:NSUTF8StringEncoding];
NSString *htmldata=[NSString stringWithFormat:#"<html>%#</html>",result1];
[view.web loadHTMLString:htmldata baseURL:nil];
This result1 string contain html data without html tag.That' why iam adding that tags.
Hope you had called pushViewController or popViewController to present the view or added the view object's view as a subview of the current view. Then make sure that the webview is allocated, given a frame and added as a subview of ViewController. You did not used the method initWithNibName:, therefor, if the webview is created from xib, it will not take.
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.
So I have this UINavigationController, I'm on the first moving to the next view, than I want to hit the 'back' button and to go back to the first view with the data that I saved into 'strAddress' on the second view. I want to present the data on the first view on 'lblShowStr.text'.
how can I manage to do that? I've searched all the web, found some people that wrote, but couldn't understand what they have been told there.
Thanks!
You can get a reference to the previous viewController in your navigation stack by saying:
NSArray *viewControllers = self.navigationController.viewControllers;
MyViewControllerClass *previousController = [viewControllers objectAtIndex:[viewControllers count] - 2];
You can then set a property on the 'previous' view controller to store your text, or even set the label outlet's text directly like this:
previousController.lblShowStr.text = self.strAddress;
It's not the best way to do it (the best way involves creating a custom delegate protocol or using NSNotificationCenter) but it's the easiest way.
In your first view controller you might have an NSString property called strAddress.
and you put that string into lblShowStr.text every time the view appears.
In your second view controller you might have a property pointing to an instance of view controller one. When you instantiate your second view controller you could assign the property on it to the first view controller.
secondViewController.firstViewController = self;
or
[secondViewController setFirstViewController:self];
Then when the user presses the back button viewDidAppear would get called for the first view and update the string.
I am assuming you don't want to store this data anywhere else e.g. in your model or nsuserdefaults etc.
I'm getting a little confused about when I should use a NIB file and when I should use code.
Here's my problem :
I have a navigation based application with a RootController and its NIB file.
The RootController's NIB file contains a TableView.
When I click on a cell I initialize a new connection with a request to load content.
When the connection has finished loading I create a new postViewController (custom) from a NIB file and I push it on the navigationController viewController stack like that :
PostViewController *postViewController = [[PostViewController alloc] initWithNibName:#"PostViewController" bundle:[NSBundle mainBundle]];
[postViewController.webView setDelegate:self];
postViewController.postContent = [[postsData objectForKey:#"post"] objectForKey:#"content"];
[self.navigationController pushViewController:postViewController animated:YES];
[PostViewController release];
Then, as you can see I try to set the rootViewController as the delegate for the webView in order to be able to intercept a click on a link and push a new ViewController on the stack. I need that new view to have the navigation bar with the back button.
Problem : looks like the setDelegate isn't working because the webView:shouldStartLoadWithRequest:navigationType never gets called !
I guess I should set the delegate in the NIB file but I have no idea how. The NIB file from PostViewController doesn't know about the RootViewController...
Here are screenshots of the NIB files :
If you need more detail just ask me.
Thanks a lot...for helping me not banging my head for another day :)
Make sure postViewController.webView isn't nil when you call setDelegate: on it. Also make sure it isn't being called anywhere else, and make sure the delegate outlet isn't connected to anything in your NIB.
A couple other comments:
It's a bit unusual to use your root view controller as the webview's delegate in a case like this. It should work, but it might make more sense to move those delegate methods down into your PostViewController. From there you can still intercept the link clicks and call [self.navigationController pushViewController:animated:].
[PostViewController release] is releasing the class object. Not a good idea. Instead you should be calling [postViewController release].
[postViewController.webView setDelegate:self]; sets the delegate for the current Controller not postViewController. Try:
[postViewController.webView setDelegate:postViewController];
And you can put this line below pushViewController:animated: then the webView should already exist.