How to Instantiate a View Controller Programatically, Without Storyboard

Swift instantiateViewController without storyboard ?programming

Yes you can init you're controller with nib name:

let main = UIViewController(nibName: "Second", bundle: nil)

Also you can call constructor without parameters like:

let main = UIViewController()

How to create ViewControllers without storyboard and set one as delegate of the other one?

Note: My previous answer was using Storyboard. But since the questioner didn't want to use storyboard, I replace my answer without using storyboard.
[ this answer was inspired by https://stackoverflow.com/a/41095757/3549695 ]

First, delete the Main.storyboard. Then in Project -> Deployment Info -> Main Interface (pick LaunchScreen instead of 'Main')Sample Image

Then on AppDelegate.swift modify didFinishLaunching with the following:

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {

window = UIWindow(frame: UIScreen.main.bounds)
let discoverVC = DiscoverVC() as UIViewController
let navigationController = UINavigationController(rootViewController: discoverVC)
navigationController.navigationBar.isTranslucent = false
self.window?.rootViewController = navigationController
self.window?.makeKeyAndVisible()

return true
}

The DiscoverVC.swift looks like this:

import UIKit

class DiscoverVC: UIViewController, SetLocationDelegate {

var name = ""

// to instantiate LocationVC once only for testing
var notVisted = true

override func viewDidLoad() {
super.viewDidLoad()

self.view.backgroundColor = .yellow
loadLocationVCOnlyOnce()
}

func loadLocationVCOnlyOnce() {
// only visit one
guard notVisted else { return }

let locationVC = LocationVC()
locationVC.delegate = self
self.navigationController?.pushViewController(locationVC, animated: true)

}

func getLocation(loc: String) {
self.name = loc
print(name)
}
}

And the LocationVC looks like this:

import UIKit

protocol SetLocationDelegate: class {
func getLocation(loc: String)
}

class LocationVC: UIViewController {

weak var delegate: SetLocationDelegate?

override func viewDidLoad() {
super.viewDidLoad()

self.view.backgroundColor = .cyan
self.delegate?.getLocation(loc: "Sam")

}

}

When you start it will automatically move from DiscoverVC (yellow background) to LocationVC (cyan background).

Then after you click the 'Back' button on top, you will see the 'Sam' printed in your console. And your view returned to DiscoverVC (yellow background).

push ViewController without storyboard

Inside AppDelegate's didFinishLaunchingWithOptions do

let fir = FirstVC()
self.window?.rootViewController = UINavigationController(rootViewController: fir)

Then this

self.navigationController?.pushViewController(vc, animated: true)

should work

Swift 5. How present or show ViewController on button touch? Without Storyboard (programmatically)

First, using Storyboards

If you are working with storyboard. You should connect your button view storyboard.

@IBAction fileprivate func handlePresentingView(_ sender: UIButton) {
let vc = SecondVC()
present(vc, animated: true, completion: nil)
}

Second, programmatically

1: If you are working programmatically
in viewDidLoad add the following line.

    mybutton.addTarget(self, action: #selector(handlePresentingVC(_:)), for: .touchUpInside)

2: Your action method

  @objc func handlePresentingVC(_ sender: UIButton) {
let vc = SecondVC()
present(vc, animated: true, completion: nil)
}

In my example, I'm assuming that you don't have a storyboard file for
your SecondVC view controller.

If SecondVC is connected to a storyboard view controller, you will
need to change the instantiation of your secondVC object inside of
your button's action method.

1: Select your SecondVC's view controller in your storyboard.

2: Add a Storyboard ID to it.

3: Change your button's action method to the following.

  @objc func handlePresentingVC(_ sender: UIButton) {
let storyboard = UIStoryboard.init(name: "Main", bundle: nil)
let secondVc = storyboard.instantiateViewController(withIdentifier: "SecondVC") as! SecondVC

present(secondVc, animated: true, completion: nil)
}

In Swift 5 and iOS 13

The default modal presentation style is a card. This shows the previous view controller at the top and allows the user to swipe away the presented view controller.

To retain the old style you need to modify the view controller inside of your button's action method like this:

secondVc.modalPresentationStyle = .fullScreen

This is the same for both programmatically created and storyboard created controllers.

Present ViewController Segue Transition Without Storyboard [Swift]

Each time you call StartViewController() you are creating a new one. Instead, put that into a constant so that you can refer to the same one:

let modalStyle = UIModalTransitionStyle.CrossDissolve
let svc = StartViewController()
svc.modalTransitionStyle = modalStyle
presentViewController(svc, animated: true, completion: nil)

You can skip creating modalStyle and just set the modalTransitionStyle directly:

let svc = StartViewController()
svc.modalTransitionStyle = .CrossDissolve
presentViewController(svc, animated: true, completion: nil)

How to instantiate a view controller programatically, without storyboard

The problem with doing this is that NONE of the UI for your view controller is created. What you are doing is simply instantiating an instance of the class. None of the corresponding UI is created, also your IBOutlets will be nil.

You have 2 options, either you use Interface builder and instantiate from the Nib or Storyboard, or you create ALL your UI manually in code.

For the sake of resuability you could create a static method on CNContactPickerViewController to handle the storyboard instantiation for you. See the following:

class CBContactPickerViewController: UIViewController {

static func fromStoryboard() -> CNContactPickerViewController {
return UIStoryboard(name: "foobar", bundle: nil).instantiateViewController(identifier: "CNContactPickerViewController") as! CNContactPickerViewController
}
}

You can then utilise this as follows:

self.present(viewController: CNContactPickerViewController.fromStoryboard(), animated: true, completion: nil)

How to open view controllers in a container view without storyboard

Let's assume we have four view controllers: RedViewController, GreenViewController, BlueViewController, and the one to contain them all, ContainerViewController.

Although you mentioned a scrolling view controller with three children within, we'll make it a two screen setup to keep it simple.
The following approach is scalable, so you would easily adopt it with an arbitrary number of view controllers.

Our RedViewController is 7 lines long:

class RedViewController: UIViewController {
override func loadView() {
let view = UIView()
view.backgroundColor = .red
self.view = view
}
}

Before we move on to GreenViewController and BlueViewController, we will define protocol SwapViewControllerDelegate:

protocol SwapViewControllerDelegate: AnyObject {
func swap()
}

GreenViewController and BlueViewController will have a delegate that conforms to this protocol, which will handle the swapping.
We will make ContainerViewController conform to this protocol.

Note that SwapViewControllerDelegate has the AnyObject in its inheritance list to make it a class-only protocol–we can thus make the delegate weak, to avoid memory retain cycle.

The following is GreenViewController:

class GreenViewController: UIViewController {
weak var delegate: SwapViewControllerDelegate?

override func loadView() {
let view = UIView()
view.backgroundColor = .green
self.view = view
}

override func viewDidLoad() {
super.viewDidLoad()

let button = UIButton()
button.setTitle("Swap Me!", for: .normal)
button.setTitleColor(.black, for: .normal)
button.titleLabel?.font = .boldSystemFont(ofSize: 50)
button.addTarget(
self,
action: #selector(swapButtonWasTouched),
for: .touchUpInside)

view.addSubview(button)

// Put button at the center of the view
button.translatesAutoresizingMaskIntoConstraints = false
button.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
button.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
}

@objc private func swapButtonWasTouched(_ sender: UIButton) {
delegate?.swap()
}
}

It has weak var delegate: SwapViewControllerDelegate? which will handle the swap when the button added in viewDidLoad is touched, triggering the swapButtonWasTouched method.

BlueViewController is implemented likewise:

class BlueViewController: UIViewController {
weak var delegate: SwapViewControllerDelegate?

override func loadView() {
let view = UIView()
view.backgroundColor = .blue
self.view = view
}

override func viewDidLoad() {
super.viewDidLoad()

let button = UIButton()
button.setTitle("Swap Me!", for: .normal)
button.setTitleColor(.white, for: .normal)
button.titleLabel?.font = .boldSystemFont(ofSize: 50)
button.addTarget(
self,
action: #selector(swapButtonWasTouched),
for: .touchUpInside)

view.addSubview(button)

// Put button at the center of the view
button.translatesAutoresizingMaskIntoConstraints = false
button.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
button.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
}

@objc private func swapButtonWasTouched(_ sender: UIButton) {
delegate?.swap()
}
}

The only difference is the view's backgroundColor and the button's titleColor.

Finally, we'll take a look at ContainerViewController.
ContainerViewController has four properties:

class ContainerViewController: UIViewController {

let redVC = RedViewController()
let greenVC = GreenViewController()
let blueVC = BlueViewController()

private lazy var scrollView: UIScrollView = {
let scrollView = UIScrollView()
scrollView.translatesAutoresizingMaskIntoConstraints = false
scrollView.bounces = false
scrollView.isPagingEnabled = true
return scrollView
}()
...
}

scrollView is the view that will contain child view controllers, redVC, greenVC, and blueVC.
We will use autolayout, so don't forget to mark translatesAutoresizingMaskIntoConstraints as false.

Now, setup autolayout constraints of the scrollView:

class ContainerViewController: UIViewController {
...
private func setupScrollView() {
view.addSubview(scrollView)
let views = ["scrollView": scrollView]
[
NSLayoutConstraint.constraints(
withVisualFormat: "H:|[scrollView]|",
metrics: nil,
views: views),
NSLayoutConstraint.constraints(
withVisualFormat: "V:|[scrollView]|",
metrics: nil,
views: views),
]
.forEach { NSLayoutConstraint.activate($0) }
}
...
}

I used VFL, but you can manually set autolayou constraints as we did for the button above.
Using autolayout, we don't have to set contentSize of the scrollView ourselves.
For more information about using autolayout with UIScrollView, see Technical Note TN2154: UIScrollView And Autolayout.

Now the most important setupChildViewControllers():

class ContainerViewController: UIViewController {
...
private func setupChildViewControllers() {
[redVC, greenVC, blueVC].forEach { addChild($0) }

let views = [
"redVC": redVC.view!,
"greenVC": greenVC.view!,
"blueVC": blueVC.view!,
]
views.values.forEach {
scrollView.addSubview($0)
$0.translatesAutoresizingMaskIntoConstraints = false
$0.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
$0.heightAnchor.constraint(equalTo: view.heightAnchor).isActive = true
}

[
NSLayoutConstraint.constraints(
withVisualFormat: "H:|[redVC][greenVC]|",
options: .alignAllTop,
metrics: nil,
views: views),
NSLayoutConstraint.constraints(
withVisualFormat: "H:|[redVC][blueVC]|",
options: .alignAllTop,
metrics: nil,
views: views),
NSLayoutConstraint.constraints(
withVisualFormat: "V:|[redVC(==greenVC,==blueVC)]|",
metrics: nil,
views: views),
]
.forEach { NSLayoutConstraint.activate($0) }

[redVC, greenVC, blueVC].forEach { $0.didMove(toParent: self) }

greenVC.view.isHidden = true

greenVC.delegate = self
blueVC.delegate = self
}
...
}

We first add each of [redVC, greenVC, blueVC] as child view controllers of ContainerViewController.
Then add the view's of child view controllers to scrollView.
Set widthAnchor and heightAnchor of the child view controllers to be view.widthAnchor and view.heightAnchor, in order to make them fullscreen.
Moreover, this will also work when the screen rotates.

Using views dictionary, we use VFL to set autolayout constraints.
We will put greenVC.view on the right of redVC.view: H:|[redVC][greenVC]|, and similarly for the blueVC.view: H:|[redVC][blueVC]|.
To fix the vertical position of greenVC.view and blueVC.view, add .alignAllTop option to the constraints.
Then apply vertical layout for redVC.view, and set the height of the greenVC.view and blueVC.view: "V:|[redVC(==greenVC,==blueVC)]|.
The vertical position is set, as we used .alignAllTop while setting the horizontal constraints.

We should call didMove(toParent:) methods on the child view controllers after we add then as child view controllers.
(If you are wondering about what didMove(toParent:) and addChild(_:) methods do, apparently they do very little; see What does addChildViewController actually do? and didMoveToParentViewController and willMoveToParentViewController.)

Finally, hide greenVC.view, and set greenVC.delegate and blueVC.delegate to self.
Then of course, we need ContainerViewController to conform to SwapViewControllerDelegate:

extension ContainerViewController: SwapViewControllerDelegate {
func swap() {
greenVC.view.isHidden.toggle()
blueVC.view.isHidden.toggle()
}
}

That's it!
The entire project is uploaded here.

I recommend reading Implementing a Container View Controller, which is well-documented by Apple. (It is written in Objective-C, but it is actually straightforward to translate into Swift)

Swift – Instantiating a navigation controller without storyboards in App Delegate

In Swift 3

Place this code inside didFinishLaunchingWithOptions method in AppDelegate class.

window = UIWindow(frame: UIScreen.main.bounds)
let mainController = MainViewController() as UIViewController
let navigationController = UINavigationController(rootViewController: mainController)
navigationController.navigationBar.isTranslucent = false
self.window?.rootViewController = navigationController
self.window?.makeKeyAndVisible()

Instantiate UIViewController programmatically without nib

According to the docs:

If the view controller does not have an associated nib file, this method creates a plain UIView object instead.

https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIViewController_Class/#//apple_ref/occ/instp/UIViewController/view

If you do not provide a nib file when instantiating the view controller, UIViewController calls it's loadView() method which creates a plain UIView object and assigns it to its view property.

The reason why you are seeing a transparent view is because the UIView object has no background color. To verify this, you can set the background color of the view controller's view property right before you push it on your navigation stack.

let viewController = TestViewController()
viewController.view.backgroundColor = .blueColor()
navigationController?.pushViewController(viewController, animated: true)

How to Push a New View Without Storyboards Swift

You have to manually create a UINavigationcontrollerto get the back bar. To do this you can use the code from this answer, which achieves this by using this code:

self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
var nav1 = UINavigationController()
var mainView = ViewController() //ViewController = Name of your controller
nav1.viewControllers = [mainView]
self.window!.rootViewController = nav1
self.window?.makeKeyAndVisible()

Here just add all the ViewControllers you want to be under the Navigation controller into the array and then push between them.

Hope that helps, Julian



Related Topics



Leave a reply



Submit