I have a big problem with the UIWebView in iPhone SDK.
I have a TabBarApplication with one WebView on each Tab (except the first).
Because it takes quiet a while to load the views I'd like to show an activity indicator.
Here is the code I'm using in order to do that:
-(void)webViewDidStartLoad:(UIWebView *) portal {
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
}
-(void)webViewDidFinishLoad:(UIWebView *) portal{
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
}
It doesn't work this way... My WebView in the first tab is called "portal", that's why I entered it above, but the same problem exists if I use WebView.
Any ideas? Can't be true that this is soooo difficult.
I'm searching for a clue quiete a while now and found nothing which helped me to build such a (think it's easy) activityindicator.
Thanks a lot for your effort!
Greets from Germany
Tobias
I typically use a UIActivityIndicatorView.
If you have a Navigation Controller on top of the web view it works perfectly:
-(void)webViewDidStartLoad:(UIWebView *) portal {
UIActivityIndicatorView *actInd = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite];
UIBarButtonItem *actItem = [[UIBarButtonItem alloc] initWithCustomView:actInd];
self.navigationItem.rightBarButtonItem = actItem;
[actInd startAnimating];
[actInd release];
[actItem release];
}
To get rid of the indicator:
-(void)webViewDidFinishLoad:(UIWebView *) portal{
self.navigationItem.rightBarButtonItem = nil;
}
If you aren't using a Navigation Controller then I would simply use either the larger style, UIActivityIndicatorViewStyleWhiteLarge OR place the view on top of a dark semi-transparent view on the screen and then remove them on load finish.
Related
I have created a mapview, with page curl feature in it. The mapview is having a toolbar, with a page curl button. On clicking the button, the mapview page curls. Here is the code.
-(IBAction) onPageCurl:(id)sender{
pageCurlViewController = [[MyMapViewPageCurlViewController alloc] initWithNibName:#"MyMapViewPageCurlViewController" bundle:nil];
[pageCurlViewController.navigationController.toolbar setHidden:NO];
[pageCurlViewController setModalTransitionStyle:UIModalTransitionStylePartialCurl];
[pageCurlViewController setToolbarItems:toolbarItems];
[[self navigationController] presentModalViewController:pageCurlViewController animated:YES];
[pageCurlViewController getMapView:&mapView];
[pageCurlViewController release];
}
As the mapview page curls, I have a new viewcontroller underneath it. The new view controller has a segmented control with 3 segments.
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
[self.navigationController.toolbar setHidden:NO];
[directionSearchSegmentedControl addTarget:self action:#selector(segmentAction:) forControlEvents:UIControlEventValueChanged];
directionSearchSegmentedControl.selectedSegmentIndex = selectedIndex;
UIBarButtonItem *directionSearchSegmentedControlButton = [[[UIBarButtonItem alloc] initWithCustomView:directionSearchSegmentedControl] autorelease];
NSArray *toolbarItems = [NSArray arrayWithObjects: navigatorButton , flexibleSpace, directionSearchSegmentedControlButton, flexibleSpace, pageCurlButton, nil];
[self setToolbarItems:toolbarItems];
[self.navigationController.toolbar setHidden:NO];
}
I have standard/satellite/hybrid view of the map on clicking each segments in the segmented controller.
- (void)segmentAction:(id)sender
{
[self dismissModalViewControllerAnimated:YES];
if([sender selectedSegmentIndex] == 0){
selectedIndex = 0;
pageCurlMapView.mapType = MKMapTypeStandard;
}
if([sender selectedSegmentIndex] == 1){
selectedIndex = 1;
pageCurlMapView.mapType = MKMapTypeSatellite;
}
if([sender selectedSegmentIndex] == 2){
selectedIndex = 2;
pageCurlMapView.mapType = MKMapTypeHybrid;
}
if([sender selectedSegmentIndex] == 2){
}
directionSearchSegmentedControl.momentary = YES;
selectedIndex = directionSearchSegmentedControl.selectedSegmentIndex;
}
The page curl feature is working fine. As the page curls, as mentioned before, I have a segmented control in the new view. But the the segmented control is not working properly in IOS 6. I have debugged and checked. On the click of the segments, the control doesn't enter the event method.
Its still working fine in the previous versions of IOS, but not in IOS 6. Cant figure out, what is wrong. Help needed.
If you look at the Xcode console when the app launches, do you see a message like this:
Applications are expected to have a root view controller at the end of application launch
If so, then your you problem could be due to changes in how the root view controller needs to be set up for iOS 6.
See here for info on how to address the problem.
Sorry if this is kinda late, but I have been working on something similar in my app. Honestly, I just used a simple switch method in order to change the map type. The code is below.
- (IBAction)toggle:(id)sender {
switch ([sender selectedSegmentIndex]) {
case 0:
{
[self.mapView setMapType:MKMapTypeStandard];
}break;
case 1:
{
[self.mapView setMapType:MKMapTypeHybrid];
}break;
case 2:
{
[self.mapView setMapType:MKMapTypeSatellite];
}break;
}
}
Now I have not tried using this with multiple view controllers so this may only work with the one view controller. I just started programming on iOS so bear with me.. I'm gonna try to add the page curl effect to my app but all I have so far is a map view and a button that when it's pushed, zooms into the users location and the user is able to change the map type..
P.S. This is a Segmented Control set as a Value Changed action named "toggle" if that helps...
I have a view which is created in IB. inside it I have a scroll view created programmatically. Within the scroll view I connect to a web service and fetch the content and the image. I want to show an activity indicator while doing this. So I have:
- (void)viewDidLoad
{
[super viewDidLoad];
activityIndicator = [[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
activityIndicator.frame = CGRectMake(0.0, 0.0, 40.0, 40.0);
activityIndicator.center = self.view.center;
[self.view.window addSubview:activityIndicator];
And right after the scrollview is added, I have:
[self.view addSubview:scrollView];
// ADD Scroll View Ends
//Add the Lisitng Image
NSOperationQueue *queue = [NSOperationQueue new];
NSInvocationOperation *operation = [[NSInvocationOperation alloc]
initWithTarget:self
selector:#selector(loadImage)
object:nil];
[queue addOperation:operation];
And in loadimage I have:
- (void)loadImage {
[activityIndicator startAnimating];
I've tried [self.view.window addSubview:activityIndicator];, [self->ScrollView addSubview:activityIndicator];, [self.view addSubview:activityIndicator];
but I just can't get the indicator to show. Anyone have any idea what might be wrong?
You should do this in you viewDidload
- (void)viewDidLoad
{
//Start Activity Indicator View
indicatorView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
indicatorView.frame = CGRectMake(40.0, 20.0, 60.0, 60.0);
indicatorView.center = self.view.center;
[self.view addSubview:indicatorView];
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
[indicatorView startAnimating];
[self performSelectorInBackground:#selector(loadscroll) withObject:self];
}
Note: "performSelectorInBackground" will show you an activity indicator on the view and your loadScroll method will fetch all data from internet and do other work.
-(void)loadscroll
{
//Your Scroll View
//Your other Data Manipulation even from internet
//When data is fetched display it
[self removeActivityIndicator]; //To stop activity Indicator
}
- (void)removeActivityIndicator
{
[indicatorView stopAnimating];
}
What I have found so far after multiple times facing above problem with activity indicator.
Activity indicator and the function (or method or piece of code) that is required to be executed after appearance of activity indicator both can't run on main thread. If it is so activity indicator will not appear. The solution is to run activity indicator on main thread and the function in a background thread.
I have also faced a case in which the function followed by activity indicator can't run in a background thread. In my case it was playing a music file using MPMoviePlayerController that can't be executed in a background thread. What I did to solve this issue was to run activity indicator in the main thread and then used an NSTimer to call my method after a delay that was good enough for activity indicator to appear and start animating.
I have also found that activity indicator must be called in performSelectorOnMainThread and should be added as a subview on the top view of given viewController once it starts animating.
Once it is not needed it should be removed by calling removeFromSuperView method.
I am working on a Universal iOS Application on Xcode 4. It's my first time trying to create an iPhone/iPad app. I am using the Master-Detail Application Template.
This app is roughly a RSS Feed Reader.
I have followed this tutorial to get the big idea: http://cocoadevblog.com/iphone-tutorial-creating-a-rss-feed-reader
I succeed to make the iPhone part working but i have a problem with the UIWebView in the iPad part.
Here is the code which may not be correct.
From MasterViewController ... :
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
if (!self.detailViewController) {
NSDictionary *theItem = [items objectAtIndex:indexPath.row];
DetailViewController *nextController = [[DetailViewController alloc] initWithNibName:#"DetailViewController_iPhone" bundle:nil];
[nextController setDetailItem:theItem];
[self.navigationController pushViewController:nextController animated:YES];
}
}
else {
if (!self.detailViewController){
NSDictionary *theItem = [items objectAtIndex:indexPath.row];
DetailViewController *nextController = [[DetailViewController alloc] initWithNibName:#"DetailViewController_iPad" bundle:nil];
[nextController setDetailItem:theItem];
self.detailViewController = nextController;
}
else {
[self.detailViewController setDetailItem:[self.items objectAtIndex:indexPath.row]];
}
}
}
... to DetailViewController, here is the implementation of setDetailItem: and configureView :
- (void)setDetailItem:(id)newDetailItem
{
if (_detailItem != newDetailItem) {
_detailItem = newDetailItem;
// Update the view.
[self configureView];
}
if (self.masterPopoverController != nil) {
[self.masterPopoverController dismissPopoverAnimated:YES];
}
}
- (void)configureView
{
// Update the user interface for the detail item.
if (_detailItem) {
NSLog(#"webpage creation");
NSString *html = [_detailItem objectForKey:#"title"];
[...html stuff added to html string...]
webView = nil;
webView.delegate=self;
//[self.webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:#"http://www.google.com"]]];
[self.webView loadHTMLString:html baseURL:nil];
NSLog(#"webView loaded");
}
}
I got a blank screen on the detailView pane when i select a row on the MasterView (MasterView/detailView -> in the universal app case, the iPad app is a splitview style App).
I linked the webView in InterfaceBuilder (for both iPhone/iPad .xib files), i received every NSLog, but i cannot make the UIWebViewDelegate answer me even if i implemented it. Of course, the comment with loading the Google Home page doesnt work either.
UPDATE 25/10/11:
I created an new Universal App, a simple one, where i added a UIWebView and i try to load Google webPage.
I can load the page from the viewDidLoad/viewDidAppear (detailViewController) but not from configureView (detailViewController) which is called when i select a row (fire the setDetailItem in detailViewController).
It is like i cannot modify it after loading the panel. There is something I dont get.
(The code I use to launch a webpage is the same code line than the comment above in configureView)
UPDATE 26/10/11
The webView has no value (null) in configureView (when i try to display it for a selected row). I tried this, which is not working but I (obviously/at least) get a value for webView in that case :
- (void)configureView{
if (_detailItem) {
UIWebView *tempWebView = [[UIWebView alloc] init];
self.webView = tempWebView;
NSLog(#"webpage creation");
NSString *html = [_detailItem objectForKey:#"title"];
[...html stuff added to html string...]
//[self.webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:#"http://www.google.com"]]];
[self.webView loadHTMLString:html baseURL:nil];
self.webView.delegate=self;//better to add "self" to get it working
NSLog(#"webView loaded");
NSLog(#"%#",self.webView); //i got this : <UIWebView: 0x6a5a550; frame = (0 0; 0 0); layer = <CALayer: 0x6a59750>>
}}
By writting "self.webView.delegate=self;" i got the delegate answering me. However this one produce two answers (??) like if there were two webView running ?:
2011-10-26 10:49:57.089 APPName[18334:f803] didStart Loading web Page
2011-10-26 10:49:59.416 APPName[18334:f803] didStart Loading web
Page
2011-10-26 10:49:59.523 APPName[18334:f803] finished loading web
page
2011-10-26 10:49:59.806 APPName[18334:f803] finished loading
web page
I finally got it working.
However, the only solution i've found is to create another project with STORYBOARD and not xib files, which was I guess the problem.
So i dont really have the answer for my problem, but i guess it is for sure a problem of link in the xib files. But i didnt find it.
With Storyboard, I worked out the iPad version easily, but not the iPhone version... until I found the good tutorial : http://www.youtube.com/watch?v=NHIOx_1mz-Q
So for the Storyboard, i deleted the tableview section (inserted by default) and put this code :
MasterViewController
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
NSLog(#"SELECT ROW IPHONE");
if (!self.detailViewController){
NSDictionary *theItem = [items objectAtIndex:indexPath.row];
DetailViewController *nextController = [self.storyboard instantiateViewControllerWithIdentifier:#"Detail"];
[nextController setDetailItem:theItem];
[self.navigationController pushViewController:nextController animated:YES];
}
}
else {
NSLog(#"SELECT ROW IPAD");
self.detailViewController.detailItem = [self.items objectAtIndex:indexPath.row];
}
}
DetailViewController
- (void)configureView{
// Update the user interface for the detail item.
if (self.detailItem) {
self.detailDescriptionLabel.text = [self.detailItem description];
self.webView.delegate=self;
NSString *html = #"";
[...html stuff...]
[self.webView loadHTMLString:html baseURL:nil];
}
}
I dont mention it, but i also created and linked webview of course.
If someone find the answer for the xib version of a simple project with TableView linked to a detailView for iPad (on Universal iOS project), i would like to ear it.
I don't know if this will fix your problem completely, but at least this line is suspicious:
webView = nil;
It looks to me like you are setting the webView to nil just before you set the delegate? If it is nil at that point, there is no way that the delegate is going to be set. In fact, when you try to send it any messages, it will just ignore them.
(Also that may be leaking memory.)
A split view controller must always be the root of any interface you create.
The panes of your split-view interface may then contain navigation controllers,
tab bar controllers,…
the split view controller automatically handles most of the rotation behaviors
And we can’t use UISplitViewController inside TabBars !!
My issue is that the back button will not restore its visibility if my web request does not finish before or soon after ViewWillAppear has fired.
I have a navigation based iPhone 4.0 application used a simple Root and Detail view setup.
I am working with data that is returned from a webservice so when I push my detail view in its ViewDidLoad function I call my web service method in a separate thread and the Iphone lifecycle does its thing on the main thread. I must disable/hide the back button until the web request has finished (or failed) so I call self.navigationItem.hidesBackButton = YES; in ViewDidLoad and self.navigationItem.hidesBackButton = NO; in the delegate function which fires once my web request has finished or failed.
I already tried the following:
[self.navigationItem performSelectorOnMainThread:#selector(setHidesBackButton:) withObject:NO waitUntilDone:NO];
[self.navigationItem setHidesBackButton:NO];
[self.view setNeedsDisplay];
[self.navigationController.view setNeedsDisplay];
UINavigationItem *nav = self.navigationItem;
nav.hidesBackButton = NO;
Root View Controller Push Code:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
ArticleViewController *articleViewController = [[ArticleViewController alloc] initWithNibName:#"ArticleViewController" bundle:nil];
NewsArticle *newsArticle = [newsItems objectAtIndex:indexPath.row];
articleViewController.articleID = newsArticle.newsID;
[self.navigationController pushViewController:articleViewController animated:YES];
[newsArticle release];
[articleViewController release];
}
Details View Controller Code:
- (void)viewDidLoad {
[super viewDidLoad];
self.navigationItem.hidesBackButton = YES;
id scrollView = [[[self webContent] subviews] objectAtIndex:0];
if([scrollView respondsToSelector:#selector(setBackgroundColor:)] )
{
[scrollView performSelector:#selector(setBackgroundColor:)
withObject:[UIColor blackColor]];
}
[self getNewsArticle];
}
//Fires when the web request has finished
- (void) finish:(NewsArticle *)newsArticleFromSvc {
self.navigationItem.hidesBackButton = NO;
self.newsArticle = newsArticleFromSvc;
[self bindNewsArtice];
}
Any help is GREATLY appreciated I can hardly ##$&^ believe that hiding a button in a UI could cause me this much wasted time.
Try use this method of UINavigationItem :
- (void)setHidesBackButton:(BOOL)hidesBackButton animated:(BOOL)animated
I wasn't able to solve this problem. Instead I tweaked my App Logic to make hiding he back button not necessary.
I have an app that loads a UIWebView. And that works well. It is possible for the user to click a link in the web view which then creates a new view controller/web view to load the link, which also works well.
What isn't working so well, is that sometimes (1/4 maybe) when I come back to the initial web view, the view is blank white.
The controller is not being dealloced, and is not receiving memory warning in between. And I'm totally puzzled.
Anyone know what's going on, and more especially, how to fix it?
Here are any methods that have anything to do with the controllers:
Initial Controller:
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
if (navigationType == UIWebViewNavigationTypeLinkClicked)
{
WebView * wv = [[WebView alloc] initWithNibName:#"WebView" bundle:nil];
UIBarButtonItem *backBar = [[UIBarButtonItem alloc] initWithTitle:#"Back to the Article" style:UIBarButtonItemStyleDone target:nil action:nil];
self.navigationItem.backBarButtonItem = backBar;
[backBar release];
[[UIAppDelegate navigationController] pushViewController:wv animated:YES];
[[wv webView] loadRequest:request];
[wv release];
}
return NO;
}
else
return YES;
}
Other controller (WebView):
- (void)viewDidLoad {
[super viewDidLoad];
[webView setDelegate:self];
}
Are you sure it's not receiving a memory warning?
It might not be obvious when this occurs.
I had a similar problem that I resolved by using:
[self.webView loadHTMLString:#"HTML GOES HERE" baseURL:[NSURL URLWithString:#""]];
instead of:
[self.webView loadHTMLString:#"HTML GOES HERE" baseURL:nil];
Apparently the UIWebView was clearing as soon as
[[self navigationController] presentModalViewController:myModalViewController animated:YES];
was called when passing nil for the baseURL: argument of UIWebView's -loadHTMLString:baseURL: method. This bug appeared after upgrading to the iOS 4.3 SDK.