UIPageViewController in UINavigationController popping issue - ios

Short story:
Going from UIPageViewController (that presents proper DetailViewController) to UITableViewController, then changing (on didSelectRowAtIndexPath) the DetailViewController of the UIPageViewController and navigationController?.popViewControllerAnimated(true), displays proper UIPageViewController with changed DetailViewController but the whole view is moved down by the height of the navigationBar. Clicking on the screen sends it under the bar (where it should be).
Long story:
I embedded UIPageViewController into UINavigationController. Navigation bar translucent
I created a DetailViewController in my storyboard with a scrollView inside it.
I created custom function in the UIPageViewController to return desired UIViewController
func viewDetailViewController(index: Int) -> DetailViewController? {
if let storyboard = storyboard, page = storyboard.instantiateViewControllerWithIdentifier("DetailViewController") as? DetailViewController {
// Setting the page variables that it stores
return page
}
return nil
}
I set up a code (also in UIPageViewController):
override func viewWillAppear(animated: Bool) {
if let viewController = viewDetailViewController(currentIndex) {
setViewControllers(
[viewController],
direction: .Forward,
animated: false,
completion: nil)
}
}
Then I setup an bar button item to and in the storyboard I connected it to so it perform segue to UITableViewController. I want to pop a viewContorller on cell click so:
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
tableView.deselectRowAtIndexPath(indexPath, animated: true)
self.pageViewController.currentIndex = indexPath.row
navigationController?.popViewControllerAnimated(true)
}
When the view is popped the new view of the UIPageViewController is replaced, the pages work well and everything is great except one thing. The whole view is moved down by the height of the navigationbar. What's even more strange is that (just) tapping on the screen sets the scrollView under the navigationbar (where it should be)
I have also noticed that not only the scrollView is moved down, but what is actually moved down is UIView that contains an UIView that contains the scrollView.
What might be causing this?

Related

How to properly use didSelectRowAt in splitViewController on compact device in swift?

I'm using a splitViewController to display a master view and a detail view.
When I tap on a row, the detail view updates correctly.
Then, when I'm in portrait view, I collapse the splitview detail view, so that that master list items are shown as follows:
And when I tap on a row, I correctly move to the detail view, as shown:
The problem I'm having is that if I rotate the device in the detail view shown above, while I'm in the detail view, the rotation correctly goes back to the splitView, however, now when I select a row, the delegate method does not update the detail view. It only seems to work if I start out in the splitView and stay in that view, or if I start out in the collapsed view and stay in that. If I rotate, then the delegate method does not seem to work.
I found a prior post, which shows how to use the delegate method to update the detail view using objective C code, using the didSelectRow function. I tried to duplicate this code with the following swift code:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let navigationVC = UINavigationController()
var detailVC = TestsDetailAdvertVC()
if let tests = controller.fetchedObjects, tests.count > 0 {
//if there is, keep track of the test which is selected
selectedTest = tests[indexPath.row]
if let isCollapsed = splitViewController?.isCollapsed {
if isCollapsed {
//solves problem of navigating to the detail view in compact view
// on the iPhone (compact) the split view controller is collapsed
// therefore we need to create the navigation controller and detail controller
detailVC = self.storyboard!.instantiateViewController(withIdentifier: "detailVC") as! TestsDetailAdvertVC
navigationVC.setViewControllers([detailVC], animated: false)
self.splitViewController?.showDetailViewController(detailVC, sender: self)
detailVC.testToEdit = selectedTest
} else {
// if the split view controller shows the detail view already there is no need to create the controllers
// so we just pass the correct test using the delegate
// if the test variable is set, then it calls the showDetail function
delegate?.testToEdit = selectedTest
}
}
}
}
I think that somehow when the one or the other method is used to update the detail view it works, but then when it switches back and forth, it stops working. I wonder if anyone has solved this issue using swift code who could point me to an example.
Note: After some additional searching, I realized that there are a few of delegate methods for the splitViewController, including:
func primaryViewControllerForExpandingSplitViewController:
and
func primaryViewControllerForCollapsingSplitViewController:
and
splitViewController:separateSecondaryViewControllerFromPrimaryViewController:
I've been fiddling around with these methods, but so far haven't been able to get them to work, and I haven't found any posts that show examples of how they are used.
Thanks.
I figured out how to make the detail view update properly, using an answer from a prior post at:
In UISplitViewController, can't make showDetailViewController:sender: push onto detail navigationController
my code to solve the problem is updated using swift code:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
var detail = UINavigationController()
var testVC = TestsDetailAdvertVC()
if let tests = controller.fetchedObjects, tests.count > 0 {
//if there is, keep track of the test which is selected
selectedTest = tests[indexPath.row]
if let isCollapsed = splitViewController?.isCollapsed {
if isCollapsed {
//in collapsed view, the correct detail view controller is not
//yet substantiated, so we need to substantiate it
testVC = self.storyboard?.instantiateViewController(withIdentifier: "detailVC") as! TestsDetailAdvertVC
detail.setViewControllers([testVC], animated: true)
testVC.testToEdit = selectedTest
} else {
//in expanded view, the correct view controller needs
//to be identified, using the appropriate view controller for
//the splitview controller
let vc = self.splitViewController?.viewControllers[1]
//which is a navigation controller
if vc is UINavigationController {
detail = vc as! UINavigationController
//which we then use to identify the correct detail view
testVC = detail.viewControllers.first as! TestsDetailAdvertVC
testVC.testToEdit = selectedTest
}
}
}
}
self.splitViewController?.showDetailViewController(detail, sender: self)
}
The key solution is that on the collapsed splitviewcontroller, the detail view has to be instantiated form the storyboard. However, on the expanded splitviewcontroller, the detail view has to come from the expanded navigation controller. Then when I rotate the correct detail view controller updates correctly.

Hiding UITabBarController's tabBarItem while being able to show associated view nevertheless

I am trying to create a navigation for my iPhone "Tabbed App" which would consist of (obviously) UITabBarController and SWRevealViewController for revealing side-menus.
All the views in my application must have both UITabBarController and UINavigationBar displayed, however, links which appear in left-side menu (handled by SWRevealViewController) must not appear in UITabBarController.
My left-side menu links are handled in this way:
import UIKit
class MenuTableViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.clearsSelectionOnViewWillAppear = false
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let selectedIndex = (indexPath as NSIndexPath).row + 1 // hardcoded for time being
let tabBarController = revealViewController().frontViewController as! UITabBarController
let navController = tabBarController.viewControllers![selectedIndex] as! UINavigationController
navController.popToRootViewController(animated: true)
tabBarController.selectedIndex = selectedIndex
revealViewController().pushFrontViewController(tabBarController, animated: false)
}
}
Now, I tried to remove a link for one of the views which I don't want to show in my UITabBarController as follows:
import UIKit
class TabBarController: UITabBarController {
override func viewDidLoad() {
super.viewDidLoad()
let index = 2 // hardcoded for time being
viewControllers?.remove(at: index)
}
}
but if I click associated link in left-side menu now, I get an NSRangeException index 2 beyond bounds [0 .. 1] error (of course, because I removed the particular tabBarItem from UITabBarController).
My question is: how can I "hide" the item from UITabBarController but still being able to reference it (and open it) from my side menu?
UPDATE
My storyboard at the moment looks like this:
It's probably not a good idea to use a "menu" to manipulate the Tabs - that's what Apple has designed the More... and Edit... features for.
Depending on your overall design / navigation / user experience flow, two reasonable options would be:
Instead of replacing the current selected Tab, .present a modal view controller, with a "Cancel" or "Save" or "Done" button to .dismiss it (whatever would be appropriate).
Since you state each Tab's ViewController is a NavigationController, you could .push the menu-selected view controller onto the current stack. Then your interface could use the standard "< Back" button navigation.
Good Luck :)

Embed Material CardTableView in UINavigationController

I am trying to setup a master/detail view with CardTableView as my root and am having trouble implementing
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
//code
}
I want to use a self.navigationController?.pushViewController so that the detail view can easily come back to the table view. However when I try to embed this in a Navigation controller I get a strange looking title bar:
This is my view hierarchy:
let tabs = AppPageTabBarController(viewControllers:[InvitedViewController(), CoordinatingViewController(), PastViewController()])
let toolbar = AppToolbarController(rootViewController: tabs)
let navigationController = UINavigationController(rootViewController:toolbar)
let fabController = AppFABController(rootViewController: navigationController)
let snackBarController = AppSnackbarController(rootViewController:fabController)
let navDrawer = MyTeeUpsViewController(rootViewController: snackBarController, leftViewController: NavDrawerContainerController())
self.present(navDrawer, animated: true)
Wondering if there are any alternatives to this approach
By default, UINavigationController shows its own navigation bar above your view controller(s). In your screenshot, it's the large, light-colored bar above your views. If you don't want this, you'll have to set it so when you instantiate the navigation controller:
// You may want to animate it if you hide/show it after it's onscreen
navigationController.setNavigationBarHidden(true, animated: false)

Presenting a new view controller from selected table row modally

override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let memeShowViewController = self.storyboard!.instantiateViewControllerWithIdentifier("ShowMeme") as! MemeShowViewController
memeShowViewController.memeImage = appDelegate.memes[indexPath.row].memeImage
self.navigationController!.presentViewController(memeShowViewController, animated: true, completion: nil)
}
So I have a tableview that has a nav controller embedded in it and when I present the memeShowViewController modally I was wondering why the view of the memeShowViewController has a black background color when I didn't change it to be black.
In storyboard:
In simulator
Right now I'm just adding a navigation bar to the segue-ed view controller, I was wondering if there is a better way to do as the only functionality I need from the navigation bar is for the done button to segue back to the tableView.
The background window has a black background so if your app doesn't cover any part of the window (or alpha = 0) you'll see that black background.

Proper segue flow

I am looking for the best-practice method of doing the following:
I have two TabBarController views embedded in a NavigationController and one which I don't want to include as a tab but is embedded in a NavigationController:
FeedView (TableViewController)
VenueView (CollectionViewController)
SelectView (ViewController)
In VenueView (2), I have the following code to bring up SelectView (3):
override func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
let selectVC: SelectViewController! = self.storyboard?.instantiateViewControllerWithIdentifier("selectVC") as! SelectViewController
selectVC.currentVenue = venueItems[indexPath.row]
presentViewController(selectVC, animated: true, completion: nil)
}
This segue works, however SelectView(3) doesn't have a navigation bar at the top although it is embedded in a separate NavigationController. If I hook it up to the other NavController in the IB, it adopts/becomes the third tab in the BarTabController. I don't want that.
How do I hook it up so that there is a NavBar (with a back button that will go back to either view) but no Tab?
Also, there is a button on SelectView(3). When this button is tapped I'd like it to segue to FeedView(1) - while persisting some data ie 'pushing into the feed'. What kind of segue should I use for this? I've tried many combinations and ran into some strange bugs and I find View management very confusing.
Storyboard image below for reference
Views are in order from top to bottom (1-3):
In order to do this you need to fix your problem, you need to present the navigation controller which contains SelectView but not SelectView itself as you do now.
You should set a storyboard identifier to your 3'rd navigation controller and apply this change:
let selectNavigationController = self.storyboard?.instantiateViewControllerWithIdentifier("selectNavigationController") as! UINavigationController
let selectVC = selectNavigationController.topViewController as! SelectViewController
selectVC.currentVenue = venueItems[indexPath.row]
presentViewController(selectNavigationController, animated: true, completion: nil)

Resources