Create an account

Very important

  • To access the important data of the forums, you must be active in each forum and especially in the leaks and database leaks section, send data and after sending the data and activity, data and important content will be opened and visible for you.
  • You will only see chat messages from people who are at or below your level.
  • More than 500,000 database leaks and millions of account leaks are waiting for you, so access and view with more activity.
  • Many important data are inactive and inaccessible for you, so open them with activity. (This will be done automatically)


Thread Rating:
  • 747 Vote(s) - 3.52 Average
  • 1
  • 2
  • 3
  • 4
  • 5
How to use single storyboard uiviewcontroller for multiple subclass

#11
Taking answers from here and there, I came up with this neat solution.

Create a parent view controller with this function.

```
class ParentViewController: UIViewController {


func convert<T: ParentViewController>(to _: T.Type) {

object_setClass(self, T.self)

}

}
```
This allows the compiler to ensure that the child view controller inherits from the parent view controller.

Then whenever you want to segue to this controller using a sub class you can do:

```
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
super.prepare(for: segue, sender: sender)

if let parentViewController = segue.destination as? ParentViewController {
ParentViewController.convert(to: ChildViewController.self)
}

}
```
The cool part is that you can add a storyboard reference to itself, and then keep calling the "next" child view controller.
Reply

#12
# There is a simple, obvious, everyday solution.

Simply put the existing storyboard/controller inside the new storyobard/controller. I.E. as a container view.

This is the exactly analogous concept to "subclassing", for, view controllers.

Everything works exactly as in a subclass.

Just as you commonly **put a view subview inside another view**, naturally you commonly put **a view controller inside another view controller**.

How else can could you do it?

It's a basic part of iOS, as simple as the concept "subview".

It's this easy ...

/*

Search screen is just a modification of our List screen.

*/

import UIKit

class Search: UIViewController {

var list: List!

override func viewDidLoad() {
super.viewDidLoad()

list = (_sb("List") as! List
addChild(list)
view.addSubview(list.view)
list.view.bindEdgesToSuperview()
list.didMove(toParent: self)
}
}

You now obviously have `list` to do whatever you want with

list.mode = .blah
list.tableview.reloadData()
list.heading = 'Search!'
list.searchBar.isHidden = false

etc etc.

Container views are "just like" subclassing in the same way that "subviews" are "just like" subclassing.

Of course obviously, you can't "sublcass a layout" - what would that even mean?

("Subclassing" relates to OO software and has no connection to "layouts".)

Obviously when you want to re-use a view, you just subview it inside another view.

When you want to re-use a controller layout, you just container view it inside another controller.

This is like the most basic mechanism of iOS!!

---

Note - for years now it's been trivial to dynamically load another view controller as a container view. Explained in the last section:

[To see links please register here]


Note - "_sb" is just an obvious macro we use to save typing,

func _sb(_ s: String)->UIViewController {
// by convention, for a screen "SomeScreen.storyboard" the
// storyboardID must be SomeScreenID
return UIStoryboard(name: s, bundle: nil)
.instantiateViewController(withIdentifier: s + "ID")
}

Reply

#13
Cocoabob's comment from Jiří Zahálka answer helped me to get this solution and it worked well.

func openChildA() {
let storyboard = UIStoryboard(name: "Main", bundle: nil);
let parentController = storyboard
.instantiateViewController(withIdentifier: "ParentStoryboardID")
as! ParentClass;
object_setClass(parentController, ChildA.self)
self.present(parentController, animated: true, completion: nil);
}
Reply

#14
Thanks for @Jiří Zahálka's inspiring answer, I replied my solution 4 years ago [here][1], but @Sayka suggested me to post it as an answer, so here it is.

In my projects, normally, if I'm using Storyboard for a UIViewController subclass, I always prepare a static method called `instantiate()` in that subclass, to create an instance from Storyboard easily. So for solve OP's question, if we want to share the same Storyboard for different subclasses, we can simply `setClass()` to that instance before returning it.

class func instantiate() -> SubClass {
let instance = (UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("SuperClass") as? SuperClass)!
object_setClass(instance, SubClass.self)
return (instance as? SubClass)!
}


[1]:

[To see links please register here]

Reply

#15
Basing particularly on [nickgzzjr][1] and [Jiří Zahálka][2] answers plus comment under the second one from CocoaBob I've prepared short generic method doing exactly what OP needs. You need only to check storyboard name and View Controllers storyboard ID

class func instantiate<T: BasicViewController>(as _: T.Type) -> T? {
let storyboard = UIStoryboard(name: "StoryboardName", bundle: nil)
guard let instance = storyboard.instantiateViewController(withIdentifier: "Identifier") as? BasicViewController else {
return nil
}
object_setClass(instance, T.self)
return instance as? T
}

Optionals are added to avoid force unwrap (swiftlint warnings), but method returns correct objects.

Also: you need to initialize properties existing only in subclass before reading them from casted objects (if subclass has those properties and `BasicViewController` does not). Those properties won't be initialized automatically and attempt to read them before initialization will lead to crash. Because they are there in effect of casting it's very likely that even weak variables won't be set to nil (will contain garbage).

[1]:

[To see links please register here]

[2]:

[To see links please register here]

Reply

#16
It is plain simple. Just define the BaseViewController in a xib and then use it like this:

let baseVC: BaseViewController = BaseViewController(nibName: "BaseViewController", bundle: nil)
let subclassVC: ChildViewController = ChildViewController(nibName: "BaseViewController", bundle: nil)

To make is simple you can extract the identifier to a field and the loading to a method like:

public static var baseNibIdentifier: String {
return "BaseViewController"
}

public static func loadFromBaseNib<T>() -> T where T : UIViewController {
return T(nibName: self.baseNibIdentifier, bundle: nil)
}

Then you can use it like this:

let baseVC: BaseViewController = BaseViewController.loadFromBaseNib()
let subclassVC: ChildViewController = ChildViewController.loadFromBaseNib()
Reply

#17
Here is a Swift solution which does not rely on Objective-C class swapping hacks.
It uses `instantiateViewController(identifier:creator:)` (iOS 13+).
I assume you have the view controller in a storyboard, with identifier `template`. The class assigned to the view controller in the storyboard should be the superclass:

let storyboard = UIStoryboard(name: "main", bundle: nil)

let viewController = storyboard.instantiateViewController(identifier: "template") { coder in
// The coder provides access to the storyboard data.
// We can now init the preferred UIViewController subclass.

if useSubclass {
return SpecialViewController(coder: coder)
} else {
return BaseViewController(coder: coder)
}
}



Here is the [documentation](

[To see links please register here]

)

Reply



Forum Jump:


Users browsing this thread:
1 Guest(s)

©0Day  2016 - 2023 | All Rights Reserved.  Made with    for the community. Connected through