Showing posts with label uisplitviewcontroller. Show all posts
Showing posts with label uisplitviewcontroller. Show all posts

Tuesday, January 9, 2018

UISplitView's MasterViewController and Navigation Issue

Leave a Comment

I am updating my existing app to include a SplitView for iPads.

I have it working with a UITabBar, but am having an issue with my masterViewController as it is generating a "duplicate" navigation bar that is covering my existing navigation items on all masterViewControllers (tabs), including searchBar on the search tab.

The code I have is:

AppDelegate

class AppDelegate: UIResponder, UIApplicationDelegate, UISplitViewControllerDelegate {  func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {      let splitViewController = self.window!.rootViewController as! UISplitViewController     splitViewController.delegate = self     splitViewController.preferredPrimaryColumnWidthFraction = 0.33     splitViewController.minimumPrimaryColumnWidth = 375     splitViewController.preferredDisplayMode = .allVisible      return true }  func splitViewController(_ splitViewController: UISplitViewController, collapseSecondary secondaryViewController:UIViewController, onto primaryViewController:UIViewController) -> Bool {     return true } 

The reason for having this in the AppDelegate is I saw an example where placing it hear will allow me not to require the code in each of the different Master Views (each tab). Have yet to test this as still working on the first master view.

Master View

override func viewDidLoad()  {      self.extendedLayoutIncludesOpaqueBars = true      self.navigationItem.hidesBackButton = true      // 3D Touch     if traitCollection.forceTouchCapability == .available {         registerForPreviewing(with: self as UIViewControllerPreviewingDelegate, sourceView: view)         ThreeDTouch = true     }      self.addSwitchVewButtonToNavigationBar()     self.addCategoryButtonToNavigationBar()  }  func addSwitchVewButtonToNavigationBar() {     let switchButton = UIButton(type: UIButtonType.custom)      let editImage = UIImage(named: "CollectionButton")?.withRenderingMode(.alwaysTemplate)     switchButton.setImage(editImage, for: .normal)     switchButton.addTarget(self, action: #selector(SpeciesViewController.onSwitchView), for: UIControlEvents.touchUpInside)     let switchButtonFinal = UIBarButtonItem(customView:switchButton)      self.navigationItem.rightBarButtonItem = switchButtonFinal  }  @IBAction func onSwitchView(_ sender: UIBarButtonItem) {     AppDelegate.getAppState().isListViewSelected = false      let speciesColletion = storyboard?.instantiateViewController(withIdentifier: Resource.SpeciesCollectionStoryboard) as! SpeciesCollectionViewController     self.navigationController?.viewControllers = [speciesColletion] } 

Originally, the onSwitchViewButton was embedded using the IB, but did not work. This is the same system used for the addFavorite on the Detail View.

enter image description here

enter image description here

1 Answers

Answers 1

The problem that you addressing wrong UINavigation controller. I assume before splitting both master and SpeciesViewController existed in same navigation environment, and use same navigation controller. But in split view they don't. Your detail controller is actually UINavigation controller you are looking for, that had to control all navigation, and had to have buttons you need. You can get it from master as:

guard let split = splitViewController, let navController = split.viewControllers.last as? UINavigationController else { return } 

And make sure that you split controller not embedded into another UINavigationController (reason for second navigation bar).

EDIT: Function to return detail's Nav Controller:

var detailsNavigationController: UINavigationController? {     return splitViewController?.viewControllers.last as? UINavigationController } 

Storyboard Split To access blue call detailsNavController, to access red use navigationController.

Read More

Sunday, July 30, 2017

Default UICollectionView layout not respecting iPad split screen

Leave a Comment

My layout when on split screen is not respecting the width of the split screen.

My custom view is respecting it(the black bar at the top) but anything using autolayout is not respecting the width.

I am handling rotation using

override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { 

Is there a helper method for handling split view? Do I handle it in layoutSubview? I would have expected the UICollectionView to handle this for us.

In viewWillTransition I use

  guard let flowLayout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout else {             return         }    flowLayout.invalidateLayout() 

enter image description here

1 Answers

Answers 1

I assume by "default" layout you mean a UICollectionViewFlowLayout. It looks like the itemSize width is bigger then the width of the view. Try adding a check in collectionView:layout:sizeForItemAtIndexPath: to make sure that the width is less than or equal to the collection's width.

Read More

Wednesday, June 29, 2016

Why can I not place Master and Detail view next to each other in UISplitViewController on the first run, but upon rotation it works?

1 comment

I have a split view controller that has a list of items on the left and a detail view on the right. Relevant code in AppDelegate:

let splitViewController = mainView.instantiateViewControllerWithIdentifier("initial") as! UISplitViewController            let rightNavController = splitViewController.viewControllers.last as! UINavigationController         let detailViewController = rightNavController.topViewController as! DetailsIpad          let leftNavController = splitViewController.viewControllers.first as! UINavigationController         let masterViewController = leftNavController.topViewController as! MainViewController          masterSplitViewController = masterViewController         detailSplitViewController = detailViewController          // Override point for customization after application launch.         let navigationController = splitViewController.viewControllers[splitViewController.viewControllers.count-1] as! UINavigationController         navigationController.topViewController!.navigationItem.leftBarButtonItem = splitViewController.displayModeButtonItem()         splitViewController.delegate = self          self.window!.rootViewController = splitViewController 

When I first launch the app I see that the right part of the split screen takes up all of the screen:

enter image description here

If I rotate the screen, it becomes properly set (probably because both views are present on the screen):

enter image description here

When I set breakpoints everywhere, I see that the detail view on the right gets loaded before the master view on the left (list of items), despite not being called directly. I cannot change the order in which the views of the split screen are called. How can I fix this?

1 Answers

Answers 1

It's the default behavior of UISplitViewController. Have a close look at the following-

// An animatable property that controls how the primary view controller is hidden and displayed. A value of `UISplitViewControllerDisplayModeAutomatic` specifies the default behavior split view controller, which on an iPad, corresponds to an overlay mode in portrait and a side-by-side mode in landscape. @property (nonatomic) UISplitViewControllerDisplayMode preferredDisplayMode NS_AVAILABLE_IOS(8_0); 

Here's the key part from same definition-

on an iPad, corresponds to an overlay mode in portrait and a side-by-side mode in landscape.

Also, if you want to query the current state (display mode) of a UISplitViewController you should use this property-

// The actual current displayMode of the split view controller. This will never return `UISplitViewControllerDisplayModeAutomatic`. @property (nonatomic, readonly) UISplitViewControllerDisplayMode displayMode NS_AVAILABLE_IOS(8_0); 

And remember, you can't compare this with UISplitViewControllerDisplayModeAutomatic because-

This will never return UISplitViewControllerDisplayModeAutomatic.

My suggestion would be to set preferredDisplayMode to the value you want. In your case, it seems like you need the primary (master) to always be visible. So here's the proposed solution-

mySplitVC.preferredDisplayMode = UISplitViewControllerDisplayModeAllVisible 

Why it loads the secondary(detail) controller first?

As you can see, a UISplitViewController instance always needs a detail view no matter what displayMode it is in currently. So it's a good call to

  • load the detail view first.
  • load the primary view after (conditionally based on displayMode).

Hope this helps.

Read More

Monday, March 28, 2016

Warning: Finishing up a navigation transition in an unexpected state. Navigation Bar subview tree might get corrupted with “UISeachController”

Leave a Comment

To visualize - I've got the main view controller embedded in a UINavigationController and on this view controller the UISearchController is installed. After performing the search tapping a row on the table view containing the search results, it segues the user to the next view controller. Then on pressing the back button I get this warning. This doesn't happen on the iPad, only iPhone. The bigger picture is that the UINavigationController is the detail view controller of a UISplitViewController.

This is the implementation of the UISearchController

    func initializeSearchController() {         searchController = UISearchController(searchResultsController: nil)         writersTableView.tableHeaderView = searchController.searchBar         searchController.searchBar.barTintColor = UIColor.whiteColor()          searchController.searchBar.tintColor = UIColor.blackColor()         searchController.searchBar.scopeButtonTitles = ["All", "Favorites"]         searchController.searchBar.placeholder = "Search writers"          searchController.searchResultsUpdater = self         searchController.searchBar.delegate = self         searchController.delegate = self          definesPresentationContext = true         searchController.dimsBackgroundDuringPresentation = false         searchController.hidesNavigationBarDuringPresentation = true          searchController.searchBar.sizeToFit()         searchController.loadViewIfNeeded()     } 

Then there is another thing. I set the title of the second view controller dynamically according to which search result row is tapped like this: title = writer.name - The back button gets reset to whatever the value of the title of the second view controller is - and the title 'in the middle of the navigation bar' is left out blank. What I would like to have is the back button as it is and the second view controller title changes dynamically.

As indicated this doesn't happen with the iPad - Only iPhone?

0 Answers

Read More