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.
Related
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):
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've been looking this up for a while now, it might have a simple answer:
According to the Apple docs, past ios6, we can subclass UINavigationController. How do we perform a segue from identifier when it prevents anything that isn't a UINavigationController. Mainly:
uncaught exception 'NSGenericException', reason: 'Could not find a
navigation controllerfor segue 'profileSegue'. Push segues can only
be used when the source controller is managed by an instance of
UINavigationController.
I'm using JaSidePanels and my center panel (navigation) needed to be subclasses for a delegate as there is a menu on the left panel that I want to switch the views when clicked.
#interface CenterViewController : UINavigationController <MenuDelegate>
Basically, since this object is a CenterViewController at runtime, is there a way to cast it to its superclass? I've tried [self superclass] but that didn't work (same error).
I need to call this code in the CenterViewController. Is it possible to move it to UINavigationController?
- (void)viewDidLoad
{
RootViewController *rootViewController = (RootViewController *)[[[[UIApplication sharedApplication] delegate] window] rootViewController];
MenuViewController *leftViewController = (MenuViewController *)rootViewController.leftPanel;
// Store a reference to the center view controller in the left view controller's delegate property
leftViewController.menuDelegate = self;
[super viewDidLoad];
}
- (void) MenuItemSelected: (NSString*) item
{
if ([item isEqualToString:#"Home"]) {
//These would throw the error since we're not technically a "UINavigationController"
//[self performSegueWithIdentifier: #"mapViewController" sender: nil];
} else if ([item isEqualToString:#"Profile"]) {
//[self performSegueWithIdentifier: #"profileSegue" sender: self];
}
}
EDIT:
More information with pictures.
I'm curious as to how navigation controllers should work with side panels. I've looked at many other plugins for sidepanels and here is an example. Even though it works, why does it have 2 nav controllers?
Here is our current setup:
So basically, am I thinking about this wrong in the sense that I want to push a new VC to the existing NavVC? Would it be better to push a new NavVC when a menu button is pressed? What would happen when we go into a subview from the Maps view. Would the menu be accessible via sliding?
If you look carefully at the message you will see that your problem isn't caused by subclassing UINavigationController it is because you are executing the segue against your (subclassed) UINavigationController. Push segues are executed against a UIViewController that is embedded in or managed by a UINavigationController, with the system then finding the managing UINavigationController via the view controller's navigationController property in order to execute the push.
The message you have received says
...Push segues can only
be used when the source controller is managed by an instance of
UINavigationController
In your case the source controller is an instance of UINavigationController, it isn't managed by a UINavigationController.
You haven't said exactly how your app navigation works, but I have a suspicion that a UINavigationController isn't the right tool to use anyway. If you are using the side menus to allow the user to select the central content in a random way (i.e. the user could select the first option then the fifth and then go back to the first) then you should probably just have a central view into which you present the selected view. Pushing views onto a UINavigationController will end up with a large 'stack' of views unless you pop the current view controller before pushing the new one, which is even more complicated and not the visual effect you are looking for.
You can still achieve the 'push' style transition but you will need to use a custom segue. If it were me I would probably push from the left if the user selected a menu item that was closer to the top than the current option and from the right if the new item was closer to the bottom than the current, but again I am making assumptions on how your app navigation works.
UPDATE
Yes, I think you are on the right track with the section of your updated question. Navigation controllers are for navigating a series of related views in a hierarchical manner - think of the Settings app - you select "general" or "wall paper" or whatever - each of these then has a series of views that you can navigate through; up and down a stack.
In your app it looks like home, profile and settings should each be navigation controllers. Your root view would then just be a view. Your menu would select which view controller to present in the root view - this is like a tab bar controller, except your menu takes the place of a tab bar.
You can allocate your home, profile & settings view controllers in your appDelegate and store them to properties of the appDelegate. Then you can use something like this:
- (void) MenuItemSelected: (NSString*) item
{
myappDelegate *app=(myappDelegate *)[UIApplication sharedApplication].delegate;
[delegate.currentViewController removeFromParentViewController];
UIViewController *newController;
if ([item isEqualToString:#"Home"]) {
newController=app.homeViewController;
} else if ([item isEqualToString:#"Profile"]) {
newController=app.profileViewController;
}
if (app.currentViewController != newController)
{
[app.currentViewController removeFromParentViewController];
[app.rootViewController addChildViewController:newController];
app.currentViewController = newController;
}
I make a Tabbed Application using storyboard template, two view controllers are embedded.
This is what I want to do: in the first viewController, let TabBar to select the second viewController programmatically.
The first viewController is a tableViewController, shows a list of items, and each item will push to a detailViewController. In the detailViewController, I edit some information and save the item. Then I want app to show the second ViewController, which is a tableViewController shows saved item.
Usually, we can use [TabBarController setSelectedIndex:1]; to select the second viewController.
However, since this is a storyboard template application, so many code are hidden behind. So I cannot get the TabBar instance in the first viewController, and use setSelectedIndex method.
This is what confuses me...
And now, I have found the solution for this problem. My answer is below.
I have figured out how to solve this problem.
First I add new a class: MyTabBarController.
Then, in storyboard, select the Tab Bar Controller, in identity inspector panel, set the custom class to this new class.
For the first viewController class, add a property
#property (nonatomic, weak) UITabBarController *tabBarController;
Then add - (void)viewDidAppear:(BOOL)animated in MyTabBarController class:
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
UINavigationController *navigationController = [self.viewControllers objectAtIndex:0];
FirstViewController *firstViewController = (FirstViewController *)navigationController.topViewController;
firstViewController.tabBarController = self;
In this way, I pass the tabBarController instance to the firstViewController, so, in the firstViewController, I can call [tabBarController setSelectedIndex:1];
Storyboard gives me a visual interface, however, it hides so many things behind.
I've got a project setup using a Storyboard that contains a UITabViewController as the initial root view. One of the tabs loads a NavigationController that in turn loads a custom view controller class.
From the custom view controller, I have a navigation bar button that I want to trigger an action that returns the root UITabViewController to it's first index. I've been able to do this using a traditional xib structure by adding the appDelegate class to the xib and linking a method to the button that way.
Effectively, I want the button to trigger code that looks something like this:
#implementation AppDelegate
#synthesize window = _window;
#synthesize tabBarController=_tabBarController;
-(IBAction)handleHome:(id)sender{
//How do I send a message to the tabBarController?
[self.tabBarController setSelectedIndex:0];
}
Is it possible to do this with the Storyboard approach? I looked at Segue's but that doesn't seem to be what I'm trying to do (there is no way for me to talk to the root UITabViewController from what I can see).
I've got the handeHome method being triggered using the Responder approach, so really all I need to know is how to access the instantiated tabViewController in the Storyboard.
Hopefully this question makes sense, let me know if there is anything I should expand on.
Why not just do this in your custom view controller?
- (IBAction)handleHome:(id)sender {
self.tabBarController.selectedIndex = 0;
}
The tabBarController property is built in to UIViewController.
I figured it out. I updated the quoted block of code to this:
#implementation AppDelegate
#synthesize window = _window;
-(IBAction)handleHome:(id)sender{
UITabBarController *tabViewController = (UITabBarController *) self.window.rootViewController;
[tabViewController setSelectedIndex:0];
}
Sigh... need more coffee before asking questions on SO