Custom UIView for an UIViewController in Swift

iOSGeek picture iOSGeek · Apr 12, 2016 · Viewed 7.3k times · Source

I use code to create the view (with subviews) for UIViewController's this is how I do it:

  1. override loadView()

    class MYViewController: UIViewController {
    
    var myView: MyView! { return self.view as MyView }
    
       override func loadView() {
          view = MyView() 
       }
    }
    

and here is how I create my custom view:

class MyView: UIView {

    // MARK: Initialization

    override init (frame : CGRect) {
        super.init(frame : frame)
        addSubviews()
        setupLayout()
    }

    convenience init () {
        self.init(frame:CGRect.zero)
    }

    required init(coder aDecoder: NSCoder) {
        fatalError("This class does not support NSCoding")
    }

    // MARK: Build View hierarchy

    func addSubviews(){
        // add subviews
    }

    func setupLayout(){
        // Autolayout
    }

    // lazy load views
}

I do this for all my View Controllers and I am looking for more elegant way, because this process is repetitive, so is there any solution for make that generic for example, create a super abstract class, or create an extension for UIViewController and UIView, Protocols ? I am new for swift and I think that Swift can have a better elegant solution with it's modern patterns

Answer

George Green picture George Green · Apr 12, 2016

If you are wanting to create many different controllers with custom view classes my recommended solution would be along these lines:

First implement a custom view subclass the way you want to be able to use it, here I have used the one you had in your question. You can then subclass this anywhere you need it and just override the relevant methods.

class CustomView: UIView {

    // MARK: Initialization

    override init(frame: CGRect) {
        super.init(frame: frame)
        addSubviews()
        setupLayout()
    }

    required init() {
        super.init(frame: .zero)
        addSubviews()
        setupLayout()
    }

    required init(coder aDecoder: NSCoder) {
        fatalError("This class does not support NSCoding")
    }

    // MARK: Build View hierarchy

    func addSubviews(){
        // add subviews
    }

    func setupLayout(){
        // Autolayout
    }

}

Then create a generic custom view controller that allows specification of a class as a generic parameter so that you can easily create a controller with a custom view class.

class CustomViewController<T: CustomView>: UIViewController {

    var customView: T! { return view as! T }

    override func loadView() {
        view = T()
    }

    init() {
        super.init(nibName: nil, bundle: nil)
    }

}

Then if you wanted to define a new custom view and create a controller that uses it you can simply:

class AnotherCustomView: CustomView { /* Override methods */ }

...

let controller = CustomViewController<AnotherCustomView>()

Boom!

If you wanted you could even typealias this new controller type to make it even more elegant:

class AnotherCustomView: CustomView { /* Override methods */ }

...

typealias AnotherCustomViewController = CustomViewController<AnotherCustomView>
let controller = AnotherCustomViewController()