iPad UISplitViewController multiple root views - iphone

I am developing for iPad and have created a standard UISplitViewController application using the template provided in Xcode - a standard UITableView on the left and a detail view on the right.
I have modified the template so that when a user selects a table cell from the left view it pushes a new table view in it's place (still on the left side). This works without issue, but I would like to be able to update the existing detail view from the new table view - kinda like how Apple's Mail application works.
- I am not trying to create multiple views on the detail view (right hand side) - I've read the documentation and seen the sample code provided by Apple.
I read/followed many tutorials, but can't seem to get this relatively simple view hierarchy to work.
More detail:-
Using detailViewController.detailItem = #"Test"; in the RootView didSelectTableRowAtIndexPath delegate method updates the Detail view label. Using the exact same code in the newly pushed Table View does not update the label - am I missing a reference point or something??
Since posting I've tried to use protocols & delegates to update a label on the detail view. The label updates correctly when changed from the Root View using the new methods, however, when I push a new view onto the root view (left hand side) I can no longer update the label.

At some point after creating the RootViewController (or maybe even in a custom init method) you are setting the delegate for the DetailViewController, its a common mistake that when a new rootViewController is pushed onto the NavController that you forget to set the delgate again.
You probably are creating a new controller in the:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath;
method and recording/incrementing the level of the new controller before you push it onto the navController. After you create this new controller, set the delegate again.
newRootController.myDelegate = self.myDelegate;
Before you do this If you NSLog the delegate just before you use it, you will probably find its nil.

Try the viewControllers property of your UISplitViewController
#property(nonatomic, copy) NSArray
*viewControllers Discussion The array in this property must contain exactly
two view controllers. The view
controllers are presented
left-to-right in the split view
interface when it is in a landscape
orientation. Thus, the view controller
at index 0 is displayed on the left
side and the view controller at index
1 is displayed on the right side of
the interface.
The first view controller in this
array is typically hidden when the
device is in a portrait orientation.
Assign a delegate object to the
receiver if you want to coordinate the
display of this view controller using
a popover.

Please beware of the detailViewController! You have to pass this instance variable to your new root view. So something like this:
newRootViewController.detailViewController = self.detailViewController
Otherwise your new root view will never know about the detailView. For your new root(table)view you have to do things like:
#import <UIKit/UIKit.h>
#class DetailViewController;
#interface VorhersageTable : UIViewController {
UITableView *vorhersageTableView;
DetailViewController *detailViewController;
}
#property (nonatomic, retain) IBOutlet UITableView *vorhersageTableView;
#property (nonatomic, retain) DetailViewController *detailViewController;
#end
to declare the property of the detailViewController in your new class.

Add this in your RootViewController.didselectRow, before you push the second table (e.g SubRoot)
SubRoot *subController = [[SubRoot alloc] initWithNibName:#"SubRoot" bundle:nil];
subController.detailViewController = self.detailViewController;
And create the SubRoot.h and SubRoot.m similar to RootViewController.
#class DetailViewController;
#interface SubRoot : UITableViewController {
DetailViewController *detailViewController;
}
#property (nonatomic, retain) DetailViewController *detailViewController;
#end
then synthesize detailViewController.
Hope it helps.

Related

UI Elements and IBOutlet Variables in Storyboard when instantiated recursively?

What I've done so far:
I am using xcode 4.2. I created a UITableView which contains an ImageView. The UITableView uses the class MyTableViewController which has the files MyTableViewController.m/.h. I created an IBOutlet variable called _bgimageview. Then in the storyboard, in the connections inspector, i connected the ImageView to this _bgimageview variable. In my viewDidLoad function of MyTableviewController.m, I set a backgound image to the _bgimageview. When I run the simulator, click on a few tabs to get to MyTableViewController, and hten I see the background image. Things work great so far.
The problem
I've also successfully built a recursive tree, but the problem is that I lose the backgorund image after the root level of the tree. I think i know why, but don't know how to fix. I instantiate a new MyTableViewController on each row-click like so:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
MyTableViewController *childView = [[MyTableViewController alloc] init];
childView._menu = children; // an NSDictionary of children
[self.navigationController pushViewController:childView animated:YES];
}
As mentioned previously, I populate the IBOutlet variable for the backgruond image in the viewDidLoad function. But that IBOutlet variable is linked up to the ImageView via the Storyboard connection. In tree levels below the root level, I programmatically instantiate new MyTableViewControllers and thus, I'm probably losing the relationship between the IBOutlet variable for the backgund image and the actual UIImageView mentioned in the Storyboard.
Can someone re-assure me this is the problem and how I might go about adding a backgorund image to MyTableViewController when used in a hierarchical manner?
MyTableViewController *childView = [[MyTableViewController alloc] init];
This line creates a new instance of your view controller without reference to the storyboard. It will therefore not be connecting any of your outlets - the table view will probably be OK, since that is created and connected by default, but anything else will not be connected.
You can confirm this by logging your image view outlet in viewDidLoad - it will be nil.
You should create a recursive stack by dragging a segue from the row of the table down to the view controller object of the same view controller:
As Lvsti points out in comments, you can pass any parameters across in prepareForSegue:sender:

Why a separate instance of ViewController Class is affecting previous instance?

UPDATE - Cause found!... please read below & suggest the solution:
While creating a video to show this issue, I have found why does that happen...
Any Control/Element that is defined between #imports & #implementation DetailViewController in .m file is lost by the original detVC when a new instance is created of VC.
Example:
I am including a video that re-creates this issue. As shown, all controls work except a UISegmentedControl & a UILabel.. both of which are defined in DetailViewController.m as:
#import "DetailViewController.h"
UISegmentedControl *sortcontrol;
UILabel *incrementLabel;
#implementation DetailViewController
Video Link - http://www.youtube.com/watch?v=2ABdK0LkGiA
I think we are pretty close.. Waiting for the answer!!
P.S.: I think this info is enough can lead us to the solution, but if needed I can share the code too.
EARLIER:
There's a detailViewController (detVC) in our app which is normally 'pushed' from a UITableViewController.
Now we are also implementing Push Notifications which would 'modally' present the same detVC whenever the notification arrives, which in turn can happen over any view controller.
It works all fine until the detVC through notification is presented while another instance of detVC was already the active view controller (pushed earlier through TableView).
The problem happens when the presented detVC is dismissed - the pushed detVC 'looses' control of about everything - It is not pointing to the same data as earlier & even the UISegmentedControl on Navigation Toolbar points to -1 index on pressing any index!
Why does such thing happen when we are creating a separate instance of detVC? Why does it affect the earlier detVC? How can it be resolved / What about Objective C needs to be learned here? (FYI, we are using ARC in the project)
Thanks
EDIT - Some code:
When Pushed:
ProductDetailViewController *detailViewController = [[ProductDetailViewController alloc] init];
detailViewController.hidesBottomBarWhenPushed = YES;
[self.navigationController pushViewController:detailViewController animated:YES];
detailViewController.serverOffset=serverOffset;
detailViewController.prdctIndex = indexPath.row;
When presented through Notification:
- (void)displaySingleProduct:(NSDictionary*)userInfo{
ProductDetailViewController *onePrdVC = [[ProductDetailViewController alloc] init];
UINavigationController *addNav = [[UINavigationController alloc] initWithRootViewController:onePrdVC];
onePrdVC.justOne=YES;
onePrdVC.onePrdIDtoLoad=[[[userInfo valueForKey:#"prd"] valueForKey:#"id"] intValue];
[self.navigationController presentModalViewController:addNav animated:YES];
}
There is a Product class in detVC which gets the data from the values stored at the prdctIndex row in the SQLite table.
After dismissing the presented detVC through notification, the Product class of the pushed detVC starts pointing to the row which was used for Product class in presented detVC. And there is no such code in viewDidAppear which would alter Product class.
So, this, UISegmentedControl issue mentioned above & some other problems are causing the pain! Basically, the whole detVC acts weird - which re-works if we just pop & re-push it!
EDIT 2
I have more info on it which could lead to the cause.
If I run viewDidLoad on viewDidAppear like this:
if (!justOne) {
aProduct = [allProducts getProduct:(prdctIndex+1) one:NO];
[self viewDidLoad];
[self populateDetails];
}
the pushed detVC regains the control & every element/control starts re-working as expected (but not in the ideal way). So, it is quite clear that the separate instance of presented detVC does messes up the earlier pushed detVC & a re-setting up of all the controls / pointers is required through viewDidLoad.
Can any helpful conclusion can be derived from this info?
When in your code you write:
#import "DetailViewController.h"
UISegmentedControl *sortcontrol;
UILabel *incrementLabel;
#implementation DetailViewController
You are not defining these variables as instance variables (ivars), so they are not individual for each instance. There are three ways to define ivars.
1) The traditional way, in your .h file.
#interface DetailViewController{
UISegmentedControl *sortcontrol;
UILabel *incrementLabel;
}
2) Some additions to objective-C have added support for the next two ways. Like declaring them in your .m file.
#implementation DetailViewController{
UISegmentedControl *sortcontrol;
UILabel *incrementLabel;
}
3) If these ivars use properties to expose them, then you can simply leave out the explicit definition of them. So in .h:
#interface DetailViewController
#property (strong, nonatomic) IBOutlet UISegmentedControl *sortcontrol;
#property (strong, nonatomic) IBOutlet UILabel *incrementLabel;
and then in the .m file:
#implementation DetailViewController
#synthesize sortcontrol;
#synthesize incrementLabel;
Are you loading the view controller from a NIB? If so, it may be that the same instance of the view controller is used each time. You can log the address of your view controller in viewDidLoad and see if this is the case.
It actually depends on how your apps architecture has been created. If you are using TabBarController based application, here is the proper way to do so.
If two instances of the same class are interfering with each other, that strongly suggests that you are not storing all your state in instance variables. I would suspect that something in ProductDetailViewController stores its state in global or class data, and that your problem lives in there.
It's possible that you have done something silly like making ProductDetailViewController a forced singleton (overriding +alloc, which you should almost never do), but generally people remember when they've done that.
Never call viewDidLoad directly. That's only for the system to call. (I know you were just testing, but don't leave that in.)
But are you calling [super viewDidAppear:animated] in your viewDidAppear:? You have to call your superclass in that method.

Is it Possible to have two view Controller's IBOutlet in one .h file? How to move to previous to previous view?

I am using XCode 4.
In My applicaion
I have Files
WelcomeViewController
FirstViewController
SecondViewController
ThirdViewController
In every view I have same functionality.
Pressing the Button and Load another view By presentModelViewController..
Problem I am Facing is I can not Declare Two outlets in One header file As If I want to move to the Back Page.?
#import <UIKit/UIKit.h>
#import "ThirdViewController.h"
#import "FirstViewController.h"
#interface SecondViewController : UIViewController {
UIButton *button1;
}
#property (nonatomic,retain) IBOutlet ThirdViewController *thirdVC;
#property (nonatomic,retain) IBOutlet FirstViewController *firstVC;
//Error at this line above
#property (nonatomic,retain) IBOutlet UIButton *button1;
#end
Is it a good practice to alloc and init the new controller or I just have to take a view controller in XIB file and then just create Outleyts???
Yes you are right definbately, I have tried it is working...but why I am taking View Controller because I just can jump to previous to previous view ? What is wrong If I take an IBOutlet.???
So, What if I want to move previous to previous view I need to write
two times
[self dismisviewContrlloer animated:YES];
[self dismisviewContrlloer animated:YES];
????
Why would you want to have 3 outlets? Just have properties for the viewcontroller. Alloc and init and present the views of the same controller class.
So basically you will have a xib file for a viewcontroller and you will need to create as many instances of that depending on your requirement. Looking at the code i think it needs lot of redesign. You present and dismiss these views accordingly.
Actually, all you need to do is [self dismissModalViewControllerAnimated:YES]; If the 3rd view is visible, the 2nd one will automatically become visible. Similarly the 1st from the 2nd. You don't really need to store references to adjacent view controllers in each view controller.
HTH,
Akshay
[self.parentViewContrtoller.parentViewController dismissModalViewControllerAnimated:YES];
By this method I can Jump Back to the previous to previous move.

iPhone Subclassing view controller and IBOutlet UITableView

I have the following problem. I've created a ViewController pretty much like the above
#interface MyViewController : UIViewController {
IBOutlet UITableView *myTableView;
}
#property (nonatomic, retain) IBOutlet UITableView *myTableView;
I've linked myTableView on the Interface Builder to the matching nib's UITableView. and I've subclassed MyViewController to create YourViewController as so:
#interface YourViewController : MyViewController {
}
And then I load from a TabBarController the YourViewController on a tab item. Although I can see that MyViewController is indeed invoked at the end, no table view is displayed on the emulator.
I've tried debugging the MyViewController and it appears the the IBOutlet is nil.
Why is that?
I have encountered major issues with inheritance and IBOutlets and IBAction. I advise you to avoid that, and create shared stuff in another way.
I was hit hard by bugs when getting memory warnings for instance. Outlets and actions didn't get reconnected properly when they were defined in base class vs derived class.
Probably MyViewController's nib file is not loaded at all. Are you using for YourViewController a specific nib file? and in which way are you creating YourViewController.
Can you also try to define an "awakeFromNib" method in YourViewController and then from it call [super awakeFromNib] ?
However to understand your issue you must clearly explain how you load objects and if and where you use Nibs?
If you add it dynamically using code then only it will work. Not using Nib.
the UITableView (ie. your myTableView) needs delegates for data source and and its control.
And your controller needs a link to the Table view in the xib.
declare table delegate protocols in the interface section of your controller.
using IB, link the TableView in your xib to owners file:delegates.
using IB, link the file owner myTableView to the tableView in the xib.
I hope it will help.
Assuming that you have your whole navigation stack in MainWindow.xib
Open MainWindow.xib
Select YourViewController (Or whatever your controller that is subclassing UITableViewController is called)
Select Attributes Inspector
Ensure that the 'NIB Name' property has your correct YourViewController (Or whatever the name) selected
I had this exact same issue, this solved it for me.

How should I load a xib into a detail view for a split view iPad objective app?

I've got a split view iPad app with table view in the master pane. After drilling down several levels, each time loading a new .xib, I'd like one of the cells to trigger a web page load in the detail pane. Right now I can only get the web page .xib to load in the master pane side -- which is a master pain in my side.
The basic load call where "URLWindow" is a class loaded with initWithNibName:
[[self navigationController] pushViewController:URLWindow animated:YES];
I want to do this, but it doesn't seem to work:
#interface
#property (nonatomic, retain) IBOutlet DetailViewController *detailViewController;
...
#implementation
[[self detailViewController] pushViewController:URLWindow animated:YES];
How should I be loading the URLWindow .xib into a detail view for a split view detail pane?
couple of things to check:
1) you may try setting BOTH master / detail controllers as 'UINavigationControllers' ?
2) [detailViewController.navigationController pushViewController:URLWindow animated:YES];
you are calling pushViewController on 'detailViewController', this is calling it on 'navigationController'