I have this application which uses internally a UISplitViewControler to display the main interface. The problem I have is that on IOS7 I don't see the button on the left to open the master panel.
The theory says that I have to set the delegate and the button will appear. In practice - my delegate is not called in IOS7. It does on IOS8.
First try:
I am following the normal double navigation controller scheme (described here: http://whoisryannystrom.com/2014/11/17/UISplitViewController-iOS-7/)
Code is swift :)
As I need my app to work on IOS7 phones, in am not creating the split view controller in code, but using the one in the storyboard:
(somewhere in app delegate):
UIStoryboard *board = [UIStoryboard storyboardWithName:#"Storyboard" bundle:nil];
UIViewController *newController = [board instantiateViewControllerWithIdentifier:#"LoginViewController2"];
self.window.rootViewController = newController;
The delegate is created in the master, and assigned to master. This works on IOS8.
Code in the master
override func akaweFromNib() {
super.awakeFromNib()
if let splitViewController = self.splitViewController {
let navigationController = splitViewController.viewControllers[splitViewController.viewControllers.count-1] as UINavigationController
if (splitViewController.respondsToSelector(Selector("displayModeButtonItem"))) {
navigationController.topViewController.navigationItem.leftBarButtonItem = splitViewController.displayModeButtonItem()
}
splitViewController.delegate = self
}
}
This works, but I have to open the drawer and choose something on the master view (create a new segue) in order to see the button.
Second try
As this did not work - I created a new UISplitViewController and set the split view controller on the storyboard to this new class. Move the onWakeFromNib to this new class (and set the delegate as before). New code works on IOS8, but under IOS7 (at least on the IPad Emulator) the new class is not used for the split view controller - I don't hit a breakpoint in the new code.
What am I doing wrong?
Edit:
While copying code here, I forgot to mention that I am doing:
navigationItem.leftItemsSupplementBackButton = true
navigationItem.leftBarButtonItem = splitViewController?.displayModeButtonItem()
But - this is only available in IOS8. What can I do in IOS7?
2015-02-12 10:37:55.987 OlympiaTracking[92551:607] -[UISplitViewController displayModeButtonItem]: unrecognized selector sent to instance 0x7b67f1c0
Edit 2:
I also followed ios7 no displayModeButtonItem or targetDisplayModeForActionInSplitViewController which works, but only after the first segue. When the controller is first displayed, the button is not visible.
Open this link and move to the iPad part. Where it says
Notice that when the iPad app is first opened up, there is no indication that this is a split view controller at all! To trigger the Master view controller, the user has to magically know to swipe left to right.
Even when the navigation controller is in place, the UI is not that
much better at first glance (although seeing a title is definitely an
improvement):
Related
Question
In a UISplitViewController collapsed display, how can I programmatically get back to master view controller?
Detail
I googled it but found no solution. Not sure if I was using the right keyword. This is how I show the detail view controller:
[self showDetailViewController:[[UINavigationController alloc] initWithRootViewController:detail] sender:self];
I also tried these 3 methods respectively, but none of them worked:
if (self.splitViewController.collapsed) {
UIBarButtonItem *backButtonItem = self.navigationItem.leftBarButtonItem;
(1):[backButtonItem.target performSelector:backButtonItem.action];
(2):[[UIApplication sharedApplication] sendAction:backButtonItem.action to:backButtonItem.target from:nil forEvent:nil];
(3):objc_msgSend(backButtonItem.target, backButtonItem.action);
}
navigation items set like thie in detail VC viewDidLoad:
self.navigationItem.leftBarButtonItem = self.splitViewController.displayModeButtonItem;
self.navigationItem.leftItemsSupplementBackButton = YES;
Alright, I have found a solution that seems to work. I have tested it on iPhone 6 and iPhone 6 Plus, but I only just discovered it thirty minutes ago, so It might have some unfortunate side effect which I have not run into yet.
It's in swift. I hope it's clear though. Let me know if you need me to provide it in Objective-C instead.
if let splitViewController = splitViewController {
if splitViewController.collapsed {
let viewControllers = splitViewController.viewControllers
for controller in viewControllers {
// PrimaryNavigationController is the navigation controller I use
// as the split views master view, which is also set as its delegate
// but it could be any UINavigationController that is the
// primary controller of the split view
if controller.isKindOfClass(PrimaryNavigationController) {
controller.popViewControllerAnimated(true)
}
}
}
}
I call this from my detail view when I want to dismiss it.
The code works by checking if the split view controller is collapsed, which is the only state where popping the detail view makes sense (to me anyways). Then it simply looks for the navigation controller currently in play in the split view controller and asks it to pop it's top view controller. This works because when in collapsed mode, the split views master view is the only view controller in the stack. The detail view is collapsed "into" it, and therefore becomes the current top view controller of it, thus is the one that gets popped.
Seems to work. Let me know if it do for you too.
I was looking to do exactly the same, and this code worked for me. I put it in the detail view, hooked up to a button in the navigation bar.
In my application the detail view can segue to itself a number of times and this code gets one back to the master view no matter how deep down the line it gets.
#IBAction func unwindSegueId(sender: AnyObject) {
if (self.splitViewController!.collapsed) {
self.splitViewController!.viewControllers[0].popToRootViewControllerAnimated(true)
}
}
This seems to work (provided you have a navigation controller in your master pane)
if (self.splitViewController.collapsed) {
[(UINavigationController *)self.splitViewController.viewControllers[0]
popToRootViewControllerAnimated:YES];
}
I am trying to replace the detailview of a UISplitViewController for a quite a while now, but the solutions I found on the internet wasn't useful.
I am executing this:
DetailViewController* secondVc = [[DetailViewController alloc] init];
NSMutableArray* arr = [[NSMutableArray alloc] initWithArray:self.splitViewController.viewControllers];
[arr replaceObjectAtIndex:1 withObject:secondVc];
[self.splitViewController setViewControllers:arr];
DetailViewController is just a normal UIViewController (is this the problem?) I chose red as its background but I am seeing a completely gray area in the detail view after this code is executed.
What surprises me is that viewDidLoad and viewDidAppear functions are called for the DetailView class, but I can't see it on the screen. The self.view.frame is 0,0,768,1024 although all my settings are in landscape mode in storyboard.
I only want to use this in landscape mode, I don't need a generic solution.
What is the most basic way to change the detail view of a split view controller? I have looked at Apple's MultipleDetailViews but that felt like overkill since most of the code in it is about responding orientation changes, like hiding the master vc etc.
I suspect your problem is using alloc init to instantiate secondVC -- that would work if you made your controller's view in code, or in a xib with the name "DetailViewController". Since it appears that you're using a storyboard, then you should be using,
DetailViewController* secondVc = [self.storyboard instantiateViewControllerWithIdentifier:#"secondVC"]; // be sure to use this same identifier in the storyboard
In storyboard, select the view controller. On the right side, go to "Simulated Metrics" and pick "Detail" for "Size". As for the color, try setting it in viewDidLoad.
I'm working on an app which uses Facebook integration, and the log in system works fine now. However, I can't seem to return to my initial view controller when the user clicks log out.
Here's an overview of my storyboard:
I would like to return to the start when the user clicks the blue button (on the top). What would I do to achieve that? As you can see I have multiple Navigation Controllers, and I only use Push-seguesto get there. However, I do use the SWRevealViewController, which you can see in the middle.
I've tried [self.navigationController popToRootViewControllerAnimated:YES]; which doesn't do anything.
Any advice? Anyone familiar with the SWRevealViewController and what it might have done to my Navigation stack? Any help will be appreciated!
Thanks.
Try this,
UIStoryboard* storyboard = [UIStoryboard storyboardWithName:#"NameOfYourStoryBoard"
bundle:nil];
LoginViewController *add =
[storyboard instantiateViewControllerWithIdentifier:#"viewControllerIdentifier"];
[self presentViewController:add
animated:YES
completion:nil];
Write Below Method in root viewcontroller
- (IBAction)returnToDashboard:(UIStoryboardSegue *)segue;
Give segue connection to destination view controller like below
Give identifier to segue and assign method to that segue
use below method in destination view controller
[self performSegueWithIdentifier:#"pushtoDashboard" sender:self];
First of all, I don't think you need that many UINavigationControllers. Using only one in your application should be enough.
The reason popToRootViewController is not working in your case is because it will go to the first view controller withing a UINavigationController. You have nested many UINavigationControllers, thus when you click the blue button in the settings view controller it will go to the sidebar view controller (can't read it properly, the image is small).
You can do the following to get to the root view controller of your app:
UINavigationController *rootController =[(UINavigationController*)[(AppDelegate*)
[[UIApplication sharedApplication]delegate] window] rootViewController]];
Replace AppDelegate with however it's called in your app.
But my advice is to remove all intermediate UINavigationControllers. Then just doing popToRootViewController should do the trick.
Problem
You'd like to return from a view controller (source) to the start (a
destination view controller) when the user clicks the blue button (on the
top)
Recommendation
I recommend you take a quick look at the highly rated SO answer which demonstrates how to use the unwind segue (that's the little green exit box on your view controller in the storyboard). Unwind segues are the modern way of accomplishing your goal, but you can also call dismissViewController:animated from the source controller. You should also take a quick read of a very small Apple note (TN2298) on using the unwind segue.
Essentially you will want to add the following method to your destination view controller:
- (IBAction)unwindToMainMenu:(UIStoryboardSegue*)sender
{
}
Then use ctrl+drag and click from the blue button down to the green exit icon on the source view controller. This will popup a menu and you can select unwindToMainMenu from the list. You will need to give the new segue an identifier in the Identity Inspector (e.g. segueToMain).
Manual Unwind
The technical note above (TN2298) will also show you how you can create a manual unwind segues that may be called programmatically (similar to how one might say performSegueWithIdentifier...).
I was working on a very similar problem. I am using a storyboard with a navigation controller & implemented the highly recommended SWRevealViewController, with iOS 7 & Xcode 5.1. I tried unsuccessfully to implement some of the solutions mentions. Either the screen didn't change or I got blank table. I used a hybrid version of the programatic examples provided in SWRevealController & other answers here to get a working solution. I added this as apart of my login button action
UIStoryboard *storyBoard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
InboxTableViewController *viewController = [storyBoard instantiateViewControllerWithIdentifier:#"inbox"];
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:viewController];
[self.revealViewController pushFrontViewController:nav animated:YES];
I retrieved my storyboard & initiated the view controller I wanted from the storyboard & then added to a navigation controller. Finally I used the SWRevealViewController method to Push the view I desired to the front.
I'm using swift and what worked for me was this:
var loginVC: UIViewController? = self.storyboard?.instantiateViewControllerWithIdentifier("UILogin") as? UIViewController
self.presentViewController(loginVC!, animated: true, completion: nil)
When you have different storyboards simply "presenting" the required VC from the initial storyboard does the trick:
Swift 3
if let loginVC = UIStoryboard(name: "Login", bundle: nil).instantiateInitialViewController() {
present(loginVC, animated: true, completion: nil)
}
In some cases there might be leaking UITransitionView's. You might remove them right after the "presenting" code, but not before it and not in it's completion:
if let subviews = UIApplication.shared.keyWindow?.subviews,
let transitionViewClass = NSClassFromString("UITransitionView") {
for subview in subviews where subview.isKind(of: transitionViewClass) {
subview.removeFromSuperview()
}
}
<– This works as of Xcode 8.2.1 and iOS 10.2 but no guarantee if will work forever. Be careful with it.
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.
I was playing around with iOS 5 and storyboards today. I currently have it so that the main storyboards starts with a uitabbarcontroller then a navigationviewcontroler and finally a uiviewcontroller. All that works fine.
What I'm looking for is how to dynamically set which viewconotroller the uitabbarcontroller is displaying when the application starts. So I'd want to use CoreData to see if a table was empty and it it was select the second viewcontroller (tabbar item 2) and if not select the first viewcontroller (tabbar item 1).
Since the storyboard is handling what is being displayed, I wasn't sure how in the app delegate I could set this?
Hoping someone can point me in the right direction here!
Thanks!
Your app delegate will have a window property. That can be used to get a pointer to the storyboard's initial view controller (which will be your UITabBarController), like this example from one of my app delegates application:didFinishLaunchingWithOptions:
UITabBarController *tabController =
(UITabBarController *)self.window.rootViewController;
tabController.selectedIndex =
[defaults integerForKey:kOptionLastTabSelectedKey];
tabController.delegate = self;
For me I can access the tabbar using self.navigationController.parentViewController;
This always return the tabbar controller.