Universal iOS App : UIWebView in detailViewController does not display content for iPad part - iphone

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 !!

Related

Loading Alternative UITabBarController for iPhone5 support

I have to add support for iPhone5 to my app. Currently the appdelegate uses a nib that is a UITabBarController, and code like this. Works fine.
[window addSubview:rootController.view];
[window makeKeyAndVisible];
rootController is an instance of UITabBarController.
So I have created a new nib for iPhone5 and changed the code to...
if ([self IsTall])
rootController = [[[UITabBarController alloc] initWithNibName:#"MainWindow_5" bundle:nil] autorelease];
else
rootController = [[[UITabBarController alloc] initWithNibName:#"MainWindow" bundle:nil] autorelease];
[window addSubview:rootController.view];
[window makeKeyAndVisible];
But, the screen is blank with this code, like the nib is not loading.
If I try this I get the correct nibs loading and displaying on the screen but the "MORE" button is not shown and only the first 4 tabs are shown (there are 7 tabs in the tabBarController
if ([self IsTall])
rootController = [[rootController initWithNibName:#"MainWindow_5" bundle:nil] autorelease];
else
rootController = [[rootController initWithNibName:#"MainWindow" bundle:nil] autorelease];
[window addSubview:rootController.view];
[window makeKeyAndVisible];
I also tried...
if ([self IsTall])
[[NSBundle mainBundle] loadNibNamed:#"MainWindow_5" owner:rootController options:nil];
else
[[NSBundle mainBundle] loadNibNamed:#"MainWindow" owner:rootController options:nil];
But this causes a crash on the tab buttons for the nib not declared in the plist under Main
"nib file base name" setting.
Any help very much greatly appreciated. This has stumped me for a couple of days now.
Kind Regards
Rob.
Miscellaneous thoughts:
iPhone 5 is iOS 6. Was your app working under iOS 6 before? If not, get it working for iOS 6 first. My apps came up blank when linked against iOS 6 even if no other changes were made. That's because view controllers work in a whole different way. So step one is to get the simple of act of launch ironed out for iOS 6. This is particularly true if you are launching into landscape; everything is totally changed in this regard.
Do not add the subview yourself. Just set the window's rootViewController. It adds the subview for you.
On the whole you should NOT be loading a different nib in any case. You should be using layout to lay out the same interface in such a way that it doesn't matter whether the screen is tall or not.
Hope something in there will help...
Your second code is totally illegal. Never never never say "init" except in the very same line where you just said "alloc" (except in an initializer, of course).
Here is the method I use
+ (BOOL)isPad{
BOOL result = NO;
if ([[UIDevice currentDevice] respondsToSelector:#selector(userInterfaceIdiom)]){
result = [[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad;
}
return result;
}
+ (BOOL) isPhone{
BOOL result = NO;
static NSString *model;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
model = [UIDevice currentDevice].model;
});
if ([model hasPrefix:#"iPhone"]){
result = YES;
}
return result;
}
+ (BOOL)isWidescreen{
BOOL result = NO;
static CGFloat ratio;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
CGSize size = [UIScreen mainScreen].bounds.size;
CGFloat numerator = MAX(size.height, size.width);
CGFloat denominator = MIN(size.height, size.width);
ratio = numerator / denominator;
});
if (ratio > 1.77 && ratio < 1.78){
result = YES;
}
return result;
}
I then have Define statements to make them easier to reach
#define IS_IPAD() [StaticContainer isPad]
#define IS_WIDESCREEN() [StaticContainer isWidescreen]
#define IS_IPHONE() [StaticContainer isPhone]
I also have a UIViewController category
#implementation UIViewController (initHelpers)
- (id) initClassDevice{
NSString *device = #"";
if (IS_IPAD()) {
device = #"iPad";
}
else if (IS_WIDESCREEN()){
device = #"iPhone5";
}
else {
device = #"iPhone";
}
NSString *nibName = [NSString stringWithFormat:#"%#_%#",[self class],device];
self = [self initWithNibName:nibName bundle:nil];
return self;
}
#end
You will need to have a nib file for each one that has the different items in it. and they need to be connected to everything separately.
Mine currently only separates iphone and ipad so for a view controller i would have
MyMagicViewController.h/MyMagicViewController.m
and for the xib files i would have
MyMagicViewController_iPad.xib, MyMagicViewController_iPhone5.xib and MyMagicViewController_iPhone.xib
the important thing is to match the name of the xib file with the call to initWithNibName:
Also Another thing to think of. is the View you are initializing needs to be the same size as the View it is going into .
view.frame = superView.bounds;
then it will fit properly
for me adding Default-568h#2x.png to my project resolved it.

TableView obstructed by Titlebar

I'm building my first basic tabbed, application with one of the views as a navigation controller that will display a view controller.
I'm running into an issue at the point the user selects a category from the first tableview as shown in the screenshot: http://www.cl.ly/7YOF
When another instance of the tableviewcontroller is loaded and pushed onto the stack of the navigationcontroller, the table is obstructed by the title bar:
http://www.cl.ly/7ZRz
The table view select logic is below:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
KHCategory *selectedItem = [categoryArray objectAtIndex:indexPath.row];
if (selectedItem.categories.count > 0) {
KHCategoryTableViewController *nextCategoryController = [[KHCategoryTableViewController alloc] init];
nextCategoryController.categoryArray = [[NSArray alloc] initWithArray:selectedItem.categories];
nextCategoryController.title = selectedItem.labelValue;
[self.navigationController pushViewController:nextCategoryController animated:YES];
[nextCategoryController release];
} else {
NSLog(#"show detail view");
}
}
EDIT:
I should be clear that an instance of KHCategoryTableViewController is the root of my NavigationController and the NavController is wired up to the first tab of a TabController.
Two interesting things: it measures 20 pixels down (size of status bar) and your line "nextCategoryController.title = ..." doesn't seem to do anything. So...
1) I assume you haven't used setStatusBarHidden?
2) Looks like navController stuff isn't working. Can you give the code from the appDelegate that creates the tabBar and NavController?
3) Add this code, and try calling [self dumpWindow: #"VDL"] from your Subcategory ViewDidLoad method. I find it invaluable whenever checking whether my view structure is correct.
- (void) dumpWindowFrom:(NSString *) fromText {
[self dumpViews: nil from:fromText];
}
void dumpViewsRecursive(UIView* view, NSString *text, NSString *indent) {
Class cl = [view class];
NSString *classDescription = [cl description];
if ([text compare:#""] == NSOrderedSame)
NSLog(#"%d: %# %# %#", (int)view, classDescription, NSStringFromCGRect(view.frame), view.hidden ? #"Inv" : #"Vis");
else
NSLog(#"%d: %# %# %# %#", (int)view, text, classDescription, NSStringFromCGRect(view.frame), view.hidden ? #"Inv" : #"Vis");
for (NSUInteger i = 0; i < [view.subviews count]; i++)
{
UIView *subView = [view.subviews objectAtIndex:i];
NSString *newIndent = [[NSString alloc] initWithFormat:#" %#", indent];
NSString *msg = [[NSString alloc] initWithFormat:#"%#%d:", newIndent, i];
dumpViewsRecursive (subView, msg, newIndent);
[msg release];
[newIndent release];
}
}
- (void) dumpViews: (UIView *) view {
dumpViewsRecursive (( (!view) ? [[UIApplication sharedApplication] keyWindow] : view), #"" ,#"");
}
- (void) dumpViews: (UIView *) view from:(NSString *) fromText{
dumpViewsRecursive ((!view) ? [[UIApplication sharedApplication] keyWindow] : view, fromText, #"");
}
4) You could always just cheat and add:
CGRect frame = [nextCategoryController.view frame];
frame.origin.y = frame.origin.y+20.0;
[nextCategoryController.view setFrame:frame];
Check the autoResizingMask of your KHCategoryTableViewController's view.
UINavigationController overview at iPhone Dev Center says:
Note: Because the amount of space
available for the custom view can vary
(depending on the size of the other
navigation views), your custom view’s
autoresizingMask property should be
set to have a flexible width and
height. Before displaying your view,
the navigation controller
automatically positions and sizes it
to fit the available space.
This issue became resolved when I built against iOS 4.3 and not iOS 5.

iPhone Creating a Modal Movie Window

I have created an app that incorporates a table view, detail view, and a web view. I have established a cell in the table view that takes a URL from an RSS feed and will display it in a webview. The URL is an .MP4 file which causes the video to play. The problem I am having is that when the video ends, I cannot go back to the previous screen. The code is below:
DetailViewController.m load webview
if (indexPath.section == SectionHeader && indexPath.row == SectionHeaderEnclosure) {
if (item.enclosures) {
for (NSDictionary *dict in item.enclosures){
NSString *url = [dict objectForKey:#"url"];
NSLog(#" url is : %#",url);
//EXPERIMENTAL
WebViewController *webVC = [[WebViewController alloc] initWithURL:url];
[self presentModalViewController:webVC animated:YES];
}
}
}
WebViewController.m
- (void)loadView
{
UIWebView *webView = [[UIWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 480)];
self.view = webView;
[webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:_url]]];
}
//Dismiss modal view
- (IBAction) done:(id)sender {
NSLog(#"done:");
[self dismissModalViewControllerAnimated:YES];
}
I think it would be better to use an MPMoviePlayerViewController. The documentation shows how to set it up with the URL directly using initWithContentURL: and present it modally. You can listen for notifications thet the movie has ended and dismiss it when done. Lots of good stuff in the documentation.

Hiding Back Button on Navigation Based iPhone App Fails

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.

iPhone SDK - WebView activity indicator

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.