iOS Master-Detail, how to use different detailViewControllers in iPad - ipad

I have a Master-Detail iOS app, built from standard XCode template with storyboards. I need to display a view with a browser on most of the items, and a settings form (using FXForms) when user clicks the "Settings" item in UITableView of the Master.
I have found a way do it in iPhone, but can't find one in iPad. In iPhone storyboard I create another view controller and make another segue connection from Master to the new view controller. Then I do the following in didSelectRowAtIndexPath
if(indexPath.section == 0 && indexPath.row == 2) // settings
[self performSegueWithIdentifier:#"showSettings" sender:self];
else
[self performSegueWithIdentifier:#"showDetail" sender:self];
With this the Settings view come into place when I click Settings. Navigation back and forth also works.
However, in iPad the detailViewController seems to be always there, on the right of the SplitViewController. How can I modify the storyboard and/or code to swap default view to Settings and then swap back again?
I also found that default template from XCode creates "relationship" segues for iPad storyboard, a type which is not there in iPhone storyboard, and cannot be named like "showSettings" or "showDetail" as I did in iPhone case.

From the comments to the original post I found that:
If I target iOS 8, I can use same storyboard for both iPhone and iPad, and then I can use "replace" segues (now called "show detail" segues) to switch to another detail view. Very convenient, but that is not available for iOS 7 yet.
As for iOS 7 I can use segues to support iPhone, and for iPad I have to do something like this (FXForms if the neat library I am using to show a view with options).
So my code for iPad (in - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath for a specific row). I do backup the current lastObject of self.splitViewController.viewControllers to restore for other rows of the table view in Master.
FXFormViewController *controller = [[FXFormViewController alloc] init];
IBSettingsForm *settingsForm = [[IBSettingsForm alloc] init];
settingsForm.fontSize = 12;
controller.formController.form = settingsForm;
// this is created so that my new view would have a navigation bar
UINavigationController *navigationController = [[UINavigationController alloc] init];
[navigationController pushViewController:controller animated:false];
self.splitViewController.viewControllers = [NSArray arrayWithObjects:[self.splitViewController.viewControllers objectAtIndex:0],
navigationController, nil];

Related

Navigation bar not displayed when view controller is presented as a popover on iPad (versus push on iPhone when it is displayed)

I am converting an iPhone App to an iPad App. I wish to present several of the view controllers as popovers that were previously pushed on the iPhone by changing the segue type in the storyboard from push to popover. The view controllers have a title, a done button, and a cancel button in the navigation bar. Now, when I run the iPad App the popover appears but without the navigation bar. How can I get the navigation bar to show. I have already tried expected setting it not hidden in viewWillAppear which did not work.
I have been able to change the segue to an IBAction with the following code and got it to work. It seems somehow the popover does not have a navigation controller so I created one. `
// Device is iPad
addNoteVC = [self.storyboard instantiateViewControllerWithIdentifier:#"addNoteVC"];
popoverNav = [[UINavigationController alloc] initWithRootViewController:addNoteVC];
[addNoteVC setManagedObjectContext:self.managedObjectContext];
[popoverNav.navigationBar setTintColor:[UIColor darkTextColor]];
NSDictionary *textAttributes = [NSDictionary dictionaryWithObjectsAndKeys:
[UIColor darkTextColor],NSForegroundColorAttributeName,nil];
[popoverNav.navigationBar setTitleTextAttributes:textAttributes];
[addNoteVC setSelectedClient:selectedClient];
[addNoteVC setAddNoteTableViewControllerDelegate:self];
popover = [[UIPopoverController alloc] initWithContentViewController: popoverNav];
[popover setBackgroundColor:[UIColor groupTableViewBackgroundColor]];
popover.delegate = self;
[popover presentPopoverFromBarButtonItem:sender permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
`
While the above code works it creates significant differences from the iPhone App code. I would rather that the iPhone and iPad stayed the same as much as possible. If adding a another navigation controller for the popover is the only solution I would prefer to do this in a custom segue so the view controller code can stay the same. In addition that approach would have the economy of one custom segue instead of changes for every view controller presented as a popover. If that indeed is the best solution, details on the custom segue would be appreciated and accepted.

Present Modal View Controller from inside Popover View

So in my universal app I have a section where a person can look at an existing list of notes from our system (retrieved through a simple web service) and then also create a new note if they want. So for the iphone it's pretty simple layout, a TableViewController for displaying the list with a "Add" button on the NavigationBar that presents the modalview for adding the new item. On the iPad though, the same layout has a lot of wasted space so I opted to go with the popOver method to show the list in a popOver and then let them add from there. My problem is that when the user clicks on the Add button within the PopOver view, the modal view comes up full screen instead of just coming up within the popover view. Here's the code I have so far:
-(void) AddButtonPressed:(id)sender {
NewNoteVC *newNote = [[[NewNoteVC alloc] initWithNibName:#"NewNoteVC" bundle:nil] autorelease];
newNote.defaultClientID = defaultClientID;
UINavigationController *navCon = [[[UINavigationController alloc] initWithRootViewController:newNote] autorelease];
if ([isPopOver isEqualToString:#"YES"]) {
[navCon setModalInPopover:YES];
[self.navigationController setModalInPopover:YES];
[self.navigationController presentModalViewController:navCon animated:YES];
}
else {
[self.navigationController presentModalViewController:navCon animated:YES];
}
}
The "isPopOver" string is just a placeholder sent from the previous screen that called this TableView (I know I can switch this to a boolean for better performance I just put this together real quick to try it out). I know I messed up somewhere, I just don't know what setting I need to get this working correctly.
You need to define the view controller's modalPresentationStyle to be "current context".
navCon.modalPresentationStyle = UIModalPresentationCurrentContext;
This will result in modal view controller filling the popover like the popover's root controller.
Try using the presentViewController:animated:completion: instead of presentModalViewController:animated: and set self.navigationController.definesPresentationContext = YES

Launch different ViewControllers on a button click in iOS

My project is in iOS6.I have three UIViewControllers on my iPhone storyboard. On the first UIVC i have a checkbox and a continue button. If checkbox is checked and button is clicked then I have to launch second UIViewController.If checkbox is unchecked and button is clicked launch the third UiViewController. How do i do it? I have done this so far:
- (IBAction)btnContinue:(UIButton *)sender
{
if (self.shipToDifferentAddress)//Checkbox CLICKED->YES
{
//Launch ShippingVC
ITMShippingAddressVC *shippingVC = [[ITMShippingAddressVC alloc]init];
[[self navigationController]pushViewController:shippingVC animated:YES];
}
else //Checkbox unchecked ->NO
{
//Launch OrderedItemsVC
ITMOrderedItemsVC *orderedVC = [[ITMOrderedItemsVC alloc]init];
[[self navigationController]pushViewController:orderedVC animated:YES];
}
}
The self.shipToDifferentAddress is a bool variable telling launch this or that. I logged both the viewDidLoads and i got the output from both of them.There is some content on both the second and third UIViewControllers like buttons, labels etc. But they appear black.No content. I have back buttons on both of them and I can traverse back to first UIVC from either one of the one. SO what am i missing? I used segues incases where I had to go to a specific UIVC. Now i have a choice either second or third. How should i do it?Please let me know if you need more information. Thanks.
To present your ViewControllers that are in your storyboard, follow this:
UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:#"MainStoryboard"
bundle: nil];
ITMShippingAddressVC *viewController = (ITMShippingAddressVC*)[mainStoryboard
instantiateViewControllerWithIdentifier: #"ITMShippingAddressVC"];
Set the Identifier in your storyboard.. click on your view, and in the right properties menu set the Storyboard ID
Than you can push like normal:
[[self navigationController]pushViewController:viewController animated:YES];
While this will work for you-- when using Storyboards, ideally you want to use Segues. It requires much less code. Here is a good tutorial on using segues to push/present view controllers and pass data between them.
You have created your VC's, but they are not linked to the storyboard ITMOrderedItemsVC and ITMShippingAddressVC. So the interface that you added in storyboard doesn't get loaded.
What you could do is create a separate xib file for each of your VCs (New > file... > User Interface > View). Name the xib file the same as your viewController:
ITMOrderedItemsVC.xib
ITMShippingAddressVC.xib
Design your interfaces in these xib files (they are just like single-entity storyboards).
The xibs will be automatically loaded when you init an instance of the respective viewController.
This will work with your current code.

Launch a Uitableview controller from UIbar buttons created dynamically

- (void)createBarButtons
{
UIBarButtonItem *myCheckButton = [[UIBarButtonItem alloc] initWithTitle:#"Check Records" style:UIBarButtonItemStylePlain target:self action:#selector(checkRecordsAction)];
UIBarButtonItem *mySaveButton = [[UIBarButtonItem alloc] initWithTitle:#"Save" style:UIBarButtonItemStylePlain target:self action:#selector(saveAction)];
[mySaveButton setTintColor:[UIColor colorWithRed:34.0/255.0 green:97.0/255.0 blue:221.0/255.0 alpha:1]];
NSArray *myButtonArray = [[NSArray alloc]initWithObjects:mySaveButton, myCheckButton,nil];
self.navigationItem.rightBarButtonItems = myButtonArray;
}
I dont know if this question falls under too localized category. But help me out here.So like you see i have created two bar button items. Save is just saving the data onto CoreData,works just fine. But the check records should launch a new UITableviewcontroller.
- (void)checkRecordsAction
{
NSLog(#"the new stack action");
ITMSyncRecordsTVC *syncRecords = [[ITMSyncRecordsTVC alloc]init];
// [self presentViewController:syncRecords animated:YES completion:^{
// self.navigationController.view.superview.bounds = CGRectMake(0, 0, 250, 250);}];
[self.navigationController pushViewController:syncRecords animated:YES];
}
ITMSyncRecordsTVC is a TableViewController with a "Back" button on it.So when i click the check records it launches a tableview controller but no values in it and it does not show the "Back" bar button i put on it. Until now i have been using segues and storyboards just fine. But how do i launch a new view controller without them i dont know. My first leap into ios is ios6. I am missing something I dont know. So let me know how to call/launch a new TableViewController. In android we had intents that did the trick. Please let me know if you need more information. Thanks...
EDIT: So i edited my checkRecordsAction code.
EDIT :
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
ITMOrdersVC *ordersVC = [[ITMOrdersVC alloc]init];
NSLog(#"at line 188 %d",indexPath.row);
if(indexPath.row < self.salesOrdersArray.count)
{
ordersVC.salesOrder = [self.salesOrdersArray objectAtIndex:indexPath.row];
NSLog(#"the sales purchase order number is %#",ordersVC.salesOrder.purchaseOrderNumber);
NSLog(#"done - 140");
[self.navigationController pushViewController:ordersVC animated:YES];
}
}
So on selecting a row on ITMSyncRecordsTVC table view controller it does the above. I get a new ITMOrdersVC screen with "Back" bar button at the left and 2 dynamically generated bar buttons.I get the 2 dynamically generated bar buttons but not the back. I thought once i click the row it will "go back" to previous screen to which i am passing the salesOrder object. My next step was to check if i get the salesorder object from the TVC then load it. So basically 2 screens only. First screen (save,check sync records). Second screen click a.) back(go to first screen..do nothing) or b.)click a row in second screen and populate first screen without the bar button.If it is not clear please ask me.
To launch a new table view controller using storyboards, you want to:
Have your main scene embedded in a navigation controller.
You want to have a push segue from the main controller to the second one. So control-drag from the view controller icon (in the bar below the main scene) to the next scene:
It should then look like (note the appearance of the navigation bar in the destination table view):
Then select the segue and give it a "storyboard identifier":
And now, your code to transition to that scene would look like:
The checkRecordsAction:
- (void)checkRecordsAction
{
NSLog(#"the new stack action");
[self performSegueWithIdentifier:#"PushSegue" sender:self];
}
Update:
By the way, in the interest of full disclosure, there's an alternative to the push segue. If you give that next scene, itself, a "storyboard id", you can use the following code (obviously replacing "insertNextScenesStoryboardIdHere" with the identifier you give your next scene:
- (void)checkRecordsAction
{
NSLog(#"the new stack action");
UIViewController *controller = [self.storyboard instantiateViewControllerWithIdentifier:#"insertNextScenesStoryboardIdHere"];
[self.navigationController pushViewController:controller animated:YES];
}
I personally don't like this as much, as your storyboard now has a scene floating out there without any segue, your code now has dictated the nature of the transition between view controllers vs having everything in the storyboard, etc., but it is another approach.

How to use a UISplitTableViewController correctly to display two UITableViewControllers?

I am (still) in the process of converting an iPhone app to a universal app.
I want to push a UISplitViewController onto a UIView. As discussed here I am trying to create this flow:
UIView -> UISplitViewController (containing two UITableViews that I use in the iPhone version) -> UIView
I want the button attached to this IBAction to make the tableview appear on iPhone (which is all set up and working) and the SplitViewController appear on the iPad:
-(IBAction)makeStory:(id)sender{
NSLog(#"makeStory:");
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
//Code here to push split view.
} else {
//I am an iPhone!
makeStoryTableViewController = [[MakeStoryTableViewController alloc] initWithNibName:#"MakeStoryTableViewController" bundle:nil];
[self.navigationController pushViewController:makeStoryTableViewController animated:YES];
[self.navigationController setNavigationBarHidden:NO animated:YES];
}
}
I've read tutorials here and here - but I can't get my head around how to add it to an existing app correctly. I would appreciate some help / direction so I can implement a UISplitViewController correctly in this universal app.
You can't "push" a split view controller or add it on top of another view. It has to be the root view controller of the window.
In your IBACtion you could do something as simple as:
appWindow.rootViewController = aSplitViewController;
Of course, you have to get a reference to your app's window, and you have to get aSplitViewController which is initialized with left and right view controllers from somewhere...

Resources