Simple, custom navigation between 4 view controllers - ios

I have UIScrollView which contains many following Subscription View Controllers. Each Subscription View Controller contains Container View Controller.
The target is to do a simple navigation between 4 Views Controllers at the right side.
Navigation logic:
On viewDidLoad show first or second View Controller in Container
When the user press a button on View Controller show the third View Controller in the Container
I tried to use Segues but this didn't work. The way to instantiate VCs to
Subscription View Controller is not good idea.

Use this code to switch the container views view...
#IBOutlet weak var container: UIView!
var currentViewController:UIViewController?
//put the view did load method here.
#IBAction func buttonPressed() {
//the storyboard file that the view is in.
let storyboard:UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
// the container view you want to switch to.
self.currentViewController = storyboard.instantiateViewControllerWithIdentifier("containerViewStoryboardID") as? UIViewController
//switch the container view.
self.addChildViewController(self.currentViewController!)
self.container.addSubview(self.currentViewController!.view)
self.currentViewController!.didMoveToParentViewController(self)
}

The simplest way I found is to create custom Segue.
Creating custom UIStoryboardSegue
Go to File -> New -> File... and choose Cocoa Class
Create a new class from UIStoryboardSegue
Configurate MySegue
import UIKit
class NewSegue: UIStoryboardSegue {
//Call when performSegueWithIdentifier() called
override func perform() {
//ViewController segue FROM
var sourceViewController: UIViewController = self.sourceViewController as! UIViewController
//ViewController segue TO
var destinationViewController: UIViewController = self.destinationViewController as! UIViewController
//Parent ViewController - ContainerViewController
var containerViewController: UIViewController = sourceViewController.parentViewController!
//Setting destinationViewController
containerViewController.addChildViewController(destinationViewController)
destinationViewController.view.frame = sourceViewController.view.frame
sourceViewController.willMoveToParentViewController(nil)
//Do animation
containerViewController.transitionFromViewController(sourceViewController,
toViewController: destinationViewController,
duration: 0.3,
options: UIViewAnimationOptions.TransitionCrossDissolve,
animations: nil, completion: { finished in
//Delete sourceViewController
sourceViewController.removeFromParentViewController()
//Show destinationViewController
destinationViewController.didMoveToParentViewController(containerViewController)
})
}
}
Go to your Storyboard file and do control drag from ContainerViewController to needed Controller and choose Custom in context menu
3.Click on created segue and configure them
Now you can call performSegueWithIdentifier("SugueID", sender: self) in your ContainerViewController or in other ViewController

Related

Missing transition from first view controller to second view controller using navigation controller

I want to create a navigation hierarchy where I want to go to a SecondViewController from FirstViewController using a NavigationController. The FirstViewController contains a button B that I intend to use to go to a SecondViewController. I am employing the navigation controller concept since I want to return to the FirstViewController later. I have done the following steps to achieve it:
I have embedded a NavigationController into a storyboard X containing both the FirstViewController and SecondViewController.
I have created a segue from the button B present in the FirstView (has FirstViewController associated with it) to the SecondView (has SecondViewController).
I set the navigationViewController nvc as following after I have presented the firstViewController:
nvc = UINavigationController.init(rootViewController: firstViewController)
Later on, when the button B gets pressed, I execute the following code to push the secondViewController onto the navigation hierarchy:
self.nvc?.pushViewController(secondViewController, animated: true)
However, the secondView doesn't present itself even after pushing.
I have named the segue between the button B and secondViewController as kSegue, so I tried to perform the segue as an addition to see if the secondViewController presents itself or not:
self.performSegue(withIdentifier: "kSegue", sender: self)
An exception occurs when both the 4th and 5th steps are performed together. The exception states that I'm pushing a viewController that already exists on the navigation hierarchy, but the second view still doesn't open even if I comment the performSegue code.
May I ask what mistake I am making here?
In the storyboard, make sure there is a rootViewController segue from the navigation controller to the first view controller. Also, make sure the navigation controller is marked as the initial view controller.
In the code, change
self.nvc?.pushViewController...
To
self.navigationController?.pushViewController...
1) Take a navigation controller on your storyboard
2) Set navigation controller as initial view controller
3) set view controller A as a root view controller of your navigation controller
4) in "GoToB" method access View controller B's instance and push it in navigation controller
5) On View controller B's "Go Back" method write code to pop it.
6) Dont forget to set storyboard Id on both A & B view controller
class FirstViewController: UIViewController
{
lazy var secondViewController : SecondViewController? =
{
let secondViewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "SecondViewController") as? SecondViewController
return secondViewController
}()
override func viewDidLoad()
{
super.viewDidLoad()
}
#IBAction func goToB(sender : UIButton)
{
guard let secondViewController = self.secondViewController else
{
return
}
self.navigationController?.pushViewController(secondViewController, animated: true)
}
}
class SecondViewController: UIViewController
{
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func goBack(sender : UIButton)
{
self.navigationController?.popViewController(animated: true)
}
}

Navigate from NavigationController to another NavigationController with a back button

Hello,
so i have this storyboard structure i have a button in ViewController B i need that button to point to the bottom NavigationController in which it has the ViewController C as rootViewController so when i press a button in ViewContoller B it takes me to C now i need a back button on C to take me back to B
i have tried looking but all of the reference materials show how to navigate between ViewController not NavigationControllers
I think you cannot push Navigation Controller inside another uinavigation controller but alternativaly you can present another nav controller.
Hope it works for you.
I have created simple demo like this.
class ViewController: UIViewController {
#IBOutlet weak var btnCustom: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func btnForward(_ sender: Any) {
/*Uncomment the code if you want to present programatically*/
/*if let navi2 = self.storyboard?.instantiateViewController(withIdentifier: "nav2"){
self.present(navi2, animated: true) {
}
}*/
}
}
class ViewController2: UIViewController {
#IBOutlet weak var btnCustom: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func btnBack(_ sender: Any) {
if let parentNav = self.navigationController {
parentNav.dismiss(animated: true) {
}
}
}}
You can use a single navigation controller, but overwrite the back buttons action.
By default it will pop the current view controller from the navigation stack and go to the previous one.
On the back action in VC B just push to the VC C, you can do this either in storyBoard by drag-drop from the back button to VC C or by code by writing:
self.navigationControlller.push(VC C)
If using 2 navigationControllers is a requirement that cannot be avoided, you can solve the problem statement in the following way.
In ViewControllerC on a UIButton's tap event, get the instance of ViewControllerB in the parent navigationController and pop to it like so,
#objc func backAction() {
let parentNav = self.navigationController?.navigationController
if let vcB = parentNav?.viewControllers.first(where: { $0 is ViewControllerB }) {
parentNav?.popToViewController(vcB, animated: true)
}
}
Add the backAction() to backButton's target like,
backButton.addTarget(self, action: #selector(backAction), for: .touchUpInside)
Put this code inside your C's viewController back button action method. Also you have to give identifiers names to your views in the storyboard screen.
...
//It possible to change between different storyboards, but in your case all the views share the same storyboard.
UIStoryboard* storyboardOfA_B = [UIStoryboard storyboardWithName: #"storyboardOfA_B" bundle:nil];
UINavigationController* navigationControllerOfA_B = [storyboardOfA_B instantiateViewControllerWithIdentifier:#"navigationControllerOfA_B"];
UIViewController* viewControllerB = [storyboardOfA_B instantiateViewControllerWithIdentifier:#"viewControllerB"];
//I'm supposing that you have the action method of the back button in the C's view controller so I use self.
UINavigationController* navigationControllerOfC = self.navigationController;
[navigationControllerOfC pushViewController: navigationControllerOfA_B animated:NO];
[navigationControllerOfA_B pushViewController: viewControllerB animated:NO];
...
Maybe this is not the best way but is a way

Switching containerView content from within viewController

I have three viewControllers Main, A, B. Main ViewController holds ContainerView and other content as well does all transactions in containerView. ViewControllerA has ButtonA when pressing it content of container has to change to ViewControllerB
how can I do that? I cannot find any similar examples.
You will need to create create delegate for that.
First create a protocol
protocol ViewControllerADelagate {
func didPressOnButtonA()
}
In ViewControllerA add following delegate variable
class ViewControllerA {
....
var delegate : ViewControllerADelagate?
....
}
In ViewControllerA add following on button press
#IBAction buttonAPressed(sender : UIButton) {
self.delegate?.didPressOnButtonA()
}
In MainViewController assign the delegate of ViewControllerA to self
like
vcA.delegate = self
Implement the delegate method in MainViewController like
func didPressOnButtonA {
let storyboard : UIStoryboard = UIStoryboard(name: storyboard, bundle: nil)
let vcB : UIViewController = storyboard.instantiateViewController(withIdentifier: viewControllerIdentifier) as! ViewControllerB
self.addChildViewController(vcB)
self.containerView.addSubview(vcB.view)
vcB.didMove(toParentViewController: self)
vcB.view.frame = CGRect.init(x: 0, y: 0, width: self.containerView.frame.size.width, height: self.containerView.frame.size.height)
}
While click on ButtonA. Post a notification to mainView. There remove viewController A from Container and add View Controller B .
I have created a storybaord with sample. you can download it from here.
You need to change the embed view to a navigation controller and then you can use segue to show second view on button press. also hide/show navigation bar depends on requirement.
I am not fond of using these but you can get the child view controller from parent controller by accessing an array childViewControllers. On view did load you would need to go through all of these and find the one that can be typecast into your view controller type like
childViewControllers.flatMap { $0 as? AViewController }.first.
Now that you found the correct view controller I suggest you to either assign yourself as a delegate
childViewControllers.flatMap { $0 as? AViewController }.first.delegate = self
or simply add a button target
childViewControllers.flatMap { $0 as? AViewController }.first.button.addTarget...
Now this can easily be done if you simply embed the 2 view controllers at the same time (have 2 content views) and hide one or the other depending on which you show. At least this way you can assign connection straight away. When this is not the case then you will need to iterate again when setting a new controller or assign connections where you initialize a new view controller.
In this case it then seems better to turn the system around: When child view controller is loaded rather call
self.delegate = parentViewController as? AViewControllerDelegate
Although this will work it seems wrong that a child view controller will control who its listener is so I advise you to avoid such coding.
I in general use a custom implementation for container view which you could do the same or maybe at least subclass the native one and target the methods so that your code will look something like:
private onBPressed() {
containerView.setViewController(viewController: BViewController(delegate: self), animation: .fade)
}
When you pressing Button A from View A:
#IBAction func BtnAPress(_ sender: Any)
{
//Moving Storyboard
let Storyboard = UIStoryboard(name: "Main", bundle: nil)
let MainVC : UIViewController = Storyboard.instantiateViewController(withIdentifier: "ViewControllerB")
self.present(MainVC, animated: true, completion: nil)
}

Swift how to create a protocol delegate from root VC to embedded VC

I have a root VC that embeds a table view through a container view with segue. So that makes both the root VC and child VC visible at the same time.
if segue.identifier == "TableSegue" {
let toView = segue.destinationViewController as! TableViewController
toView.delegate = self
}
How can I implement a protocol delegate between the root vc and child vc since the child VC is embedded inside the root vc?
What I want to do is to have a functions fired in the child VC once a button in root VC is clicked.
I have tried to implement a protocol delegate the normal way but it seems to not be picked up in the child VC
protocol TableViewInterface {
func someWork()
}
class RootVC:UIViewController {
var delegate: TableViewInterface?
func callDelegate() {
delegate?.someWork()
}
}
class TableViewController: UITableViewController, TableViewInterface {
func someWork() {
//Perform your the work you want done or the action you want fired in your tableView here...
}
}
The above is an example of a standard delegate pattern in swift. It looks like you are trying to set the delegate in prepareForSegue(), but IRRC it won't get called with a containerView. You probably want to get a reference to you tableView through your container view.
You should be able to get a reference to it by doing something like this in your RootVC
if let tableVC = childViewControllers[0] as? TableViewController {
self.tableViewController = tableVC
}
I hope this all makes sense

How to redraw my view in SWIFT?

On my iPad app, I have a UIViewController with a button that open a modalView.
#IBAction func showPostCommentViewController(sender: AnyObject){
let modalView = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("PostCommentViewController") as! PostCommentViewController
modalView.modalTransitionStyle = UIModalTransitionStyle.CoverVertical
modalView.modalPresentationStyle = UIModalPresentationStyle.FormSheet
modalView.delegate=self
self.presentViewController(modalView, animated: true, completion: nil)
}
When I close the modalView with dismissViewControllerAnimated, I would like "refresh" my view controller (because I added new content). But as the modal view is a "formsheet" style, viewDidAppear or viewWillAppear aren't called.
I tried to use setNeedsDisplay, but it doesn't work.
I don't know how to do.
This would be a perfect use case for the delegate pattern.
1) define a protocol within PostCommentViewController.
protocol PostCommentVCInformationDelegate {
func hasDismissedPostCommentViewController(controller:PostCommentViewController)
}
2) Set a delegate variable within PostCommentViewController
var delegate: PostCommentVCInformationDelegate?
3) When you dismiss PostCommentViewController, you will call delegate?.hasDismissedPostCommentViewController(self)
This will send information back to the presenting VC.
4) Now we have our presenting View Controller conform to this protocol.
class ViewController: UIViewController, PostCommentVCInformationDelegate
5) When presenting the modal View:
modalView.delegate = self
6) Finally, we implement:
func hasDismissedPostCommentViewController(controller: PostCommentViewController) {
//Update
}

Resources