UISplitViewController display wrong ViewController on launch - ios

I have a fully functional Address book app (from Lynda.com called iOS Address Book App: Hands On) that utilizes a UISplitViewController.
I want to implement this Address Book app into my existing Tab bar app.
I have moved all over and the most all runs well. (Storyboard items and all ViewControllers, except the AppDelegate).
The only issue I have is when I select the Address Book Tab (on home screen), it displays the detailsViewController as the default view. I can hit the back button that takes me to the Master screen and all works fine. I just need it to display the Master View as the initial screen.
Clearly, it is a setting issue as it works fine as a stand alone.
Willing to post any code necessary to help, but not sure which VC to post.
Here are some images to show what I am referring to:
Tab bar
Selecting "Buddies" takes me here, to the DetailViewConroller
Instead of here, the MasterViewController
Hopefully, someone has seen this before.
MasterViewController

Found it. Posting in case someone stumbles across this same issue. Apparently, the DetailViewController is the default view while in portrait (I assume non iPad portrait). To change the initial screen do the following to the MasterViewController.swift (or the view controller you use to control the UITableView)
class MasterViewController: UITableViewController, UISearchResultsUpdating, UISplitViewControllerDelegate {
private var collapseDetailViewController = true
override func viewDidLoad() {
super.viewDidLoad()
splitViewController?.delegate = self
}
func splitViewController(splitViewController: UISplitViewController, collapseSecondaryViewController secondaryViewController: UIViewController, ontoPrimaryViewController primaryViewController: UIViewController) -> Bool {
return collapseDetailViewController
}

Related

Swift UISplitViewController - unable to present master view before detail view (iOS 14, Xcode 12.4)

I am having this issue where I am using a UISplitViewController MainSplitVC, and I am unable to present the master view controller over the detail view controller. Basically, when testing this on an iPad, I want the master and detail VC to be visible side by side, but on an iPhone (portrait mode), I want only the master VC. Currently, this works on iPad, but on an iPhone in portrait mode, Swift is showing the detail view controller first, and I have to click the back button in the navigation bar to return to the master view controller.
I have tried every possible approach that I could think of. For instance, I created a class for the Split View Controller, MainSplitVC, where I subclass UISplitViewController and UISplitViewControllerDelegate. Then, in viewDidLoad(), I set the preferred display mode to oneBesideSecondary (since allVisible was replaced by that according to Xcode). I also include the function collapseSecondary() to always collapse back to the master view controller.
class MainSplitVC: UISplitViewController, UISplitViewControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
self.delegate = self
print("viewDidLoad called")
self.preferredDisplayMode = UISplitViewController.DisplayMode.oneBesideSecondary
}
func splitViewController(
_ splitViewController: UISplitViewController,
collapseSecondary secondaryViewController: UIViewController,
onto primaryViewController: UIViewController) -> Bool {
print("collapseSecondary called")
return true
}
}
I have consulted many other posts regarding this issue, and all the posts indicate that using the collapseSecondary() function and setting preferredDisplayMode to oneBesideSecondary or allVisible should do the job. However, none of this is working. What's more is that the collapseSecondary() function is not even being called, even though I included UISplitViewControllerDelegate in the class header.
Could anyone clarify if I made any mistakes in the code below? When the app opens, I just want the master view controller to be shown; the detail view controller is simply a blank view controller that changes to another when something in the tableview of the master view controller is clicked.
The links I used for reference were as follows:
UISplitViewController showing detail view controller first on iPhone, also delegate not calling proper functions
UISplitViewController in portrait on iPhone shows detail VC instead of master
Open UISplitViewController to Master View rather than Detail
Thanks!
EDIT 2/3/21: I've resolved the issue. I instead extended SceneDelegate under UISplitViewControllerDelegate and included the following function:
func splitViewController(_ svc: UISplitViewController, topColumnForCollapsingToProposedTopColumn proposedTopColumn: UISplitViewController.Column) -> UISplitViewController.Column {
return .primary
}
The collapseSecondary function didn't work apparently because there is a bug where "Interface Builder doesn’t allow creating a classic style UISplitViewController. (65966010) (FB8107534)", so I get the error message "Skipping delegate callback, splitViewController:collapseSecondaryViewController:ontoPrimaryViewController:. Unsupported for UISplitViewController style DoubleColumn".
Thanks!
Returning .primary should solve your issue.
#available(iOS 14.0, *)
public func splitViewController(_ svc: UISplitViewController, topColumnForCollapsingToProposedTopColumn proposedTopColumn: UISplitViewController.Column) -> UISplitViewController.Column {
return .primary
}
[From Documentation]
Asks the delegate to provide the column to display after the split view interface collapses.
When the split view controller transitions from a horizontally regular to a horizontally compact size class, it calls this method and asks you for the column to display when that transition is complete. Use this method to customize the view controller you’re collapsing to.

InterativeSideMenu Library issue for IOS Navigation Controller

I have a project with one navigation controller and I am also making use of the InteractiveSideMenu library which can be found here : https://github.com/handsomecode/InteractiveSideMenu. The main issue is, when I embed the rootview controller, based on the specification of the library, I have to create a class the calls the delegate to show the side menu for the root view controller like so:
class NavigationViewController: UINavigationController, SideMenuItemContent {
}
This will enable to Sidemenu before the main view controller is declared in another class under UIViewController like this
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource{}
The main issue here is, I have a navbar button that I call to display the side menu using this IBAction:
#IBAction func sideMenuDisplay(_ sender: Any) {
if let navigationViewController = self.navigationController as? SideMenuItemContent {
navigationViewController.showSideMenu()
}
}
But it appears the if statement resolves to false and hence, the sidemenu is not triggered on this IBaction. I am very new to IOS and finding it really difficult wrapping my head around this issue.
In your storyboard, you need to set the class of your navigation controller to be your custom 'Navigation View Controller.'
That is found on the right-hand side under the 'Identity Inspector'

Popup From a Tabbar (Like Yelp App)

I am pretty much trying to replicate the same tabBar popup as you see in the yelp app (before and after screenshots at the bottom) where no matter what view your in you can press on the center tabBar item and pop up will appear.
Coincidentally I have 5 tabBar items (like Yelp) and I am trying to have three popups with a image and title for each (like Yelp). Seeing that what I am trying to do is already done in an app shows me that this is possible, but I do not know how to do it. I have tried to change the types of relationships between view controllers or do it programmatically, but nothing seems to work. What am I missing or doing wrong?
Tabbar Controller Code:
func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool {
if viewController.tabBarItem.tag == 1 {
return false
} else {
return true
}
}
Before Press
After Press
One solution could be to adopt the UITabBarControllerDelegate.
This way we can use the func tabBarController(UITabBarController, didSelect: UIViewController) method of the delegate to alter the regular presentation behavior and show our popups instead. We could find the offset required (in terms of CGPoint) relative to the tab bar button and then apply that offset and add the popup buttons to as a subview. Note that for this method you would need to programmatically set the frames of your popup buttons.
Alternatively, you could also make a bunch of popup buttons and set their alphas to zero and one when clicked. Hope this helped! Thanks :)
Custom action for tab bar
UITabBarControllerDelegate documentation

UISplitViewController: How to prevent expansion when rotating from Compact to Regular

There are many answers to the complementary question, which is how to prevent a transition to PrimaryOverLay on a from Regular to Compact interface change, eg use
func splitViewController(splitViewController: UISplitViewController, collapseSecondaryViewController secondaryViewController: UIViewController, ontoPrimaryViewController primaryViewController: UIViewController) -> Bool
In my case, I have an iPhone 6+ with the detail view showing in portrait. When I rotate the device to horizontal (Compact to Regular), I want the primary view to stay hidden. I've tried setting the preferredDisplayMode to .PrimaryHidden in many places, but it has no apparent affect. Googling has turned up nothing.
Well, after I wrote the question, but before posting it, I tripped on a possible solution, which is to override the trait collection that the split view controller references.
I took that idea and decided to subclass UISplitViewController, and override the traitCollection property. That did the trick:
final class MySplitViewController: UISplitViewController {
var didOnce = false
override var traitCollection: UITraitCollection {
let old = super.traitCollection
let change = UITraitCollection(horizontalSizeClass: .Compact)
let new = UITraitCollection(traitsFromCollections: [old, change])
return new
}
Obviously this is hardcoded for one device - later I'll go and add some functions that I can use to control what is in fact returned.
Don't override traitCollection, instead use the method setOverrideTraitCollection:forChildViewController: in a parent view controller of your split controller, like in Apple's example AAPLTraitOverrideViewController.m
If your split controller doesn't have a parent, making a parent is really easy in the Storyboard. Add a new view controller, make it the entry point, add a container view, delete the default embedded view and instead add an embed segue to the split controller and set the override on self.childViewControllers.firstObject in viewDidLoad.

Right way or event to choose what view load in swift

I'm working in an app that logs in an user if there isn't another user already logged in at launch time. This way the first view to appear should be the Login View. But in the case there is a logged user already, the first view appearing should be the main menu. Im handling this with the viewWillAppear function and it's working, but I don't know if this is the correct approach or how it should be handle in this situations.
Here is my code. My first view is MainMenuVC in which I control if there is a logged user or not, then I choose if stay in main menu view or push my login view.
class MainMenuVC: UIViewController {
override func viewWillAppear(animated: Bool) {
if (UserMgr.users.count == 0){
var vc1:LoginVC = self.storyboard?.instantiateViewControllerWithIdentifier("LoginView") as LoginVC
self.navigationController?.pushViewController(vc1, animated: false)
}
else
{
//I do nothing so this view is loaded
}
}
I don't know if i should use another ViewController and implement the function loadView() to decide what view load, but the problem is make that view work with the story board and my navigation controller.
Any suggestions?
Basically you will have two different view controllers, one for the login screen (VCLogin) and one for the main menu (VCMainMenu). Now, in your AppDelegate there are methods which are called, when the app launches respectively when it appears. So, place the code checking whether a user is logged in there and make the appropriate view controller the root view controller, e.g.
let navigationController = window.rootViewController as UINavigationController
navigationController.rootViewController =
userIsLoggedIn ? mainMenuViewController : loginViewController

Resources