I'm trying to write a little extension in Swift to handle instantiation of a UIViewController from a storyboard.
My idea is the following: Since UIStoryboard's method instantiateViewControllerWithIdentifier needs an identifier to instantiate a given storyboard's view controller, why don't assign every view controller in my storyboard an identifier equal to its exact class name (i.e a UserDetailViewController would have an identifier of "UserDetailViewController"), and, create a class method on UIViewController that would:
accept a UIStoryboard instance as a unique parameter
get the current class name as a string
call instantiateViewControllerWithIdentifier on the storyboard instance with the class name as a parameter
get the newly created UIViewController instance, and return it
So, instead of (which repeats the class name as a string, not very nice)
let vc = self.storyboard?.instantiateViewControllerWithIdentifier("UserDetailViewController") as UserDetailViewController
it would be:
let vc = UserDetailViewController.instantiateFromStoryboard(self.storyboard!)
I used to do it in Objective-C with the following category:
+ (instancetype)instantiateFromStoryboard:(UIStoryboard *)storyboard
{
return [storyboard instantiateViewControllerWithIdentifier:NSStringFromClass([self class])];
}
But I'm completely stuck with the Swift version. I hope is that there is some kind of way to do it.
I tried the following:
extension UIViewController {
class func instantiateFromStoryboard(storyboard: UIStoryboard) -> Self {
return storyboard.instantiateViewControllerWithIdentifier(NSStringFromClass(Self))
}
}
Returning Self instead of AnyObject allows the type inference to work. Otherwise, I would have to cast every single return of this method, which is annoying, but maybe you have a better solution?
This gives me the error: Use of unresolved identifier 'Self'
The NSStringFromClass part seems to be the problem.
What do you think?
Is there any way to return Self from class functions?
How would you get this working without the need to cast the return value every time? (i.e keeping -> Self as return value)
How about writing an extension to UIStoryboard instead of UIViewController?
extension UIStoryboard {
func instantiateVC<T: UIViewController>() -> T? {
// get a class name and demangle for classes in Swift
if let name = NSStringFromClass(T.self)?.componentsSeparatedByString(".").last {
return instantiateViewControllerWithIdentifier(name) as? T
}
return nil
}
}
Even adopting this approach, cost of an use side is low as well.
let vc: UserDetailViewController? = aStoryboard.instantiateVC()
Thanks to MartinR and his answer, I know the answer:
UPDATE: rewritten with a protocol.
Instantiable
protocol StringConvertible {
var rawValue: String {get}
}
protocol Instantiable: class {
static var storyboardName: StringConvertible {get}
}
extension Instantiable {
static func instantiateFromStoryboard() -> Self {
return instantiateFromStoryboardHelper()
}
private static func instantiateFromStoryboardHelper<T>() -> T {
let identifier = String(describing: self)
let storyboard = UIStoryboard(name: storyboardName.rawValue, bundle: nil)
return storyboard.instantiateViewController(withIdentifier: identifier) as! T
}
}
//MARK: -
extension String: StringConvertible { // allow string as storyboard name
var rawValue: String {
return self
}
}
StoryboardName
enum StoryboardName: String, StringConvertible {
case main = "Main"
//...
}
Usage:
class MyViewController: UIViewController, Instantiable {
static var storyboardName: StringConvertible {
return StoryboardName.main //Or you can use string value "Main"
}
}
let viewController = MyController.instantiateFromStoryboard()
you can create UIViewController Instance like this:
Create enum with all your storyboard name.
enum AppStoryboard: String {
case main = "Main"
case profile = "Profile"
}
Then, here is the extension for instantiate UIViewController
extension UIViewController {
class func instantiate<T: UIViewController>(appStoryboard: AppStoryboard) -> T {
let storyboard = UIStoryboard(name: appStoryboard.rawValue, bundle: nil)
let identifier = String(describing: self)
return storyboard.instantiateViewController(withIdentifier: identifier) as! T
}
}
Usage:
let profileVC: ProfileVC = ProfileVC.instantiate(appStoryboard: .profile)
self.navigationController?.pushViewController(profileVC,animated:true)
We are porting our objective c project to swift. We have split the project into modules. Modules have their own storyboards. We have extended your(even our's as well) problem's solution to one more level by avoiding explicit storyboard names.
// Add you modules here. Make sure rawValues refer to a stroyboard file name.
enum StoryModule : String {
case SomeModule
case AnotherModule = "AnotherModulesStoryBoardName"
// and so on...
}
extension UIStoryboard {
class func instantiateController<T>(forModule module : StoryModule) -> T {
let storyboard = UIStoryboard.init(name: module.rawValue, bundle: nil);
let name = String(T).componentsSeparatedByString(".").last
return storyboard.instantiateViewControllerWithIdentifier(name!) as! T
}
}
// Some controller whose UI is in a stroyboard named "SomeModule.storyboard",
// and whose storyboardID is the class name itself, ie "MyViewController"
class MyViewController : UIViewController {
// Controller Code
}
// Usage
class AClass
{
// Here we must alwasy provide explicit type
let viewController : MyViewController = UIStoryboard.instantiateController(forModule: StoryModule.SomeModule)
}
Two things:
Class constructors in Objective-C are convenience initializers in Swift. Use convenience init rather than class func.
NSStringFromClass(Self) with NSStringFromClass(self.type).
Here is a modern Swift example, based on #findall's solution:
extension UIStoryboard {
func instantiate<T>() -> T {
return instantiateViewController(withIdentifier: String(describing: T.self)) as! T
}
static let main = UIStoryboard(name: "Main", bundle: nil)
}
Usage:
let userDetailViewController = UIStoryboard.main.instantiate() as UserDetailViewController
I think it is ok to fail when trying to instantiate a view controller from a storyboard as this kind of problem should be detected soon.
Or, you can do so
func instantiateViewControllerWithIdentifier<T>(_ identifier: T.Type) -> T {
let identifier = String(describing: identifier)
return instantiateViewController(withIdentifier: identifier) as! T
}
Use protocol in UIViewController to reach your thoughts
let vc = YourViewController.instantiate(from: .StoryboardName)
You can see the use of my link :D
https://github.com/JavanC/StoryboardDesignable
You can add this extension :-
extension UIStoryboard{
func instantiateViewController<T:UIViewController>(type: T.Type) -> T? {
var fullName: String = NSStringFromClass(T.self)
if let range = fullName.range(of:".", options:.backwards, range:nil, locale: nil){
fullName = fullName.substring(from: range.upperBound)
}
return self.instantiateViewController(withIdentifier:fullName) as? T
}
}
And can instantiate view controller like this :-
self.storyboard?.instantiateViewController(type: VC.self)!
In complement for the version of #ChikabuZ, here mine that takes into account which bundle the storyboard is in (for example, if your storyboads are in another bundle than your app). I added also a small func if you want to use xib instead of storyboad.
extension UIViewController {
static func instantiate<TController: UIViewController>(_ storyboardName: String) -> TController {
return instantiateFromStoryboardHelper(storyboardName)
}
static func instantiate<TController: UIViewController>(_ storyboardName: String, identifier: String) -> TController {
return instantiateFromStoryboardHelper(storyboardName, identifier: identifier)
}
fileprivate static func instantiateFromStoryboardHelper<T: UIViewController>(_ name: String, identifier: String? = nil) -> T {
let storyboard = UIStoryboard(name: name, bundle: Bundle(for: self))
return storyboard.instantiateViewController(withIdentifier: identifier ?? String(describing: self)) as! T
}
static func instantiate<TController: UIViewController>(xibName: String? = nil) -> TController {
return TController(nibName: xibName ?? String(describing: self), bundle: Bundle(for: self))
}
}
I had a similar thought and settled on using the extension below. It still uses the normal instantiation process, but removes reliance on stringly typed Storyboard and View Controller names:
let myVC = UIStoryboard(.main).instantiate(MyViewController.self)
The return type above is pre-cast to MyViewController, not the standard UIViewController.
extension UIStoryboard {
enum Name: String {
case main = "Main"
case launch = "LaunchScreen"
case other = "Other"
}
convenience init(_ name: Name, bundle: Bundle? = nil) {
self.init(name: name.rawValue, bundle: bundle)
}
func instantiate<T: UIViewController>(_ type: T.Type) -> T {
instantiateViewController(withIdentifier: String(describing: type)) as! T
}
}
Note that you must ensure that each VC's Storyboard Identifier exactly matches its class name! Failure to do so will result in the exception:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Storyboard (<UIStoryboard: 0x6000035c04e0>) doesn't contain a view controller with identifier 'MyViewController''
Related
I am trying to figure out a generic way to find a uiviewcontroller which is auto type casted. Currently, I do have this.
extension UINavigationController {
func contoller(ofType type:AnyClass) -> UIViewController? {
for controller in self.viewControllers {
if controller.isKind(of: type) {
return controller
}
}
return nil
}
}
Calling will be like:
if let controller = self.navigationController?.contoller(ofType: MyController.self) as? MyController{}
That's how I am able to get controller object and I need to type cast it also.
I am trying to figure out a way to do this like as:
if let controller:MyController = self.navigationController?.contoller(ofType: MyController.self){}
So that I will not need to do any type casting.
For this, I may need to do some changes in UINavigationController extension function.
Need some suggestion for this.
Use Generics:
extension UINavigationController {
func controller<T: UIViewController>(ofType _: T.Type) -> UIViewController? {
for controller in viewControllers where controller is T {
return controller
}
return nil
}
}
EDIT 1: I would return also the VC as type we requested.
And recommend to use first in the name, cause there might be more than one. And you might want to introduce lastController(as:) in the future
extension UINavigationController {
func firstController<T: UIViewController>(as _: T.Type) -> T? {
for case let controller as T in viewControllers {
return controller
}
return nil
}
}
Then usage could be:
let nav = UINavigationController()
nav.viewControllers = [UIViewController(), UITabBarController()]
let tabVC = nav.firstController(as: UITabBarController.self)
EDIT 2: You can shorten the extension body:
extension UINavigationController {
func firstController<T: UIViewController>(as _: T.Type) -> T? {
viewControllers.first(where: { $0 is T }) as? T
}
}
Hi I created a simple framework with a delegate to return back a value to my main app.
Steps I did:
Created a framework in Xcode
Created a protocol as public
Imported my framework in main app project and integrated my protocol successfully
But not able to grab the value from framework
In framework
public protocol MyDataSendingDelegateProtocol: NSObject {
func sendDataToFirstViewController(myData: String)
}
public var delegate: MyDataSendingDelegateProtocol? = nil
Sending a value form framework as
delegate?.sendDataToFirstViewController(myData: "hello world")
In the main app
class ViewController: UIViewController, MyDataSendingDelegateProtocol{
func sendDataToFirstViewController(myData: String) {
print("from frame work \(myData)")
}
}
Accessing my framework VC
public class OOB_View : UIViewController {
public func registraionView() -> UIViewController {
let storyboard = UIStoryboard.init(name: "main", bundle: Bundle(for: oobRegVc.self))
let homeVC = storyboard.instantiateViewController(withIdentifier: "view") as! oobRegVc
return homeVC
}
}
In your framework modify the registraionView method to accept the delegate parameter of type MyDataSendingDelegateProtocol and then set it as homeVC's delegate, i.e.
public class OOB_View : UIViewController {
public func registraionView(delegate: MyDataSendingDelegateProtocol) -> UIViewController {
let storyboard = UIStoryboard.init(name: "main", bundle: Bundle(for: oobRegVc.self))
let homeVC = storyboard.instantiateViewController(withIdentifier: "view") as! oobRegVc
homeVC.delegate = delegate
return homeVC
}
}
Now, in your main app, call the method registraionView(delegate:) like so,
let oobClass = OOB_View()
let x = oobClass.registraionView(delegate: self)
x.modalPresentationStyle = .fullScreen
self.present(x, animated: true, completion: nil)
The above code must be somewhere in the ViewController conforming to MyDataSendingDelegateProtocol.
I did a class Router (subclass of UINavigationController) in my project for centralize and minimize the code used to instantiate and navigate between views. But when i try minimize a particular function (buildView), the type casting not work. But this works fine out of scope of function buildView, same whithout the as! operator in function goHome.
enum Routes {
case home
case account
var file: String {
switch self {
case .home:
return "HomeView"
case .account:
return "AccountView"
}
}
protocol HomeInterface: class {
func goTo(view: Routes)
func showModal(view: Routes, caller: UIViewController)
}
class HomePresenter: NSObject, HomeInterface {
init(view: HomeViewInterface) {
self.view = view
}
internal func goTo(view: Routes) { /* Implementation */ }
internal func showModal(view: Routes, caller: UIViewController) {/* Implementation */ }
}
protocol HomeViewInterface: class {
/* Implementation */
}
class HomeViewController: UIViewController, HomeViewInterface {
var presenter: HomeInterface?
override func viewDidLoad() {
super.viewDidLoad()
}
/* Implementation */
}
Working Code
func goHome() {
let viewInstance = buildView(view.file, HomeViewController.identifier, HomeViewController.self)
viewInstance.presenter = HomePresenter(view: viewInstance)
self.view?.pushViewController(viewInstance, animated: true)
}
private func buildView<T>(_ nameFile: String, _ identifier: String, _ viewClass: T.Type) -> T {
return UIStoryboard(name: nameFile, bundle: nil).instantiateViewController(withIdentifier: identifier) as! T
}
Desired final code, but does not work:
func goHome() {
buildViewFinal(view.file, HomeViewController.identifier, HomeViewController.self)
}
func buildViewFinal<T, P>(_ nameFile: String, _ identifier: String, viewClass: T, presenter: P) {
let viewInstance = UIStoryboard(name: nameFile, bundle: nil).instantiateViewController(withIdentifier: identifier) as? T
viewInstance.presenter = P(view: viewInstance)
self.view?.pushViewController(viewInstance, animated: true)
}
When i try minimize the code only to buildViewFinalfunction, the property presenter of viewInstance is not recognize, showing a compile error
Value of type 'T?' has no member 'presenter'
, and in pushViewControllershow error:
Cannot convert value of type 'T?' to expected element type
'UIViewController'
The main goal is turn all code to create and navigate useful and simple.
So, how this works fine in first code, but fails in recognize type inside buildViewFinal scope?
In your first piece of code, you are passing HomeViewController.self as viewClass and so it knows that buildViewFinal is going to return an instance of HomeViewController and that HomeViewController has a presenter property.
In the second code snippet, the compiler doesn't know anything about T, so it can't assume that it will have a presenter property.
You could use type constraint to enforce that T is a HomeViewController or whatever class defines the presenter property:
func buildViewFinal<T: HomeInterface, P>(_ nameFile: String, _ identifier: String, viewClass: T, presenter: P) {
let viewInstance = UIStoryboard(name: nameFile, bundle: nil).instantiateViewController(withIdentifier: identifier) as? T
viewInstance.presenter = P(view: viewInstance)
self.view?.pushViewController(viewInstance, animated: true)
}
but then you will have a problem that viewInstance can't be pushed because the compiler doesn't know that it is an instance of a UIViewController subclass.
Really generics and protocols are just complicating things here.
You are dealing with UIKit which is class oriented, so you might as well just use good old inheritance
Swift 3.0
From what I found here is UIStoryboard always return non-optional instance in function instantiateViewController(withIdentifier:).
open class UIStoryboard : NSObject {
...
open func instantiateViewController(withIdentifier identifier: String) -> UIViewController
}
The crash happen if we adding wrong identifier value, without noticed.
This case might happen in complex projects which have large number of controller in many storyboards, and a controller StoryboardId is missed.
The only solution I found is making a function to temporary create all controllers when start app delegate (this function should be called in debugging mode only).
func validateControllers() {
guard let _ = xyzStoryboard.instantiateViewController(withIdentifier: "ABC") as? ABCViewController else {
fatalError("fail in init controller ABC from storyboard XYZ")
}
...
}
But I wonder if we could have another way to handle this situation. Or should I raise this issue to Swift team? Thanks!
as far as i know there is no solution for preventing crashes like this. but this is a good thing and it's meant to crash like this to show you there is something wrong with your code and you need to fix them!
Writing this answer to just to let you know how we handled the accidental typo(s) of a View Controller Identifier, which could lead to error when you try to create an ViewController from Storyboard(s) using view controller's identifier.
We had a complex project which had almost 15-20 ViewControllers, we didn't put them in a single storyboard instead we shared these VCs across multiple story boards and then we created an object called StoryBoardManager, which would create an VC from various storyboard(s) and hand it over to us.
We also created couple of enums to represent various storyboard(s) and viewController(s) inside.
It somewhat looks like this,
enum Storyboards : String {
case main = "Main"
case signup = "SignUp"
case discover = "Discover"
case utility = "Utility"
case event = "Event"
}
enum ViewControllers : String {
case login = "login"
case onBoard = "on_board"
case signup = "signup"
case signupNavigation = "signupNavigaitonVC"
case discoverNavigation = "discoverNavigation"
case individualProfileSetUp = "individualProfileSetUp"
case organizationProfileSetUp = "organizationProfileSetUp"
case discover = "discover"
case imagePickerMenuVC = "imagePickerMenuVC"
case eventDiscoverMapNavigation = "eventDiscoverMapNavigationVC"
case eventDiscoverMapVC = "eventDiscoverMapVC"
case event = "event"
}
class StoryboardManager {
static func storyboard(name:Storyboards) -> UIStoryboard {
let aStoryboard = UIStoryboard(name: name.rawValue, bundle: nil)
return aStoryboard
}
static func viewController(storyboardId:Storyboards, viewControllerId:ViewControllers) -> UIViewController {
let storyboard = StoryboardManager.storyboard(storyboardId)
let viewcontroller = storyboard.instantiateViewControllerWithIdentifier(viewControllerId.rawValue)
return viewcontroller
}
}
We mainly did this to avoid the typo mistakes for the ViewController identifier(s), which would lead to an runtime error. You will add all the viewController(s) identifiers to ViewControllers enum and storyboard(s) names to the Storyboards enum as a enum case. We updated both the enums whenever we introduced a new storyboard or a new ViewController in any of the storyboards and this helped all the team members not to make the typo for ViewController identifier.
Then we created ViewController(s) using the below code,
let loginVC = StoryboardManager.viewController(.main, viewControllerId: .login)
HTH :)
Short brief
Currently, I have a complex project that have 5 storyboards, with 20 controllers. In my case, using storyboard segue seems not a good idea. Therefore, creating controller's instance from storyboard and make a transition by code is much better.
But the problem is that instantiateViewController(withIdentifier:) always return non-optional instance, and it will be crashed if we put an invalid identifier (which didn't define in storyboard).
Solution (Swift 3.0)
There are steps to prevent this problem.
In *.storyboard, use controller class name as StoryboardID
Create UIStoryboard+Ext.swift to define all storyboards & controllers belong
Create BaseViewController.swift to define the initialization from storyboard
Create function to init all storyboards & controllers, to prevent crashing in runtime.
Download sample code from if you want to try yourself: https://github.com/nahung89/StoryboardPattern
Detail (TL;DR)
Step 1:
Step 2:
extension UIStoryboard {
enum Identifier: String {
case main = "Main"
case explore = "Explore"
case search = "Search"
case profile = "Profile"
}
static func name(for controller: UIViewController.Type) -> String? {
var identifier: Identifier?
switch controller.className {
case HomeViewController.className:
identifier = .main
case ExploreViewController.className:
identifier = .explore
case SearchViewController.className:
identifier = .search
case ProfileViewController.className:
identifier = .profile
default:
break
}
return identifier?.rawValue
}
}
extension UIStoryboard {
static func instanceFromIdentifier(_ identifier: Identifier) -> UIStoryboard {
return UIStoryboard(name: identifier.rawValue, bundle: Bundle.main)
}
static func instanceFromName(_ name: String) -> UIStoryboard? {
guard let identifier = Identifier(rawValue: name) else { return nil }
return instanceFromIdentifier(identifier)
}
}
Step 3:
extension UIViewController {
var className: String {
return String(describing: type(of: self))
}
class var className: String {
return String(describing: self)
}
}
class BaseViewController: UIViewController {
static var controllerId: String {
return String(describing: self) // return slass name, i.e "ExploreViewController"
}
static func instanceFromStoryboard() -> Self? {
guard let storyboardName = UIStoryboard.name(for: self) else { return nil }
return instantiateFrom(storyboardName: storyboardName)
}
private static func instantiateFrom<VC: UIViewController>(storyboardName: String) -> VC? {
let storyboard = UIStoryboard(name: storyboardName, bundle: nil)
let controller = storyboard.instantiateViewController(withIdentifier: controllerId) as? VC
return controller
}
}
Step 4
extension AppDelegate {
func validateStoryboards() {
guard
let _ = UIStoryboard.instanceFromName(UIStoryboard.Identifier.main.rawValue),
let _ = UIStoryboard.instanceFromName(UIStoryboard.Identifier.explore.rawValue),
let _ = UIStoryboard.instanceFromName(UIStoryboard.Identifier.profile.rawValue),
let _ = UIStoryboard.instanceFromName(UIStoryboard.Identifier.search.rawValue)
else {
fatalError("fail to init storyboard by name")
}
guard let _ = HomeViewController.instanceFromStoryboard(),
let _ = ExploreViewController.instanceFromStoryboard(),
let _ = ProfileViewController.instanceFromStoryboard(),
let _ = SearchViewController.instanceFromStoryboard()
else {
fatalError("fail to init controller from storyboard")
}
}
}
Question:
How might one write a custom segue that would allow you to embed view controllers from a different storyboard?
Context:
I am trying to write a custom segue with which I can link from one storyboard to another. A good article on atomicobject.com illustrates how to create a segue that originates from a button / event etc. Translated into swift, and allowing for non UINavigationControllers, the code looks like:
public class SegueToStoryboard : UIStoryboardSegue {
private class func viewControllerInStoryBoard(identifier:String, bundle:NSBundle? = nil)
-> UIViewController?
{
let boardScene = split(identifier, { $0 == "." }, maxSplit: Int.max, allowEmptySlices: false)
switch boardScene.count {
case 2:
let sb = UIStoryboard(name: boardScene[0], bundle: bundle)
return sb.instantiateViewControllerWithIdentifier(boardScene[1]) as? UIViewController
case 1:
let sb = UIStoryboard(name: boardScene[0], bundle: bundle)
return sb.instantiateInitialViewController() as? UIViewController
default:
return nil
}
}
override init(identifier: String!,
source: UIViewController,
destination ignore: UIViewController) {
let target = SegueToStoryboard.viewControllerInStoryBoard(identifier, bundle: nil)
super.init(identifier: identifier, source: source,
destination:target != nil ? target! : ignore)
}
public override func perform() {
let source = self.sourceViewController as UIViewController
let dest = self.destinationViewController as UIViewController
source.addChildViewController(dest)
dest.didMoveToParentViewController(source)
source.view.addSubview(dest.view)
// source.navigationController?.pushViewController(dest, animated: true)
}
}
Problem:
The problem that I am having with both their Obj-C and the above Swift code is that when you try to use the via a container view (with semantics of an embed segue - starting with an embed segue, deleting the segue, and then use the above custom segue), it crashes before ever calling the segue code with the following method-not-found error:
*** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<UIStoryboardSegueTemplate 0x7ffc8432a4f0>
setValue:forUndefinedKey:]: this class is not key value
coding-compliant for the key containerView.'
I have tried to inspect the address listed but get no meaningful results. I do the see the bold statement that it expecting the containerView but don't know how one might isolate, satisfy, and/or work around this problem.
Summary:
My end goal is to embed view controllers defined in separate storyboards to facilitate collaboration and testing without having to write additional code (a non invasive solution). Does anyone have any insight into how to accomplish this greater task? I could fall back to hybrid approach of calling performSegue, but would like to find a single, contained, and complete solution. The above code gets there for event driven (buttons etc) segues, but not with the embed segue.
Any input is appreciated, thanks in advance.
Your approach works fine for custom segues to push / display modally other view controllers but not for embed segues. The reason for this is that the "Embed" segue is not a subclass of UIStoryboardSegue but inherits from UIStoryboardSegueTemplate, which is a private API.
Unfortunately I couldn't find a better way to achieve what you want than with the hybrid approach.
My way is to link the containerView and delete the viewDidLoad segue from it. and manually call the segue on viewdidLoad
public protocol EmbeddingContainerView {
var containerView: UIView! { get set }
}
public class CoreSegue: UIStoryboardSegue {
public static func instantiateViewControllerWithIdentifier(identifier: String) -> UIViewController {
let storyboard = UIStoryboard(name: "Core", bundle: NSBundle(forClass: self))
let controller = storyboard.instantiateViewControllerWithIdentifier(identifier) as! UIViewController
return controller
}
var isPresent = false
var isEmbed = false
override init!(identifier: String?, source: UIViewController, destination: UIViewController) {
if var identifier = identifier {
if identifier.hasPrefix("present ") {
isPresent = true
identifier = identifier.substringFromIndex(advance(identifier.startIndex, count("present ")))
}
if identifier.hasPrefix("embed ") {
isEmbed = true
identifier = identifier.substringFromIndex(advance(identifier.startIndex, count("embed ")))
}
let controller = CoreSegue.instantiateViewControllerWithIdentifier(identifier)
super.init(identifier: identifier, source: source, destination: controller)
} else {
super.init(identifier: identifier, source: source, destination: destination)
}
}
public override func perform() {
if let source = sourceViewController as? UIViewController, dest = destinationViewController as? UIViewController {
if isPresent {
let nav = UINavigationController(rootViewController: dest)
nav.navigationBarHidden = true // you might not need this line
source.presentViewController(nav, animated: true, completion: nil)
} else if isEmbed {
if let contentView = (source as? EmbeddingContainerView)?.containerView {
source.addChildViewController(dest)
contentView.addSubview(dest.view)
dest.view.fullDimension() // which comes from one of my lib
}
} else {
source.navigationController?.pushViewController(destinationViewController as! UIViewController, animated: true)
}
}
}
}
and later in your code:
class MeViewController: UIViewController, EmbeddingContainerView {
#IBOutlet weak var containerView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
performSegueWithIdentifier("embed Bookings", sender: nil)
}
}