Xcode Master Detail Application UIWebView not loading content - ios

I have a Master Detail Application, on the RootViewController(master) I'm using a tableview to display links, when a user clicks on one of the cells it should populate the detailview with the appropriate webpage.
In the RootViewController here is the code I'm using on the DidSelectRow:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"did select row");
NSArray* reversedArray = [[objectData reverseObjectEnumerator] allObjects];
NSDictionary *snapshot = [[NSDictionary alloc] init];
snapshot = [reversedArray objectAtIndex:indexPath.row];
DetailViewController *myCont = [[DetailViewController alloc] init];
NSString *url = snapshot[#"url"];
[myCont loadPage:url];
}
On the detailview here is the method I have to load the webpage
- (void)loadPage:(NSString*)urlString
{
NSLog(#"THE URL: %#",urlString);
NSURLRequest* request=[NSURLRequest requestWithURL:[NSURL URLWithString:urlString]];
NSLog(#"REQUEST: %#",request);
//self.myWebView.scalesPageToFit = YES;
[myWebView loadRequest:request];
}
In my Storyboard, I have my UIWebView connected to my DetailViewController for webview delegate and webview outlet.
The page never loads

Have you tried conforming to the UIWebViewDelegate protocol?
UIWebViewDelegate has these callbacks that will help you debug:
webView:shouldStartLoadWithRequest:navigationType:
webViewDidStartLoad:
webViewDidFinishLoad:
webView:didFailLoadWithError:
All you need to do is find the identifier for the segue and pass the URL string to the DetailViewController when trying to segue into that controller and actually call your loadPage method inside the viewDidLoad() of DetailViewController.
In the Master-Detail template apple provides you can pass data like this
** Take note of the [controller setDetailItem: object] thats where you should pass your URL.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([[segue identifier] isEqualToString:#"showDetail"]) {
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
NSManagedObject *object = [[self fetchedResultsController] objectAtIndexPath:indexPath];
DetailViewController *controller = (DetailViewController *)[[segue destinationViewController] topViewController];
[controller setDetailItem:object];
controller.navigationItem.leftBarButtonItem = self.splitViewController.displayModeButtonItem;
controller.navigationItem.leftItemsSupplementBackButton = YES;
}
}

Your are creating a new DetailViewController, instead of take the DetailViewController of your storyboard.
This line is wrong:
DetailViewController *myCont = [[DetailViewController alloc] init]

"In my Storyboard, I have my UIWebView connected to my
DetailViewController for webview delegate and webview outlet."
But in your code it seems like you create Details View Controller manually, not from storyboard.
DetailViewController *myCont = [[DetailViewController alloc] init];
You should use UIStoryboardSegue instead of alloc+init.
Try to read this article - https://developer.apple.com/library/ios/featuredarticles/ViewControllerPGforiPhoneOS/UsingViewControllersinYourApplication/UsingViewControllersinYourApplication.html

Related

segue not passing data over

im still having problems with my segue
i have at the moment
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
// Check that a new transition has been requested to the DetailViewController and prepares for it
if ([segue.identifier isEqualToString:#"WeddingDetail"]){
// Capture the object (e.g. exam) the user has selected from the list
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
PFObject *object = [self.objects objectAtIndex:indexPath.row];
// Set destination view controller to DetailViewController to avoid the NavigationViewController in the middle (if you have it embedded into a navigation controller, if not ignore that part)
UINavigationController *nav = [segue destinationViewController];
DMKWeddingDetailViewController *detailViewController = (DMKWeddingDetailViewController *) nav.nextResponder;
detailViewController.exam = object;
NSLog(#"object details: %#",object);
}
}
i was hoping that exam would hold the data but it isnt the data is held in object. but not sure how to pass object over to detail view controller.
in my detailview controller i have a list if textlabels
self.name.text = [self.exam objectForKey:#"name"];
but they are not populating
in case that the destination ViewController is a UINavigationController, I think the problem is in
DMKWeddingDetailViewController *detailViewController = (DMKWeddingDetailViewController *) nav.nextResponder;
you should use this instead
DMKWeddingDetailViewController *detailViewController = (DMKWeddingDetailViewController *) [nav topViewController];
and if the destination is your target ViewController you should use
DMKWeddingDetailViewController *detailViewController = (DMKWeddingDetailViewController *) [segue destinationViewController];

didSelectRowAtIndexPath or prepareForSegue to use?

i want to be able to select a item in my tableview and get to the correct view.
say i have two items in my uitableview. one has a value of ubuntu other has a value of arch- in my plist from arraA i have a SystemID which holds the text Ubuntu/Arch, never both
now when i select the one with ubuntu i want it to push to a new view where ubuntu stuff is
and if i go back and select arch it will again push me to a view where arch stuff is
so far i made this and it's not working as intended:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *segueName = nil;
if ([[arrayA valueForKey:#"SystemID"]isEqualToString:#"Ubuntu Linux"]) {
ActionViewController *controller = [[ActionViewController alloc] initWithNibName:#"Ubuntu Linux" bundle:nil];
controller.Serverinfo = [arrayA objectAtIndex:indexPath.row];
[self.navigationController pushViewController:controller animated:YES];
}
else if ([[arrayA valueForKey:#"SystemID"]isEqualToString:#"Arch Linux"]) {
ActionViewController *controller = [[ActionViewController alloc] initWithNibName:#"Arch Linux" bundle:nil];
controller.Serverinfo = [arrayA objectAtIndex:indexPath.row];
[self.navigationController pushViewController:controller animated:YES];
}
[self performSegueWithIdentifier: segueName sender: self];
}
also tried prepare forsegue
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if([arrayA valueForKey:#"Arch Linux"])
{
ActionViewController *controller = (ActionViewController *)segue.destinationViewController;
NSIndexPath *indexPath = [self.mainTableView indexPathForSelectedRow];
controller.Serverinfo = [arrayA objectAtIndex:indexPath.row];
NSLog(#"hostname = %#", controller.Serverinfo);
}
if ([arrayA valueForKey:#"Ubuntu Linux"]) {
ActionViewController *controller = (ActionViewController *)segue.destinationViewController;
NSIndexPath *indexPath = [self.mainTableView indexPathForSelectedRow];
controller.Serverinfo = [arrayA objectAtIndex:indexPath.row];
NSLog(#"hostname = %#", controller.Serverinfo);
}
}
arrayA is a NSMutableArray that read from a plist that you can see here
Replace your method to:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if ([arrayA[indexPath.row][#"SystemID"]isEqualToString:#"Ubuntu Linux"]) {
//ActionViewController *controller = [[ActionViewController alloc] initWithNibName:#"Ubuntu Linux" bundle:nil];
// Make sure you replace Storyboard with your storyboard name
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Storyboard" bundle:nil];
ActionViewController *controller = [storyboard instantiateViewControllerWithIdentifier:#"Ubuntu Linux"];
controller.Serverinfo = [arrayA objectAtIndex:indexPath.row];
[self.navigationController pushViewController:controller animated:YES];
}
else if ([arrayA[indexPath.row][#"SystemID"]isEqualToString:#"Arch Linux"]) {
ActionViewController *controller = [[ActionViewController alloc] initWithNibName:#"Arch Linux" bundle:nil];
controller.Serverinfo = [arrayA objectAtIndex:indexPath.row];
[self.navigationController pushViewController:controller animated:YES];
}
}
You don't need to call [self performSegueWithIdentifier: segueName sender: self]; in didSelectRow because you already push your view in didSelectRow method. If you call it you puts the view twice.
Ooh boy, you're off in the weeds.
First, stop talking about pushing a "view." The term view and view controller are NOT interchangeable. A view is an object that appears on the screen. A view CONTROLLER is an object that manages a collection of views (usually the entire screen.)
You push a view controller, not a view. The view controller then displays it's content view on the screen.
Now, as to your code.
The if statement in your tableView:didSelectRowAtIndexPath: method is messed up.
if ([[arrayA valueForKey:#"SystemID"]isEqualToString:#"Ubuntu Linux"])
You don't want to use valueForeKey on an array. That method attempts to call valueForKey on every object in your array, and what it returns is an array of the results.
Arrays are indexed by a numeric index, not a key. You want an array as the data source for a table view.
What you want to do is to fetch the object at your selected row from your array.
In order to help you further, though, you need to explain what you have stored in your arrayA. Is it an array of dictionaries? An array of strings?
Also, you are mixing NIB based view controller code with storyboard code. You can certainly use both in the same program if you need to, but as a beginner you probably should not.
In your tableView:didSelectRowAtIndexPath method you're creating a view controller with initWithNibName, pushing it, and then calling performSegueWithIdentifier. You want to do one or the other, not both.
Use storyboard with prepareForSegue

Data is not transferred to next view Controller

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSLog((#"This is didSelectRowAtIndexPAth"));
DetailViewController *detailViewController = [[DetailViewController alloc] init];
detailViewController.myDictionary = [self.placeArray objectAtIndex:[indexPath row]];
/* The below NSlog is showing the content , its not null still in next viewcontroller same variable showing null content*/
NSLog(#"My Dictionary: This is a MasterView%#",detailViewController.myDictionary);
[self performSegueWithIdentifier:#"showDetail" sender:self];
}
It is because the presented instance of your DetailViewController is different than the instance you are creating manually. You should either present that new ViewController yourself (modally or by push), or implement the prepareForSegue method and set the dictionary there.
Edit:
The 2 solutions:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
DetailViewController *detailViewController = [[DetailViewController alloc] init];
detailViewController.myDictionary = [self.placeArray objectAtIndex:[indexPath row]];
// If it is a modal
[self presentViewController:detailViewController animated:YES];
}
OR
#implementation MyClass {
NSIndexPath *selectedCellIndexPath;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSLog((#"This is didSelectRowAtIndexPAth"));
selectedCellIndexPath = indexPath;
[self performSegueWithIdentifier:#"showDetail" sender:self];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"showDetail"]) {
DetailViewController *detailViewController = (DetailViewController *)segue.destinationViewController;
detailViewController.myDictionary = [self.placeArray objectAtIndex:selectedCellIndexPath.row];
}
}
try this
Decalre one NSInteger varable Globaly assign that varble to indexpath.row
#interface DashbordViewController ()
{
NSInteger SelectedRownum;
}
#end
#implementation DashbordViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
SelectedRownum=indexPath.row
[self performSegueWithIdentifier:#"showDetail" sender:self];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"showDetail"])
{
NSLog((#"This is didSelectRowAtIndexPAth%#",SelectedRownum));
DetailViewController *detailViewController = [segue destinationViewController];
detailViewController.myDictionary = [self.placeArray objectAtIndex:SelectedRownum];
}
}
In DetailViewController check if the myDictionary is a property declared as weak or strong..
try this . . .
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog((#"This is didSelectRowAtIndexPAth"));
UIStoryboard *mystoryboard = [UIStoryboard storyboardWithName:#"myStoryBoardName" bundle:nil];
self.detailsViewController = [mystoryboard instantiateViewControllerWithIdentifier:#"detailviewcontroller"]
/* The below NSlog is showing the content , its not null still in next viewcontroller same variable showing null content*/
NSLog(#"My Dictionary: This is a MasterView%#",detailViewController.myDictionary);
[self performSegueWithIdentifier:#"showDetail" sender:self];
}
you have to set identifier of your detailViewController in storyBoard .
You need to assign the dictionary in prepareForSegue: method.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
DetailViewController *viewController = = (DetailViewController *)[segue destinationViewController];
detailViewController.myDictionary = //YOUR Dictionary here;
}
This way the same dictionary can be obtained in viewDidLoad: of DetailViewController.
Note: Make sure you allocate the dictionary.
Hope it helps.
Since I can't see implementation of your controller (being subclass of UIViewController or not), I cannot say a lot, but keep in mind that initWithNibName:bundle: is the designated initializer for UIViewController and not init.
So, this is the first problem. In any case, check the instance before setting a property. It is very likely that instance is nil.
EDIT:
It is also very likely, they you are instantiating new View Controller from existing. In this case, you can add new view controller as child controller or present new controller from existing. In both cases properties of existing are reachable from new through properties: parentViewController and presentingViewController, which makes the story opposite: you take property value from existing controller into new controller.
You are creating new view controller in didSelectRowAtIndexPath, and at the end you are calling prepareForSegue method... which will create new Instance, So you have to set your data which you want to send in new view controller in prepareForSegue method like this.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
//
if ([[segue identifier] isEqualToString:#"name_of_ur_segue"])
{
// Get reference to the destination view controller
DetailViewController *detailViewController = [segue destinationViewController];
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
// Pass any data here ...
detailViewController.myDictionary = [self.placeArray objectAtIndex:[indexPath row]];
}
}
Edit:
As you are accessing data in ViewDidLoad method and in iOS 7 data passed like this is not accessible in ViewDidLoad method. As view controller presentation logic is bit changed now. Now we can pass data in initMethod if we want to access this in viewDidLoad.
Or access this in viewWillAppear or viewDidApepar.
If you want to call this code only once in viewDidAppear or ViewWillApepar just set a boolean flag, which can tell if you want to access this or not.

Getting a blank view. View logging null

I'm having a problem similar to this... Why am I seeing a black screen? Did I not alloc something?
Sorry for the beginner question but I'm not sure how to debug this. I'm seeing a blank (black) screen for one of my view controllers. When I set the view controller to "is initial view controller" in the inspector, it works as expected. However when I remove that (it shouldn't be the initial VC, just did that to debug) and navigate to the view, I get a blank (black) screen.
There is a webview in the VC (referenceDetailWebView) and when I try to log that out on the viewDidLoad of the VC and I get (null). Again, when I set the VC to be "initial", it logs out the referenceDetailWebView object as expected.
This is the implementation of the VC...
#implementation DetailViewController
#synthesize currentReference, referenceDetailWebView;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
NSString *htmlFile = [[NSBundle mainBundle] pathForResource:#"reference/accommodation" ofType:#"html"];
NSString *htmlString = [NSString stringWithContentsOfFile:htmlFile encoding:NSUTF8StringEncoding error:nil];
[referenceDetailWebView loadHTMLString:htmlString baseURL:nil];
// log for debugging
NSLog(#"%#", [self referenceDetailWebView]);
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
The only thing that I can think of is that I might be missing something in my segues. I'm handling them programmatically and it's very possible that I'm overlooking something there...? My didSelectRowAtIndexPath looks like this...
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
Reference *currentItem = [references objectAtIndex:indexPath.row];
NSString *mySegueID;
if([currentItem hasSubReferences]){
mySegueID = #"ShowSubTable";
} else {
mySegueID = #"ShowDetail";
}
[self performSegueWithIdentifier:mySegueID sender:nil];
}
And my performSegueWithIdentifier looks like this...
- (void)performSegueWithIdentifier:(NSString *)identifier sender:(id)sender{
// the segue with identifier allows us to chose a segue programatically
NSIndexPath *path = [self.tableView indexPathForSelectedRow];
Reference *item = [references objectAtIndex:path.row];
if([identifier isEqualToString:#"ShowSubTable"]){
// looking for sub table
ReferencesSubTableViewController *subTableVC = [[ReferencesSubTableViewController alloc] initWithNibName:nil bundle:nil];
[subTableVC setCurrentReference:item];
[self.navigationController pushViewController:subTableVC animated:YES];
} else if ([identifier isEqualToString:#"ShowDetail"]){
// looking for detail view
DetailViewController *detailVC = [[DetailViewController alloc] initWithNibName:nil bundle:nil];
[detailVC setCurrentReference:item];
[self.navigationController pushViewController:detailVC animated:YES];
}
}
The segues are working as expected with the exception of that detailViewController displaying a blank screen and giving me null for my referenceDetailWebView.
I'm stuck. Any advice or guidance is very much appreciated! Thanks in advance for the help!!
Edit: It looks like the segues are probably the cause. When I remove the multiple segues and related code, and add a simple segue from the table cell to the DetailViewController only, the detailVC and views load correctly. Not sure what I'm missing in the segues though...?
I've simplified the performSegueWithIdentifier to the following and I'm still getting the blank screen...
- (void)performSegueWithIdentifier:(NSString *)identifier sender:(id)sender{
NSIndexPath *path = [self.tableView indexPathForSelectedRow];
Reference *item = [references objectAtIndex:path.row];
DetailViewController *detailVC = [[DetailViewController alloc] initWithNibName:nil bundle:nil];
[detailVC setCurrentReference:item];
[detailVC setTitle:[item title]];
[self.navigationController pushViewController:detailVC animated:YES];
}
You're getting a black screen because you're instantiating your controller incorrectly. Also, you don't push the view controller with pushViewController:animated:, that's done for you when you call performSegueWithIdentifier:. If you have the segue setup in the storyboard, then you should be implementing prepareForSegue:sender:, not performSegueWithIdentifier:. Its code should look like this:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// the segue with identifier allows us to chose a segue programatically
NSIndexPath *path = [self.tableView indexPathForSelectedRow];
Reference *item = [references objectAtIndex:path.row];
if([segue.identifier isEqualToString:#"ShowSubTable"]){
// looking for sub table
ReferencesSubTableViewController *subTableVC = (ReferencesSubTableViewController *)segue.destinationViewController;
[subTableVC setCurrentReference:item];
} else if ([segue.identifier isEqualToString:#"ShowDetail"]){
// looking for detail view
DetailViewController *detailVC = (DetailViewController *)segue.destinationViewController;
[detailVC setCurrentReference:item];
}
}
You really need to read and understand Apple's docs on view controllers and segues -- you clearly have some fundamental misunderstanding of the way they work.

Pushing a view in Storyboard

I am trying to use Table Views and navigation controllers. This is the code I would use if I was using NIB files. This piece of code would work fine if I was using NIB files, but I decided to try storyboard -
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if (_webViewController == nil) {
self.webViewController = [[[WebViewController alloc] initWithNibName:#"WebViewController" bundle:[NSBundle mainBundle]] autorelease];
}
RSSEntry *entry = [_allEntries objectAtIndex:indexPath.row];
_webViewController.entry = entry;
[self.navigationController pushViewController:_webViewController animated:YES];
}
I have created another scene in storyboard and have everything set up out there. When a user clicks a cell in this Table View they will be taken to that scene. This is what I wrote for the code but it is not right and I'm hopelessly stuck :( -
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"articleView"]){
UINavigationController *navigationController = segue.destinationViewController;
WebViewController *controller = (WebViewController *)navigationController.topViewController;
controller.webView = self;
}
Answer - Answers 2 and 3 are both right
I had a very similar problem 2 days ago, with the user selecting the disclosure accessory on a row and wanting to invoke a segue. What worked for me:
In storyboard, create segue "articleView" from the entire tableView controller (not the cell) to the next viewController.
Add the following method to your tableViewController:
- (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath {
[self performSegueWithIdentifier:#"articleView" sender:[self.tableView cellForRowAtIndexPath:indexPath]];
}
Just for you I also tested with tableView:didSelectRowAtIndexPath: and it works.
Enjoy,
Damien
If your table view is already embedded in a navigation controller (looks that way, from your first code snippet) then the destination view controller of your segue will be a WebViewController, not a navigation controller.
You can therefore have your prepareForSegue method pass the item directly across :
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"articleView"])
{
WebViewController *controller = (WebViewController *)segue.destinationViewController;
RSSEntry *entry = [_allEntries objectAtIndex:[self.tableView indexPathForSelectedRow].row];
controller.entry = entry;
}
}
You may find this excellent two-part tutorial helpful
http://www.raywenderlich.com/5138/beginning-storyboards-in-ios-5-part-1

Resources