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

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

Related

Where in my ViewController do I put initializing code?

I'm trying to add a FittedSheet (Repo) to my app, and in their Usage explanation, they just have this chunk of code with no further explanation regarding where to put it.
let controller = MyViewController()
let sheetController = SheetViewController(controller: controller)
// It is important to set animated to false or it behaves weird currently
self.present(sheetController, animated: false, completion: nil)
I want to know where that code is supposed to go so I can include the basic version of a FittedSheet in my app. Any input would be greatly appreciated, I'm new to swift and have all the backend data in my app working but am struggling to display it.
MyViewController() is a controller name whose data need to be present using the FittedSheet library.
Let me take an example.
In the below example, i create customSheetController in my project. I design UI in storyboard and create a swift class for same.
customSheetController storyboard UI
Below is customSheetController swift class syntax
class customSheetController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
}
In some another viewcontroller class, open that sheet on collection view item click.
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
if let sheetView = storyboard.instantiateViewController(withIdentifier: "customSheetController") as? customSheetController {
let sheetController = SheetViewController(controller: sheetView)
// It is important to set animated to false or it behaves weird currently
self.present(sheetController, animated: false, completion: nil)
}
}
In the above example i opened a sheet on collection view item click, you can add above code on button event or any event from where you want to open that sheet.
another example
Let me take another example, suppose you have two viewcontroller class in your project lets say A and B. You want to present A controller over B controller. Then you need to modify the code as below.
In B controller class, suppose you want to present A controller on button click then you can write below code on the button click event
let controller = A()
let sheetController = SheetViewController(controller: controller)
self.present(sheetController, animated: false, completion: nil)

Adding a dependencies to a child view controller

I want to present a custom view controller within a navigation controller within modal view. To do this I made a view in interface builder, added a container view, and embedded the contained view in a navigation controller. The contained view is my custom view controller, DetailViewController.
I have to add dependencies to the DetailViewController object to at runtime. Here’s the method where I’ll be presenting the DetailViewController:
override func tableView(_: UITableView, didSelectRowAt: IndexPath) {
guard let record = records[didSelectRowAt.row] else { return }
let storyBoard = UIStoryboard(name: "Main", bundle: nil)
let containerView = storyBoard.instantiateViewController(withIdentifier: "DetailContainer")
containerView.modalPresentationStyle = .overFullScreen
self.present(detailContainer, animated: true, completion: nil)
}
How can I add the record to DetailViewController? I tried to access children of the container view, but the array is empty. This Apple document says
The children must be instantiated at the same time as the parent so that the appropriate parent-child relationships can be created.
I'm not sure how to achieve that.
containerView.children is empty because viewcontroller's view isn't loaded.
You can force to load view by calling containerView.view thereafter containerView.children will exist.

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.

Dismiss the view controller which have navigation controller and pass the value back to main view controller

I have an mainViewcontroller in that one button called get value.Then i am calling dataviewcontroller to select any item in collection view cell.Once user select any cell.That particular dataviewcontroller will dismiss and while dismiss it will have the user selected item name and it will display in mainViewcontroller .Now the view controller is not dismissing.
Here the code :
In my mainViewcontroller:
var SelectedName: String? = ""
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
currentTF.text = SelectedName
}
Now dataviewcontroller :
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if collectionView.cellForItem(at: indexPath) != nil {
selectedNamesss = allitems[indexPath.row] as String
}
calldismiss()
print(selectedNamesss)
}
func calldismiss() {
if let presenter = presentingViewController as? mainViewcontroller {
presenter.SelectedName = selectedNamesss
}
dismiss(animated: true, completion: nil)
}
Now how can i solve this.My viewcontroller is not dismising and values also not showing.
Thanks in advance ~
My Suggestion is to use "UnwindSegue" technique, which is simply works like "PerformSegue", So you can send back values very easily using prepare for segue and will be back to main controller. Here is tutorial for unwind segue. Or you can find more from google.
For The problem where data is not showing, problem is with this condition:
if let presenter = presentingViewController as? mainViewcontroller {
presenter.SelectedName = selectedNamesss
}
this condition will never be true as the presentingViewController would always be a UINavigationController. so you need to change that may be something like follows:
if let presenter = (presentingViewController as! UINavigationController).viewControllers.first! as? mainViewcontroller {
presenter.SelectedName = name
}
Here i am using viewControllers.first! in condition as mainViewcontroller was at first index of the viewControllers array. Check the viewControllers array and find the index of mainViewcontroller and make changes accordingly.
I made this changes and the code is working for me. I am getting the selected name on the mainViewcontroller.
Also i would like to mention that this is not the right way to transfer data backwards. You can use delegates, blocks or unwind segue to achieve this.
Though this is working i am attaching a gif to show this working.
Regarding the issue where your controller is not being dismissed, i am presenting the controller in following way:
let dataVC = self.storyboard?.instantiateViewController(withIdentifier: "dataviewcontroller") as! dataviewcontroller
self.present(vc, animated: true, completion: nil)
Try this and see if this helps you :)

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