{"id":16684544,"url":"https://github.com/saoudrizwan/controllerviewdesign","last_synced_at":"2025-04-09T23:42:01.962Z","repository":{"id":82148731,"uuid":"93065308","full_name":"saoudrizwan/ControllerViewDesign","owner":"saoudrizwan","description":"This new design pattern focuses on separating view-related code from controller classes.","archived":false,"fork":false,"pushed_at":"2017-10-25T02:56:57.000Z","size":14,"stargazers_count":20,"open_issues_count":0,"forks_count":4,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-03-24T01:35:24.511Z","etag":null,"topics":["ios","swift"],"latest_commit_sha":null,"homepage":null,"language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/saoudrizwan.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-06-01T14:20:50.000Z","updated_at":"2023-08-20T04:30:21.000Z","dependencies_parsed_at":"2023-03-01T02:30:49.162Z","dependency_job_id":null,"html_url":"https://github.com/saoudrizwan/ControllerViewDesign","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/saoudrizwan%2FControllerViewDesign","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/saoudrizwan%2FControllerViewDesign/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/saoudrizwan%2FControllerViewDesign/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/saoudrizwan%2FControllerViewDesign/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/saoudrizwan","download_url":"https://codeload.github.com/saoudrizwan/ControllerViewDesign/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248131470,"owners_count":21052819,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ios","swift"],"created_at":"2024-10-12T14:44:13.790Z","updated_at":"2025-04-09T23:42:01.954Z","avatar_url":"https://github.com/saoudrizwan.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n    \u003cimg src=\"https://cloud.githubusercontent.com/assets/7799382/26728465/2e259924-475f-11e7-813a-3e691c82567a.png\" alt=\"Controller View Design\" /\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n    \u003ca href=\"#installation\"\u003eInstallation\u003c/a\u003e\n  • \u003ca href=\"#getting-started\"\u003eGetting Started\u003c/a\u003e\n  • \u003ca href=\"#should-i-use-cvd\"\u003eShould I use CVD?\u003c/a\u003e\n\u003c/p\u003e\n\nThe Control View Design pattern, or CVD, is a new \u0026 elegant approach to delegating subviews' initialization, layout, and animation code to a separate class from UIViewControllers. Managing user interfaces programatically can result in massive view controller classes; however with CVD, view-related code is contained in a **ControllerView** subclass while data model management, user interaction, etc. is handled by a **Controller** subclass (previously known as a ViewController.) In many ways, CVD ensures you follow proper MVC guidelines [recommended by Apple](https://developer.apple.com/library/content/documentation/General/Conceptual/DevPedia-CocoaCore/MVC.html). \n\n[Read the Medium article.](https://medium.com/@sdrzn/controller-view-design-pattern-for-swift-new-6283cb052)\n\n## Compatibility\n\nThe Controller View Design pattern is to be used primarily with Swift. \n\n## Installation\nCVD is a design pattern and not a framework, but there is some boilerplate code (~30 lines) required to get started. The required boilerplate code is included in `ControllerViewDesign.swift`, which you can add anywhere in your project. You can also drag and drop `ControllerViewDesign.swift` into your project.\n\n## Getting Started\nBefore, we would create a *UIViewController* subclass, where we initialize subviews, lay them out in our *viewDidLoad()* method, and then add any animation functions if we needed them. \nHowever with CVD, we put all that view-related code in a *ControllerView* subclass and set view handlers (like targets, gesture recognizers, delegates, data sources, etc.) \u0026 call any animation functions in a *Controller* subclass.\n\n1. [Create a ControllerView subclass.](#creating-a-controllerview-subclass)\n\n2. [Create a Controller subclass.](#creating-a-controller-subclass)\n\n### Creating a ControllerView subclass\n*ControllerView* is simply a subclass of a *UIView*, and will act as a 'container view' for all our subviews.\n```swift\nclass HomeControllerView: ControllerView {\n    \n    // MARK: Views\n    // This is where you want to declare all your subviews' instances. Creating custom views as computed objects is much faster and easier than creating custom subclasses.\n    \n    let label: UILabel = {\n        let label = UILabel()\n        label.text = \"Hello world\"\n        label.isUserInteractionEnabled = true\n        return label\n    }()\n    \n    // MARK: Layout\n    \n    override func addSubviews() {\n        // Here we will add all our subviews on to self as if we would to self.view in a UIViewController.\n        addSubview(label)\n        // Set constraints or frames for all our subviews here as well.\n        label.frame = CGRect(x: 100, y: 100, width: 300, height: 30)\n    }\n    \n    // MARK: Methods\n    // We also want to add any interface-changing code here, such as animations.\n    \n    func animateLabel() {\n        UIView.animate(withDuration: 0.4, animations: { \n            label.frame = CGRect(x: 0, y: 0, width: 300, height: 30)\n        }, completion: nil)\n    }\n}\n```\n\n### Creating a Controller subclass\n*Controller* is simply a subclass of a *UIViewController* that's linked to a *ControllerView* instance.\n```swift\nclass HomeController: Controller {\n\n    override func viewDidLoad() {\n        super.viewDidLoad()\n        // We have to set our controllerView property in viewDidLoad, as this is the best place to initialize and layout subviews.\n        controllerView = HomeControllerView(controller: self)\n    }\n    \n    // Use this function to set up any targets, gesture recognizers, delegates, data sources, etc. for our subviews. Our ControllerView subclass automatically calls this function for us in the background at the proper time.\n    override func setViewHandlers() {\n        // To access our views, we first have to downcast our controllerView class property to our custom ControllerView subclass.\n        guard let controllerView = self.controllerView as? HomeControllerView else { fatalError(\"Controller view has not been set\") }\n        controllerView.label.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(labelTapped)))\n    }\n    \n    func labelTapped() {\n        guard let controllerView = self.controllerView as? HomeControllerView else { fatalError(\"Controller view has not been set\") }\n        controllerView.animateLabel()\n    }\n}\n```\nNow whenever we need to access a subview, we first need to get our *ControllerView* subclass. \n```swift\nextension HomeController: UITableViewDataSource {\n    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -\u003e UITableViewCell {\n        guard let controllerView = self.controllerView as? HomeControllerView else { fatalError(\"Controller view has not been set\") }\n        let cell = controllerView.tableView.dequeueReusableCell(withIdentifier: \"id\", for: indexPath)\n        cell.textLabel?.text = data[indexPath.row]\n        return cell\n    }\n}\n```\n**NOTE:** Don't let the *fatalError()* bit scare you. That's simply there to throw an error if you forget to set the *controllerView* property in the *viewDidLoad()* function.\n\n**Alternative ways of accessing a ControllerView's views and methods using CVD:**\n\nUsing a computer variable with a custom getter:\n\n```swift\nclass HomeController: Controller {\n    \n    var homeControllerView: HomeControllerView {\n        get {\n            guard let controllerView = self.controllerView as? HomeControllerView else { fatalError(\"Controller view has not been set\") }\n            return controllerView\n        }\n    }\n    \n    override func viewDidLoad() {\n        super.viewDidLoad()\n        controllerView = HomeControllerView(controller: self)\n    }\n    \n    override func setViewHandlers() {\n        homeControllerView.label.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(labelTapped)))\n    }\n    \n    func labelTapped() {\n        homeControllerView.animateLabel()\n    }\n}\n```\n... or if you like optional chaining:\n```swift\n(controllerView as? HomeControllerView)?.label.text = \"Bye world\"\n(controllerView as? HomeControllerView)?.animateLabel()\n```\n\n## Should I use CVD?\n\nIf you create, layout, animate, and manage your app's subviews programatically, then CVD is a clean \u0026 easy approach to ensuring you don't end up with massive view controllers. CVD is a design pattern you can use alongside other design patterns like MVC or MVVM, so it isn't a complete replacement for them. In fact, it even helps ensure you follow proper MVC guidelines if that's what you're using in your project.  However, if you're using only storyboards for your particular project, then CVD may not be the best solution for managing all your IBOutlets.\n\n## Credits\n\nIcons in header image by [Yummygum](https://yummygum.com/)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsaoudrizwan%2Fcontrollerviewdesign","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsaoudrizwan%2Fcontrollerviewdesign","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsaoudrizwan%2Fcontrollerviewdesign/lists"}