I'm trying to add a split view controller to my existing project so that it shows over the existing content in a new window.
The template project from Apple works as expected. For testing, I simply copied the storyboard items from the template project onto my own storyboard, gave the splitViewController a storyboard identifier and copied the classes.
When a plus phone is turned landscape it shows the master and detail side-by-side properly. However, when I tap on a master entry it pushes a new detail controller instance over the master content instead of using the secondary detail view on the right for the content.
This is how I show show the splitViewController:
guard let splitViewController = storyboard.instantiateViewController(withIdentifier: "MasterViewController") as? UISplitViewController else { return }
splitViewController.delegate = self
splitViewController.preferredDisplayMode = .automatic
self.conversationWindow = UIWindow(frame: UIScreen.main.bounds)
self.conversationWindow?.windowLevel = UIWindowLevelNormal + 0.1
self.conversationWindow?.rootViewController = splitViewController
self.conversationWindow?.makeKeyAndVisible()
Before tapping entry:
After tapping entry:
Storyboard:
Anyone ever experience this?
Found my answer. Seems copying the views changed the segue type from showDetails to show
Related
I have a navigation controller and several view controllers. When I click on a table cell in the Stocks View Controller, I open the Stock Chart View Controller. In it I have 3 buttons "Chart", "Summary", "News". I want to click on "Summary" to move to the Stock Summary View Controller, but an error occurs for several dozen lines. How can I implement transitions in such a menu?
And how do I click on" back" in Stock Chart View Controller and Stock Summary View Controller to return to Stocks View Controller?
Code for switching from the Stock Chart View Controller to the Stock Summary View Controller:
#IBAction func onSummaryButtonTapped(_ sender: Any) {
let storyboard = UIStoryboard(name: "Main", bundle: .main)
let viewController = storyboard.instantiateViewController(identifier: "StockSummaryViewController") as! StockSummaryViewController
navigationController?.pushViewController(viewController, animated: true)
}
Storyboard:
enter image description here
Your App:
NavigationVC > StocksVC (tapping on a cell) ➡ StockChartVC > StockSummaryVC > etc.
➡ is important here, how do you navigate from StocksVC to StockChartVC? Do you push StockChartVC or present modally? Since you do segues programmatically, it can't bee seen in the image.
I can provide the following info to help you:
The push-style navigation automatically contains/adds controls for backward navigation because it adds all ViewControllers to its navigation stack; all ViewControllers will be embedded in the same NavigationController
In case of presenting scenes modally, these ViewControllers have to have their own NavigationController. thus, your StockChartVC has to be embedded in its own NavigationController
I believe you do the push-style segue from StocksVC to StockChartVC and also pushing from StockChartVC to StockSummaryVC; in that case, there should be no problem at all. Moreover (as I wrote), all they VCs will be added controls for backward navigation. Everything will be working just fine!
So pay attention how you are segueing and according to that, implement the right navigation.
I would like to emulate contact list in ios. When one taps + button to add contact, a new view controller is presented that gives you textfields to enter contact name and additional info for saving about that contact. Once you tap Done button, the next view controller presented already seems to be embedded in a navigation controller with the button that takes you back to the contact list. I've tried code found on here but it pushes 3 view onto navigation stack
//first attempt
var controllers = navigationController?.viewControllers
controllers?.append(secondVc)
controllers?.append(thirdVC)
navigationController?.setViewControllers(controllers!, animated: true)
//second attempt
let pushVC = UIViewController()
let backVC = UIViewController()
if let navigationController = navigationController {
navigationController.pushViewController(pushVC, animated: true)
let stackCount = navigationController.viewControllers.count
let addIndex = stackCount - 1
navigationController.viewControllers.insert(backVC, atIndex: addIndex)
}
I've also tried other combinations that look cringy. Here is what I want it to look like: https://imgur.com/a/IAJ5G
This should work:
navigationController.pushViewController(secondVc, animated:false)
navigationController.pushViewController(thirdVc, animated:true)
Apple does the same by presenting a new window, with the form to create a new contact.
After making the new window as the keyWindow, they push the contact details VC on to the navigation controller on the original window. This push happens in the background, which the user is not able to witness.
You can view the same by attaching a debugger to the Contacts app.
Heres a screenshot of the view hierarchy. You will be able to see that there are no view controllers from the original window underneath the CNContactContentViewController.
When the user taps Done, the original window is restored as the keyWindow, and the contact details VC is updated to show the newly added contact.
I have three storyboards in my project. There is a main and two separate workflow storyboards. Each storyboard is embedded in its own navigation controller.
Since I have broken up the storyboards into workflows I have to programmatically add each workflow to the tab bar. This is working correctly.
My issue occurs when I try and push a view (within a workflow) onto the workflows navigation controller. It seems that the navigation controller for a workflow is never being used. I verified this by changing the navigation bar color for each workflow.
I have tried two options, each set up on a workflow.
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
let mainStoryboard = UIStoryboard(name: "Workflow 1", bundle: nil)
let actionItemStoryboard = UIStoryboard(name: "Workflow 2", bundle: nil)
let workflow1Controller = mainStoryboard.instantiateViewController(withIdentifier: "navigationController1")
let workflow1TabItem = UITabBarItem(title: "Item 1", image: nil, selectedImage: nil)
workflow1Controller.tabBarItem = workflow1TabItem
let workflow2Controller = actionItemStoryboard.instantiateViewController(withIdentifier: "workflow2")
let workflow2TabItem = UITabBarItem(title: "Item 2", image: nil, selectedImage: nil)
workflow2Controller.tabBarItem = workflow2TabItem
self.viewControllers = [workflow1Controller, workflow2Controller]
}
Option 1
Setting the tab view controller to point to the navigation controller (pretty sure this is incorrect but was a test I did). This displays the views how I want but doesn't show the navigation item (back button).
Option 2
Setting the tab view controller to point to the list view (see screen shots below). This displays the back button but upon clicking on the cell the last view is displayed "over" the tabs.
Main Storyboard
Workflow 1 Storyboard
Workflow 2 Storyboard
In order to solve this I ended up doing the following:
Remove the navigation controllers from the workflows
Create a "MockTabController" (just a UIViewController with a UITabBar placed on the bottom of the view)
Now that I have a UIViewController with a UITabBar I can have each workflow view extend this view instead of a UIViewController and thus a tab bar is consistent throughout my app (where I want it).
For instances where the workflow has a UITableViewController I simply embed the UITableViewController inside a ViewController as a child view. The ViewController then extends my MockTabController and the result is a TableViewController with a tab bar that doesn't need any modifications to work.
In order to simplify the navigating throughout the app, I simply reset the navigation stack back to the beginning of the tab controller. Clicking a tab bar item unwinds all the workflow and then pushes the start of a new workflow.
I have been scratching my head trying to figure out how to do this. If anyone has some insight it would be greatly appreciated. I've attempted to do this using segues and push/presentViewController methods. With pushViewController nothing happens.
Scenario: Split view controller has two navigation controllers connected (one as master, one as detail). The master's navigation controller has a form with various cells that should control what is being displayed in the right hand side detail view when in landscape mode on the iPad. The navigation controller connected to the detail view has storyboard references connected to it (3 of them).
What I want to do: From the master view controller (which is the app menu), I would like to control what is being displayed in the detail view while maintaining navigation bar.
Attempt 1:
let detailVC = self.splitViewController!.viewControllers[1]
let newVC = UIStoryboard(name: "D", bundle: nil).instantiateViewControllerWithIdentifier("P")
detailVC.self.navigationController?.pushViewController(newVC, animated: true)
Attempt 2:
let detailVC = self.splitViewController!.viewControllers[1]
let newVC = UIStoryboard(name: "D", bundle: nil).instantiateViewControllerWithIdentifier("P")
detailVC.performSegueWithIdentifier("navP", sender: self)
One other related question I had...if a user does many hops between several of the menu options, how can one "reset" the back button's history in the navigation bar to prevent a case where clicking back will cycle you through the same several views?
You shouldn't be pushing the view controller or performing a segue but calling showViewController.
Do you definitely need to maintain the navigation bar or can you show different UINavigationBars (via showing your view controller embedded in an UINavigationViewController potentially)?
Alternatively just show a single view controller and use the logic you add in your view controller to change the content under the control of your master view controller.
After some experimenting with the best approach I solved this. :) Solution below for all devices (iPhone/iPad).
Define extension for UISplitViewController:
Modified version based off https://stackoverflow.com/users/4418308/santiago-bendavid
extension UISplitViewController {
func toggleMasterView() {
if UIScreen.mainScreen().bounds.height > UIScreen.mainScreen().bounds.width {
var nextDisplayMode: UISplitViewControllerDisplayMode
switch(self.preferredDisplayMode){
case .PrimaryHidden:
nextDisplayMode = .AllVisible
default:
nextDisplayMode = .PrimaryHidden
}
UIView.animateWithDuration(0.2) { () -> Void in
self.preferredDisplayMode = nextDisplayMode
}
} else {
// do nothing
}
}
}
Code in master navigation controller's root view controller:
let newVC = UIStoryboard(name: "some_storyboard_id", bundle: nil).instantiateInitialViewController()
if self.splitViewController!.viewControllers.count == 2 {
let detailVC = self.splitViewController!.viewControllers[self.splitViewController!.viewControllers.endIndex - 1]
if detailVC.childViewControllers[detailVC.childViewControllers.count - 1].restorationIdentifier! != "some_id_here" {
detailVC.childViewControllers[0].navigationController?.pushViewController(newVC!, animated: true)
}
self.splitViewController!.toggleMasterView()
self.navigationController?.splitViewController!.preferredDisplayMode = .Automatic
} else {
self.navigationController?.pushViewController(newVC!, animated: true)
}
This code works on regular iPhone (non-Plus) where the master view controller is the sole controller used for navigation. On iPads and iPhone 6/6s+ models (which behave same as an iPad) I check whether the current view is already present using the restoration ID property, and then present the new view if it's not the same as the one already rendered on screen and dismiss the master view controller if we're in portrait mode. If in landscape, we keep it on screen (default behavior).
i have created uitabbarcontroller which is not properly working,my issue is if i click second tab bar the corresponding view is not visible only the first view is visible for all actions.i couldn't navigate and see the next views.
my code is :
if (this.tabBarController == null)
{
this.tabBarController = new UITabBarController ();
}
var viewController1=new Filterview();
var viewController2=new SearchView();
tabBarController = new UITabBarController ();
tabBarController.ViewControllers = new UIViewController []
{
viewController1,
viewController2,
} ;
viewController2.TabBarItem = new UITabBarItem (UITabBarSystemItem.Search, 1);
viewController1.TabBarItem = new UITabBarItem ("Filter", UIImage.FromFile ("Images/1382614124_filter.png"), 0);
this.NavigationController.PushViewController (this.tabBarController, true);
this code is not working pl anybody suggest me how to create uitabbar in iOS xamarin mono touch applications.
UITabBarController is a root controller. This means it is supposed as the topmost controller in your hierarchy. You are pushing this controller on a UINavigationController.
This is most probably causing the issues you see.
Try to set your tab bar controller in your app delegate:
window.RootViewController = someTabBarController;
Let me quote from Apple's documentation:
When deploying a tab bar interface, you must install this view as the
root of your window. Unlike other view controllers, a tab bar
interface should never be installed as a child of another view
controller.