UISplitViewController shows detail view first - ios

I am very new to iOS development. In my app I have a tab bar and in one of the tabs I have a UISplitViewController. My issue is that when I go to the tab it shows the Detail view first. Then I have to click the back button to get the the master view. I have found one other person having this issue on stackoverflow, but the solution was in Objective-c and I am using the storyboard (not sure how to attach a class to it) and swift, so that did not help.
It also does not work when using an ipad in portrait mode. When I shift to landscape it works fine, but just shows a black screen (no back button) in portrait mode. Any help would be appreciated. Thanks.
I am not sure what other info you need or what you want me to show, so let me know if I left something out.
adjusted appdelegate
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
let tabBarController = self.window!.rootViewController as! UITabBarController
let splitViewController = tabBarController.viewControllers![3] as! UISplitViewController
///////////////////Always visible property
splitViewController.preferredDisplayMode = .AllVisible
///////////////////
let navigationController = splitViewController.viewControllers[splitViewController.viewControllers.count-1] as! UINavigationController
navigationController.topViewController!.navigationItem.leftBarButtonItem = splitViewController.displayModeButtonItem()
splitViewController.delegate = self
return true
}
Update: ended up fixing following this answer Open UISplitViewController to Master View rather than Detail

I have created a sample SplitViewController in the project and set the property in the appdelegate. This works for me https://github.com/harsh62/stackoverflow_TestMasterDetailApp
splitViewController.preferredDisplayMode = .AllVisible
The full function is as follows:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
let splitViewController = self.window!.rootViewController as! UISplitViewController
///////////////////Always visible property
splitViewController.preferredDisplayMode = .AllVisible
///////////////////
let navigationController = splitViewController.viewControllers[splitViewController.viewControllers.count-1] as! UINavigationController
navigationController.topViewController!.navigationItem.leftBarButtonItem = splitViewController.displayModeButtonItem()
splitViewController.delegate = self
return true
}
References:
UISplitViewController - set always visible master controller when

Related

How to set a UITabBarController as rootViewController from Storyboard

Short question:
How can I launch and make a UITabBarController be the rootViewController of my app after starting with a Storyboard?
Long question:
I'm not a swift expert, but I managed to create a complete app using XIBs from the beginning. Now I need my app to start with a Storyboard as a new requirement to post updates to the appstore from 01/07/2020, but I never used it to build my views. It was easy to modify my app to have my Storyboard as an entry point, but the problem is that my initial view today is a TabController, and I don't know how to navigate from my initial Storyboard to my TabController.
My AppDelegate today works something like this:
var window: UIWindow?
func application(_application: UIApplication, didFinishLauchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
self.window = UIWindow(frame: UIScreen.main.bounds)
// initiate UINavigationControllers and UITabBarController here...
tabController.viewController = [nav1, nav2, nav3, nav4]
tabController.modalPresentationStyle = .fullScreen
self.window!.rootViewController = tabController
self.window!.makeKeyAndVisible()
}
All my attempts ended with a white screen after showing my Storyboard without showing my TabBar.
One of these attempts was this:
override func loadView() {
super.loadView()
// initiate tabController the same way I did in the AppDelegate
UIApplication.shared.keyWindow?.rootViewController = tabController
}
Check the value "Is initial View Controller" for it.

How to programmatically hide the left view controller in a split view controller in iOS 10

I'm developing an app in Swift for iOS 10. The UI for my app uses the inbox UISplitViewController. When my app is running on an iPad in portrait orientation I'd like the left view controller popover to automatically hide once an item is selected.
The only answers I've been able to find use methods that no longer exist or are deprecated.
To be more specific, I'd like to hide the view on the left in the screenshot below once an item in the table has been selected.
First, ensure you have a reference to your UISplitViewController from the app delegate setup. E.g.
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
let splitViewController = window!.rootViewController as! UISplitViewController
splitViewController.preferredDisplayMode = UISplitViewControllerDisplayMode.primaryOverlay
let navigationController = splitViewController.viewControllers[splitViewController.viewControllers.count-1] as! UINavigationController
splitViewController.delegate = self
}
Then in your didSelectRowAtIndexPath method for your table view, add something like:
if UIApplication.shared.statusBarOrientation == .portrait {
splitViewController?.preferredDisplayMode = .primaryHidden
}
To animate:
if UIApplication.shared.statusBarOrientation == .portrait {
UIView.animate(withDuration: 0.3, animations: {
self.splitViewController?.preferredDisplayMode = .primaryHidden
}, completion: nil)
}

How to automatically navigate to a screen on start-up

When developing an app, if I only want to test one screen, say, the third tab of a tab view, how would I make the app navigate there on start up?
I think a good solution is use a UI test to automatically navigate to the correct place in the app. Pausing on a breakpoint at the end of the test leaves the app to be played with manually.
rootViewController of window property in AppDelegate will be the first view controller shown on screen. you can make it by programming or using storyboard
by programming:
if NavigationController is the rootViewController of your window, put your own viewController in the first place (index at 0) of NavigationController's viewControllers which is an array. it will be on screen by default.
customNavigationController.viewControllers = [yourViewController]
or simply set your viewController to the rootViewController to the window property in appDelegate
AppDelegate.swift:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
window = UIWindow(frame: UIScreen.main.bounds)
// window?.rootViewController = CustomTabBarViewController()
// customViewController will show on screen by default.
window?.rootViewController = CustomViewController()
window?.makeKeyAndVisible()
return true
}
by interface builder:
check your viewControlelr's attributes inspector panel, and check "is Initial View Controller" option, then you can see a simple arrow attached to this view controller.
or:
add identifier to your viewController, and fetch it from storyBoard, then set it to the rootViewController of the window object in appDelegate
let testController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "testController") as! TestController
let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.window?.rootViewController = testController

UISplitViewControllerDelegate never called

I've been trying for days to get UISplitViewController to call the delegate. I tried setting it in the MasterViewController, I tried setting it in the DetailViewController, I tried having the SVC as an embedded view controller in a container inside another view controller, and use the prepareForSegue call to set the delegate. I tried instantiating it from the Storyboard in the AppDelegate and setting the delegate there. The delegate is never ever called. I'm going crazy.
I'm on the latest non-beta Swift (3.1), on iOS 10.3.1.
I've used the Debugger to check the delegate is in fact set, and remains in memory, however, the methods are never called.
I appreciate any help you can provide me with.
This is my current AppDelegate but I've tried countless ways as I said:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
Realm.Configuration.defaultConfiguration.deleteRealmIfMigrationNeeded = true
if let svc = self.window?.rootViewController as? UISplitViewController {
svc.delegate = svc.viewControllers.last as! UISplitViewControllerDelegate
}
return true
}
Edit 1:
Some of you didn't understand what delegate I'm trying to implement. To clarify, I'm trying to control the delegate of UISplitViewController, in other words, the UISplitViewControllerDelegate. The reason I'm trying to do this is to be able to control the collapsing of views. In the default configuration of the UISplitViewController, on an iPhone on portrait mode, it defaults to the detailViewController. This behavior is clearly wrong for most apps (and mine). I intend to first show the masterViewController that lists the content I have for display, and trigger the detailViewController only when an item has been selected.
I would also accept an answer that gave me some alternative way to have that behavior (prefer masterViewController on portrait unless my detailViewController has content set.
I know you can get the described behavior by not setting a detailViewController in the Storyboard, and using the showDetail segue when selecting an item, but my problem with that is I can't have a default view when no item has been selected, causing a grey square being shown (not very pretty) when in landscape on an iPad or iPhone Plus.
Please try below code it's work for me.
In AppDelegate:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
let splitViewController = self.window!.rootViewController as! UISplitViewController
let navViewController = splitViewController.viewControllers.first as? UINavigationController
let masterVC = navViewController?.topViewController as? MasterViewController
let detailViewController = splitViewController.viewControllers.last as? DetailsViewController
masterVC?.delegate = detailViewController
return true
}
In MasterViewController
protocol MasterViewControllerDelegate : class{
func passingData(strName : String)
}
class MasterViewController: UITableViewController {
weak var delegate : MasterViewControllerDelegate?
override func viewDidLoad() {
super.viewDidLoad()
//Calling Delegate
delegate?.passingData(strName: "Any Message")
if let detailViewController = self.delegate as? DetailsViewController {
splitViewController?.showDetailViewController(detailViewController, sender: nil)
}
}
}
In DetailViewController
class DetailsViewController: UIViewController, MasterViewControllerDelegate{
//Other IBOutlet and Methods
//Delegate method of MasterViewControllerDelegate
func passingData(strName : String)
print(strName) //OUTPUT: "Any Message"
}
}
I hope it also work for you.

I would like to embed UINavigationController with UITableViewController which is the sub-view of UITabBarViewController by programmatically in Swift

Recently, I want to make an App for self-study and that App looks like Contacts App in iOS. First, I have two UITableViewControllers connecting to UITabBarViewController. Then I want to embed UINavigationController with those 2 tableviews by coding (In this case, I don't want to embed it in Storyboard by design). But I'm stuck with it. Here is my code in AppDelegate
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
let tabBar : UITabBarViewController = UITabBarViewController()
let tableView1 : UITableViewController = UITableViewController()
let nav = UINavigationController(rootViewController: tableView1)
let tableView2 : UITableViewController = UITableViewController()
let nav2 = UINavigationController(rootViewController: tableView2)
tabBar.viewControllers = [nav, nav2]
self.window?.rootViewController = tabBar
return true
}
And the result always displays the first tableView1. How can I make it to display two tab options(on the bottom of the view) in UITabBarViewController? If I make it by designing in Storyboard, I'm sure it will work fine. But by coding, I've no idea how to fix it.
Please kindly guide me. Thanks in advance!
There is no UITabBarViewController in UIKit. It is called UITabBarController. Your ViewControllers need to have NavigationItems. If you display them like in your code you will have a Tabbar on bottom but without anything visible on it. In your case the NavigationControllers are the ones where you need to define nav.navigationItem.title = ...
EDIT:
and I realized you are not initializing the window. This is not related to your other problem but you would not see anything with your code. So you have to change it to:
self.window = UIWindow(frame: UIScreen.mainScreen().bounds);
self.window?.rootViewController = tabBar
self.window?.makeKeyAndVisible();

Resources