Proper segue flow - ios

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)

Related

Modally presented view controller dismisses by itself on touching the view in the upper side of the screen

I have a view controller which has a table view and in the didSelectRowAt indexPath I instantiate the destination view controller, embed it into a navigation controller and present it... here is some code
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
let destVC = DetailVC(frame: self.view.frame)
//passing data to the destVC ...
destVC.modalTransitionStyle = .partialCurl
let nav = UINavigationController(rootViewController: destVC)
present(nav, animated: true, completion: nil)
}
Working like a charm so far. When the detail VC appears everything's great, the tableView works perfectly, the leftBarButtonItem dismisses the detailVC as it should, but whenever I press anywhere on the upper side of the screen the detail view somehow automatically dismisses itself, which I don't want. I'm gonna leave a link to an image to see exactly what part of the screen automatically enables the dismiss function:
http://prntscr.com/kaibxg
I've been struggling with this and I didn't find any solution. I don't know why this is happening. When I use the pushViewController function, the detail VC is all right and is not popping itself out, but the thing is I really need to use the .partialCurl transition animation. I tried to find a way to somehow use .partialCurl when pushing the VC but I didn't find anything about that. Any help, answer, question is more than welcomed and appreciated. Thank you all in advance.

Load ViewController as child view in main view from other child view

I´m coding an application using swift3/xcode 8. My application have a central view controller, called MenuViewController, which is responsible for the side menu.
Inside this one, i load the other views as child views, so in this way I can use the side menu in all other views.
But I´m in doubt now: In a specific view controller I have a table view, which have a method to perform an action on selecting an item. This action calls other
view and pass a parameter.
So, how do I load this view in the BaseViewController with this parameter? Is it possible?
Please let me know if it is not clear....
Thank you!
Update 1:
I´m using a solution I found interesting:
Cocoacasts
Basically I have this code on MenuViewController:
Declare the view to be loaded in MenuViewController:
lazy var filamentoViewController: FilamentoViewController = {
let storyboard = storyboard.instantiateViewController(withIdentifier: "FilamentoViewController") as! FilamentoViewController
self.addViewControllerAsChildViewController(childViewController: viewController)
return viewController
}()
Adds the viewcontroller as child in MenuViewController:
private fun addViewControllerAsChildViewController(childViewController: UIViewController)
{
addChildViewController(childViewController)
view.addSubView(childViewController.view)
childViewController.view.frame = view.bounds
childViewController.view.autoResizingMask = [.flexibleWidth, .flexibleHeight]
childViewController.didMove(toParentViewController: self)
}
Code in FilamentoViewController which calls other viewcontroller:
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: IndexPath)
{
let editFilamentoViewController:EditFilamentoViewController = EditFilamentoViewController()
editFilamentoViewController.cod = indexPath.row
self.presentViewController(editFilamentoViewController, animated: true, completion: nil)
}
The code
self.presentViewController(editFilamentoViewController, animated: true, completion: nil)
is the one I want do load into MenuViewController.
Update 2:
MenuViewController is a CentreViewController. I load the other views in the center and the menu comes from left.
There is a built-in controller for your kind of requirement(i.e UISplitViewController) , Have you considered this?
Apple documentation
Sample Tutorial
And there are also slide-out menu libraries.
slide out menu

Warning: Attempt to present View Controller on * which is already presenting <UISearchController: 0x142a1f7c0>

I made a view controller with a UISearchController and a UITableView . There are two different kind of search you can select from the search scope buttons : groups and people. Both searches work and show results on the table. However, if you click on each cell they should direct you to different dynamic pages (a dynamic group page or a dynamic person profile page). The one for groups works, while the one for profiles doesn't. Meaning whenever I click on the a person cell from the results that I got, nothing happens and I get the following warning printed on the console :
Warning: Attempt to present <MyProject.profileView: 0x13e9df000> on <MyProject.SearchPage: 0x142a1d8f0> which is already presenting <UISearchController: 0x142a1f7c0>
If you have any idea why this could be happening it'd be really appreciated if you could let me know.
EDIT : Here's the function that should link the cell to the different view controllers :
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
if self.searchForGroups {
let detailCell:PFObject = self.filteredGroups.objectAtIndex(indexPath.row) as! PFObject
let vc = self.storyboard!.instantiateViewControllerWithIdentifier("DynamicCellView") as! DynamicCellView
vc.GroupId = detailCell.objectId!
self.presentViewController(vc, animated: false, completion: nil)
}else{
//Link to use profile
let user = self.peopleResults[indexPath.row]
let vc = self.storyboard!.instantiateViewControllerWithIdentifier("ProfileView") as! profileView
vc.profileId = user.objectId!
self.presentViewController(vc, animated: true, completion: nil)
}
}
I was having the same warning and this fixed it for me. You need to stop presenting the search controller so you can present other controller while you are leaving the view.
override func viewDidDisappear(_ animated: Bool) {
if SearchController.isActive == true {
SearchController.isActive = false
}
}
I was figuring the same original issue and none of this resolved it for me.
Actually you just have to dismiss the UISearchController as it is said because it is already presenting to the current view.
So, when you want to launch your action, you just have to call this :
if searchController.isActive {
self.searchController.dismiss(animated: false) {
// Do what you want here like perform segue or present
}
}
Hope this will help!
I hit the same error while trying to perform a segue when tapping on a search result. This isn't an ideal workaround but dismissing the searchController before performing the segue fixed it for me:
self.searchController.dismiss(animated: false) {
self.performSegue(withIdentifier: "<YOUR SEGUE IDENTIFIER>", sender: cell)
}
I'm not sure if the above answers worked properly in previous version but in swift 5, calling dismiss will cause the segue to trigger properly but the search bar will remain active and when they dismiss the triggered segue (come back to the search page) the search bar will look active but the results will not be.
Also dismissing from viewDidDisappear() did not work properly. Here's how I did it.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
//Do some stuff here
if searchController.isActive{
searchController.isActive = false
}
performSegue(withIdentifier: "<yourSegueIdentifierHere>", sender: nil)
}
Dismissing UISearchController (proposed in this thread in dozen different ways) is a working solution. One more workaround I've found is just having
definesPresentationContext = true
in viewDidLoad() of a View Controller having UISearchController. This workaround is better in a way the once you navigate back, UISearchController is still showing the search results.
Without the code is kind of difficult to help you. That error may be happening because you are breaking the view controller hierarchy.
The message is saying that you have 3 view controllers involved:
SearchPage is presenting UISearchController
profileView is not yet presented but should be presented on UISearchController or should replace it (For that UISearchController should be dismissed first)
Take in mind that a view controller can present only 1 view controller at the time but it can have several child view controllers (such as a navigation controller).
For more information you can check: https://developer.apple.com/library/ios/featuredarticles/ViewControllerPGforiPhoneOS/TheViewControllerHierarchy.html#//apple_ref/doc/uid/TP40007457-CH33-SW1
Just as a comment, its a good coding practice to start your class name with a uppercase letter ('ProfileView' instead of 'profileView')

Segue back from Modal to First Tab Bar

I have two TabBarController views embedded in a NavigationController and one View presented modally with its own NavigationController:
FeedView (1) (TableViewController -> Contained within TabBarController)
VenueView (2) (CollectionViewController -> Contained within TabBarController)
SelectView (3) (ViewController -> Modally presented from VenueView)
In VenueView (2), I have the following code to bring up SelectView (3):
override func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
let selectNavigationController = self.storyboard?.instantiateViewControllerWithIdentifier("selectNavController") as! UINavigationController
let selectVC = selectNavigationController.topViewController as! SelectViewController
selectVC.currentVenue = venueItems[indexPath.row]
presentViewController(selectNavigationController, animated: true, completion: nil)
}
This allows me to bring up SelectView(3) without the tabbarcontroller, but still have a navbar. I have a button in SelectView(3) which I would like to use to persist an object's data and segue into FeedView(1).
I've tried many methods but the closest I've got to success was to segue to FeedView(1) but add more viewControllers (evidenced by buttons in navbar) and not have it contained within the TabBarController.
What is the correct way of creating this segue? I know how to persist the data but my question is more related to the proper technique of segueing from a modal View to the first tab of a BarTab.
Pic below for reference:
You could use an unwind segue if you stayed within the same navigation stack.
However, FeedView is on a different navigation controller.
One way to do it is to have your TabbarController switch back to FeedbackView (1st tab) using:
tabBarController.selectedIndex = 0

Segue VS didDeselectRowAtIndexPath

I am using didDeselectRowAtIndexPath to navigate through different Storyboards like:
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let story = ["News","Video","Twitter","Request Info","More"]
let vc = self.storyboard?.instantiateViewControllerWithIdentifier(story[indexPath.row]) as NewsFeedTableViewController
self.presentViewController(vc, animated: true, completion: nil)
}
However, the Navigation Bar does not show up when I use this function. But, when I use segue, the Navigation Bar shows up.
Now the Problem is:
Cells in a tableView cannot segue more than 1 storyboard
Need Navigation Bar to Scroll in the App (which seems to need Segue)
Any solutions through this?
You are using presentViewController, that's why you are not getting the NavigationBar, it presents the view modally. Instead of that pushViewController like:
self.navigationController?.pushViewController(vc, animated: true)
Probably in your storyboard the segue type will be push, that's why you are getting the Navigation Bar.
You might be interested in the following:
presentViewController:animated:completion:
ViewController Programming Guide for iOS
You are presenting the next view controller, not pushing it. (at least in case 1) So you have a modal link and no nav controller !
You can either use .pushViewController or simply performSegue and make sure your segue type is push. I would go for the performSegue and change the identifier name in each switchcase.
And you can still pass data in the prepareForSegue
Better to call performSegue in didSelectRow. And perform actions in perpareSegue Method .

Resources