How to sub class custom view controller in swift? [duplicate] - swift

This question already has an answer here:
Adding subclass to Storyboard crashes
(1 answer)
Closed 5 years ago.
I have custom view controller which contains TableView. I have another view controller which extends custom view controller.
How can I pass the TableView from subclass to super view?
Here is the parent view controller:
class CustomViewController: CustomNavigationController, UISearchResultsUpdating,UITableViewDelegate, UITableViewDataSource {
var query: String = "male"
var tableView: UITableView!
convenience init(tableView: UITableView, query: String){
self.init()
self.tableView = tableView
self.query = query
}
And this is what I did in sub class:
class GirlsViewController: CustomViewController {
#IBOutlet weak var tableViewObj: UITableView!
convenience init() {
self.init(nibName:nil, bundle:nil)
}
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
tableView = tableViewObj
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
The application crash at this line fatalError("init(coder:) has not been implemented")

Just do what the error message says: You need to implement init?(coder.
The basic implementation is to call super and do the same things as in init(nibName
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
tableView = tableViewObj
}

Related

Adding a ViewController into a UICollectionviewcell

I would like to embed a ViewController inside a UICollectionView cell, I tried to search on the internet but I haven't found anything applicable to my case.
I've done this so far,
this is the View Controller:
class ExploreViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemGreen
}
}
This is the collectionViewCell:
class ExploreCollectionViewCell: UICollectionViewCell {
let exploreViewController = ExploreViewController()
override init(frame: CGRect) {
super.init(frame: frame)
addSubview(exploreViewController.view)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Basically I'm instantiating a ViewController inside the cell and displaying its view;
I don't think this is actually the best thing to do but honestly I don't know how to implement it in a different way.
The best thing would be to actually embed the viewController inside the cell, do you know how could I do it?

Subclass of UIView doesn't invoke didSet

I have a subclass of UIView named BaseView. In subclass of BaseView I create didSet with some code. In UIViewController I init this subclass of BaseView and he doesn't invoke his didSet
BaseView code:
class BaseView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
setupViews()
}
func setupViews() { }
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Subclass of BaseView code:
class DetailProductView: BaseView {
var product: Product? {
didSet {
productImage.image = UIImage(named: (product?.productImageName)!)
productTitle.text = product?.title
productCompositionLabel.text = product?.description
productPriceLabel.text = "₽" + product!.productPrice!.stringValue
productWeightLabel.text = product!.productWeight!.stringValue + "г."
}
}
UIViewController code:
class DetailProductController: UIViewController {
var product: Product?
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
let productView = DetailProductView(frame: self.view.bounds)
view.addSubview(productView)
view.layoutSubviews()
}
}
Everything is correct. You created instance of DetailProductView, but you never set any value to it’s product property. Thus didSet was never called (cause you didn’t set anything).
If you want it to be called you should set any value to this property.

How to Pass extra parameters to a custom UIView class for initialization in swift

I'm trying to write a class that is of type UIView, but on initialization I want it to take an extra parameter, but I can't figure out how to get around the UIView needing its params instead. Any help is much appreciated!
class MenuBar: UIView {
let homeController: HomeController
init(controller: HomeController){
homeController = controller
super.init()
}
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
In the ViewController I'm initializing it like this:
let menuBar: MenuBar = {
let mb = MenuBar(controller: self)
return mb
}()
Try this.
class MenuBar: UIView {
let homeController: HomeController
required init(controller: HomeController){
homeController = controller
super.init(frame: CGRect.zero)
// Can't call super.init() here because it's a convenience initializer not a desginated initializer
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
From my experience this is what works best if you want to have custom initialiser for UIView:
class CustomView : UIView {
private var customProperty: CustomClass
required init(customProperty: CustomClass) {
super.init(frame: .zero)
self.customProperty = customProperty
self.setup()
}
required override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.setup()
}
fileprivate func setup() {
//Here all custom code for initialisation (common for all creation methods)
}
}
This approach allows you to keep common initialisation code regardless of method of creating the view (both storyboard and code)
That's about creating UIView properly.
Additionally I would recommend to avoid passing UIViewController to UIView - I think you are trying to solve some problem in a wrong way.
Much better ways to communicate between those two is to use delegate or closure - but that's a bit off-topic - maybe you can create another question about why you want to pass it like this.

Is there a way to make an Xcode template that generates a View-Controller pair?

I often use this pattern when creating UIViewController/UIView pairs. It would be nice if I could define a template in Xcode so that I could click New File -> [template] and generate a MyViewController.swift and MyView.swift like in the example below.
MyViewController.swift
class MyViewController: UIViewController {
override func loadView() {
self.view = MyView()
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
}
extension MyViewController : MyViewDelegate
{
// Provide data, pop off a navigation stack, etc
}
MyView.swift
protocol MyViewDelegate : class {
}
class MyView: UIView
{
weak var delegate : MyViewDelegate?
// MARK: - Initialization
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
func setup()
{
// Configure views
// Assemble
}
}

Initializers may only be declared within a type / coder

I use this tutorial in order to create a simple Shopping List app. I have a problem with those lines of code:
override func viewDidLoad() {
super.viewDidLoad()
required init?(coder aDecoder: NSCoder) { // error appears here
self.init(coder: aDecoder);
loadItems()
}
}
There is an error: Initializers may only be declared within a type.
Why it is not correct? What should I change here?
Initializers must be placed at the type level, not inside any other functions.
class Item: NSObject, NSCoding {
required init?(coder aDecoder: NSCoder) {
self.init(coder: aDecoder)
loadItems()
}
override func viewDidLoad() {
super.viewDidLoad()
// this code runs when the view loads
}
func loadItems() {
// item loading code here
}
}