{"id":13489324,"url":"https://github.com/alexwinston/UIViewprint","last_synced_at":"2025-03-28T04:31:06.380Z","repository":{"id":137517877,"uuid":"51780032","full_name":"alexwinston/UIViewprint","owner":"alexwinston","description":"(Swift) iOS UIView layout reimagined","archived":false,"fork":false,"pushed_at":"2016-03-16T21:47:14.000Z","size":2847,"stargazers_count":8,"open_issues_count":0,"forks_count":1,"subscribers_count":4,"default_branch":"master","last_synced_at":"2024-10-31T02:33:36.918Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/alexwinston.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2016-02-15T19:33:51.000Z","updated_at":"2022-08-26T09:59:38.000Z","dependencies_parsed_at":"2024-01-18T22:39:49.837Z","dependency_job_id":"4a432169-602a-423e-8779-666035d6fa2b","html_url":"https://github.com/alexwinston/UIViewprint","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/alexwinston%2FUIViewprint","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alexwinston%2FUIViewprint/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alexwinston%2FUIViewprint/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alexwinston%2FUIViewprint/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/alexwinston","download_url":"https://codeload.github.com/alexwinston/UIViewprint/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245970450,"owners_count":20702419,"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":[],"created_at":"2024-07-31T19:00:23.041Z","updated_at":"2025-03-28T04:31:05.408Z","avatar_url":"https://github.com/alexwinston.png","language":"Swift","funding_links":[],"categories":["Libs"],"sub_categories":["Layout"],"readme":"# \u0026lt;UIViewprint/\u0026gt;\n[![Language: Swift 2](https://img.shields.io/badge/language-swift2-f48041.svg?style=flat)](https://developer.apple.com/swift)\n![Platform: iOS 8+](https://img.shields.io/badge/platform-iOS%208%2B-blue.svg?style=flat)\n[![License: MIT](http://img.shields.io/badge/license-MIT-lightgrey.svg?style=flat)](https://github.com/s4cha/Stevia/blob/master/LICENSE)\n\n\n\niOS view layout completely reimagined\n\n\u003e **Blueprint** /ˈbluːˌprɪnt/ : a detailed outline or plan of action:\na blueprint for success.\n\n![](images/example1.png)\n\n```\nclass ViewController: UIScrollViewableController {\n    \n    var sellerLabel:UIViewable?\n    var categoryLabel:UIViewable?\n    var compatibilityLabel:UIViewable?\n\n    override func viewDidLoad() {\n        super.viewDidLoad()\n\n        let appDetails = AppDetails()\n        appDetails.name = \"Day One 2 Journal + Notes\"\n        appDetails.developer = \"Bloom Built, LLC\"\n        appDetails.rating = \" 4+\"\n        appDetails.category = \"Lifestyle\"\n        appDetails.price = \"4.99\"\n        appDetails.reviews = 385\n        appDetails.editorsNotes = \"It's hard to make the best even better, but the sequel to Day One lives up to the expectations -- and then some.\"\n\n        super.scrollView.addSubview(\n            div(style(padding:(10,0,0,0)),\n                div(style(display:.Flex(.Row), padding:(0,right:10,0,10)),\n                    image(\"logo\", style:style(width:80, height:80), appearance:logoStyle),\n                    div(style(padding:(0,0,0,10)),\n                        div(.Flex(.Row),\n                            label(appDetails.name),\n                            label(appDetails.rating, style:style(width:20, padding:(0,right:10,0,0)), appearance:ratingLabelStyle)\n                        ),\n                        div(\n                            label(\"\\(appDetails.developer)\", font:.systemFontOfSize(12)),\n                            label(\" \u003e\", font:.systemFontOfSize(11))\n                        ),\n                        div(\n                            label(\"Editors' Choice\", style:style(padding:(1,5,1,5)), appearance:editorsChoiceLabelStyle)\n                        ),\n                        div(style(display:.Flex(.Row)),\n                            div(style(display:.Flex(.Row), align:.Bottom(.Left)),\n                                div(style(display:.Flex(.Row), align:.Middle(.Left), width:55), subviews:\n                                    div(foreach:[Int](1...5)) { (i:Int) in\n                                        return StarView(frame:CGRect(x:0, y:0, width:10, height:10))\n                                    }\n                                ),\n                                label(\"(\\(appDetails.reviews))\", font:.systemFontOfSize(10))\n                            ),\n                            div(style(width:52, height:24), appearance:priceStyle,\n                                label(\"+\", font:.boldSystemFontOfSize(8), style:style(align:.Top(.Left), padding:(0,0,0,3))),\n                                label(\"$\\(appDetails.price)\", font:.boldSystemFontOfSize(12)).align(.Middle(.Left))\n                            )\n                        )\n                    )\n                ),\n                div(style(display:.Flex(.Row), padding:(10,10,0,10)),\n                    image(\"logo\", style:style(width:15,height:15), appearance:watchLogoStyle),\n                    label(\"Offers Apple Watch App for iPhone\", style:style(align:.Middle(.Left), padding:(0,0,0,5)), appearance:watchLabelStyle)\n                ),\n                segment(style(align:.Top(.Center), padding:(10,0,0,0)), items:[\"Details\", \"Reviews\", \"Related\"], color:.grayColor()),\n                hr(padding:(top:10,0,0,0), color:.lightGrayColor()),\n                div(style(padding:(0,0,0,left:10)),\n                    div(style(height:40),\n                        label(\"App Store Editors' Notes\").align(.Middle(.Left))\n                    ),\n                    label(appDetails.editorsNotes, style:style(display:.Block), font:.systemFontOfSize(12)),\n                    hr(padding:(top:10,0,0,0), color:.lightGrayColor()),\n                    div(style(display:.Flex(.Row), padding:(top:10,0,0,0)),\n                        image(\"gamecenter\").width(30).height(30),\n                        div(style(padding:(0,0,0,left:10)),\n                            label(\"Game Center\", font:.systemFontOfSize(12), style:style(display:.Block)),\n                            label(\"Challenge friends and check leaderboards and achievements.\", style:style(display:.Block), appearance:informationLabelStyle)\n                        )\n                    ),\n                    hr(padding:(top:10,0,bottom:10,0), color:.lightGrayColor()),\n                    label(\"Information\", style:style(display:.Block)),\n                    div(style(padding:(top:5,0,0,0)),\n                        informationItem(\u0026sellerLabel, title:\"Seller\", description:appDetails.developer),\n                        informationItem(\u0026categoryLabel, title:\"Category\", description:appDetails.category),\n                        informationItem(\u0026compatibilityLabel, title:\"Compatibility\", description:\"Requires iOS 9.0 or later. Compatible with iPhone, iPad, and iPos touch.\")\n                    )\n                )\n            )\n        )\n    }\n\n    func informationItem(inout view:UIViewable?, title:String, description:String) -\u003e UIViewable {\n        return\n            div(.Flex(.Row),\n                div(\u0026view, style(padding:(0,right:10,0,0)),\n                    label(title, appearance:informationLabelStyle).align(.Top(.Right))\n                ),\n                label(description, font:.systemFontOfSize(12))\n            )\n    }\n\n    func layerStyle(layer:CALayer, borderWidth:CGFloat, borderColor:UIColor, cornerRadius:CGFloat = 0) {\n        layer.borderWidth = borderWidth\n        layer.borderColor = borderColor.CGColor\n        layer.cornerRadius = cornerRadius\n    }\n\n    func logoStyle(view:UIImageView) {\n        layerStyle(view.layer, borderWidth:0.5, borderColor:.grayColor(), cornerRadius:15.0)\n        view.clipsToBounds = true\n    }\n\n    func watchLogoStyle(view:UIView) {\n        layerStyle(view.layer, borderWidth:0.5, borderColor:.grayColor(), cornerRadius:7.5)\n        view.clipsToBounds = true\n    }\n\n    func watchLabelStyle(label:UILabel) {\n        label.textColor = UIColor(red: 106/255.0, green: 113/255.0, blue: 127/255.0, alpha: 1.0)\n        label.font = .boldSystemFontOfSize(10.0)\n    }\n\n    func ratingLabelStyle(label:UILabel) {\n        layerStyle(label.superview!.layer, borderWidth:1, borderColor:.grayColor())\n        label.textColor = .lightGrayColor()\n        label.font = .systemFontOfSize(11)\n        label.textAlignment = .Right\n    }\n\n    func editorsChoiceLabelStyle(label:UILabel) {\n        label.superview!.backgroundColor = .grayColor()\n        label.superview!.layer.cornerRadius = 7\n        label.superview!.clipsToBounds = true\n        \n        label.textColor = .whiteColor()\n        label.font = .systemFontOfSize(10.0)\n    }\n\n    func priceStyle(view:UIView) {\n        layerStyle(view.layer, borderWidth:1, borderColor:view.self.tintColor!, cornerRadius:4.0)\n        view.clipsToBounds = true\n    }\n\n    func informationLabelStyle(label:UILabel) {\n        label.textColor = .lightGrayColor()\n        label.font = .systemFontOfSize(12.0)\n    }\n\n    override func viewWillLayoutSubviews() {\n        super.viewWillLayoutSubviews()\n        \n        let informationItemWidth = CGFloat(self.view.frame.width * 0.3)\n        self.sellerLabel!.width(informationItemWidth)\n        self.categoryLabel!.width(informationItemWidth)\n        self.compatibilityLabel!.width(informationItemWidth)\n    }\n}\n```\n\n## Disclaimer\n\n**UIViewprint** is currently in Beta and pushes various Swift language features to the absolute limit. Swift is an amazing language and I have been pleasently surprised with what is possible and the performance the runtime provides.\n\n**This was previously a thought experiment to demonstrate how layout can be simplified with Swift operator overloading. This layout syntax has now been deprecated in favor of a more editor friendly syntax.**\n\nIn addition this project attemps to borrow ideas from React Native but tailored specifically to iOS.\n\n## Deprecated syntax\nThe operator overloading syntax that this project initially used has now been deprecated.  Although it was fascinating to see how far operator overloading could be pushed it wasn't all that useful in a practical sense.  Especially considering how often it crashed the Xcode editor and the problems with code alignment.\n\n### Example 6 deprecated syntax\n\n```\nvar emailTextField:UITextField?\nvar passwordTextField:UITextField?\n    \nself.view\n    \u003c .flexColumn()+\u003e\n        \u003c .view\u003e\u003e\n        \u003c .flexRow()+\u003e\n            \u003c .view(.style(width:10))\u003e\u003e\n            \u003c .view+\u003e\n                \u003c .input(\u0026emailTextField, \"Email\", style:largeRoundedStyle)\u003e\u003e\n                \u003c .view(.style(height:10))\u003e\u003e\n                \u003c .input(\u0026passwordTextField, \"Password\", style:largeRoundedStyle)\u003e\u003e\n            \u003c .view/\u003e\n            \u003c .view(.style(width:10))\u003e\u003e\n        \u003c .view/\u003e\n        \u003c .view\u003e\u003e\n        \u003c .view+\u003e\n            \u003c .button(\"Login\", display:.Flex(.Row), height:80, touch:login).align(.Bottom(.Left))\u003e\u003e\n        \u003c .view/\u003e\n    \u003c .view/\u003e\n \nfunc login(button:UIButton) {\n    print(emailTextField!.text)\n}\n    \nfunc largeRoundedStyle(view:UIView) {\n    if let textField = view as? UITextField {\n        textField.borderStyle = .RoundedRect\n        textField.frame.size.height = 60\n        textField.font = UIFont(name: \"HelveticaNeue-Light\", size: 26)\n    }\n}\n\n```\n\n## The Basics\nUIViewprint grew out of various shortcomings and frustrations with Interface Builder, Auto Layout, Stack Views and the myriad of Swift frameworks that attempt to make Auto Layout easier. In an effort to reimagine how layout could be improved a structure similar to HTML and CSS was adopted given it's ubiquity and surprising ability to model heirarchical views quite well.\n\nSpecifically UIViewprint implements the layout behavior inherent in HTML and CSS which provides familiarity for web developers while also providing concise programmatic views. Given the popularity of frameworks like React Native this decision caters to the large development community that expects simple and predictable layout.\n\n### Example 1\nUIViewprint can be utilized from a controller or any code that initializes a view. In the examples below a basic UIViewController is assumed.\n\n```swift\noverride func viewDidLoad() {\n    super.viewDidLoad()\n    \n    self.view.addSubview(\n        div()\n    )\n}\n```\n\nIn this particular example a blank \"block\" style view is added as a subview to the controllers view.  Because it does not contain any children or have a specified height nothing is displayed.\n\nThe div function is the primary mechanism to initialize a new view (UIViewable).\n\n\n### Example 2\n\nIn this example a view is added as a subview to the controllers view with a height of 10 and width of the controllers view.  By default UIViewables are displayed as \"block\" elements and as such expand to fill the width of the parent view.\n\n```swift\noverride func viewDidLoad() {\n    super.viewDidLoad()\n    \n    self.view.addSubview(\n        div(style(height:10, backgroundColor:.orangeColor()))\n    )\n    // An alternative fluent style syntax exists that would look like the following\n    // div().height(10).backgroundColor(.orangeColor())\n}\n```\n![UIViewprint Example 2](images/example2.png)\n\n### Example 3\n\nIn this example UILabels are introduced along with display types which is inherent to each UIViewable.\n\n```swift\noverride func viewDidLoad() {\n    super.viewDidLoad()\n    \n    self.view.addSubview(\n\t    div(\n\t        label(\"Label 1\"),\n\t        label(\"Label 2\"),\n\t        label(\"Label 3\", style:style(display:.Block))\n\t    )\n\t)\n}\n```\n\n![UIViewprint Example 3](images/example3.png)\n\nOf interest is the fact that by default UILabels are analogous to \"inline\" elements.  Label 1 and label 2 align inline.  Label 3 is pushed below the previous labels because it's display property has been set to \"block\".\n\nThis is similar to the following HTML\n\n```\n\t\u003cdiv\u003e\n\t\tLabel 1\n\t\tLabel 2\n\t\t\u003cdiv\u003eLabel3\u003c/div\u003e\n\t\u003c/div\u003e\n```\n\n## Flex rows\n\nFlex rows are similar to an HTML flex container with flex row items. Unlike  CSS flexbox layout, views within a flex row are automatically divided equally among the parent views width unless a view specifically defines a width.\n\n### Example 4\n\n```\noverride func viewDidLoad() {\n    super.viewDidLoad()\n    \n    self.view.addSubview(\n        div(style(display:.Flex(.Row)),\n            label(\"Label 1\"),\n            label(\"Label 2\"),\n            label(\"Label 3\")\n        )\n    )\n}\n```\n\n![UIViewprint Example 4](images/example4.png)\n\nAs expected three evenly spaced views have been created that each contain the specified label.  The views within a flex row are by default aligned top left. It should be noted that UIViewprint will intelligently resize views on orientation change.\n\nIf however the first label specifies a width of 100 then the remaining views divide the space evenly after accounting for the fixed width view.\n\n```\noverride func viewDidLoad() {\n    super.viewDidLoad()\n    \n    self.view.addSubview(\n        div(style(display:.Flex(.Row)),\n            label(\"Label 1\").width(100),\n            label(\"Label 2\"),\n            label(\"Label 3\")\n        )\n    )\n}\n```\n\n## Flex columns\n\nFlex columns are similar to flex rows but stretch along the Y axis. Views within a flex column are automatically divided equally among the parent views height unless a view specifically defines a height.\n\n### Example 5\n\n```\noverride func viewDidLoad() {\n    super.viewDidLoad()\n    \n    self.view.addSubview(\n        div(style(display:.Flex(.Column)),\n            div(\n                label(\"Label 1\")\n            ),\n            div(\n                label(\"Label 2\")\n            ),\n            div(\n                label(\"Label 3\")\n            )\n        )\n    )\n}\n```\n\n![UIViewprint Example 5](images/example5.png)\n\n## UIKit Controls\nUIViewprint provides wrappers for a large number of default iOS UIKit controls. Detailed examples are provided in the project source repository.\n\n- UILabel\n- UIButton\n- UITextField\n- UIImageView\n- UISegmentedControl\n- UITableView \n\nPlease file bug reports or pull requests for controls that are not currently supported or do not provide adequate configuration.\n\n## Padding\nUIViewprint provides CSS padding like functionality that can be defined for each UIViewable.  The example below adds padding around two subviews with the inner most defining a label.\n\n```\ndiv(style(padding:(top:10, right:10, bottom:10, left:10), backgroundColor:.orangeColor()),\n\tdiv(style(padding:(top:10, right:10, bottom:10, left:10), backgroundColor:.yellowColor()),\n\t\tlabel(\"Padded\").backgroundColor(.lightGrayColor())\n\t)\n)\n``` \n\n![UIViewprint Example 8](images/example8.png)\n\n## UITablewView support\nUIViewprint now provides rather robust table view support however some complex edge cases can still be difficult. The easiest way to develop table views in UIViewprint is to use the UITableViewableController class.  This subclass of the UITableViewController provides a number of convenience methods in addition to various important boilerplate code that provides impressive performance out of the box.\n\n### Static table\nThe example code below demonstrates how to create a simple static table that simply renders a number of predefined rows. In addition to easily defining a table header and footer, multiple sections can be defined in addition to variable sized cell \"rows\" that are intelligently cached and resized appropriately during orientation changes. Row selectors can be defined per section.\n\n```\nclass ViewController: UITableViewableController {\n\toverride func viewDidLoad() {\n\t    super.viewDidLoad()\n\t    \n\t    self.title = \"Static table\"\n\t    self.edgesForExtendedLayout = UIRectEdge.None;\n\t    \n\t    self.table(\n\t        header: label(\"Header\"),\n\t        sections: [\n\t            section(touchRow,\n\t                header: \"Section One\",\n\t                rows: [\n\t                    row(\n\t                        div(\n\t                            label(\"Left 1\"),\n\t                            label(\"Right 1\").align(.Top(.Right))\n\t                        )\n\t                    ),\n\t                    row(label(\"Row 2\")),\n\t                    row(label(\"Row 3\")),\n\t                    row(label(\"Row 4\")),\n\t                    row(label(\"Row 5\")),\n\t                    row(\n\t                        div(\n\t                            label(\"Left 6\"),\n\t                            label(\"Right 6\").align(.Top(.Right))\n\t                        )\n\t                    )\n\t                ]\n\t            ),\n\t            section(touchRow,\n\t                header: \"Section Two\",\n\t                rows: [\n\t                    row(\n\t                        div(\n\t                            label(\"Left 2-1\"),\n\t                            label(\"Right 2-1\").align(.Top(.Right))\n\t                        )\n\t                    ),\n\t                    row(label(\"Row 2-2\")),\n\t                    row(label(\"Row 2-3\")),\n\t                    row(label(\"Row 2-4\")),\n\t                    row(label(\"Row 2-5\")),\n\t                    row(\n\t                        div(\n\t                            label(\"Left 2-6\"),\n\t                            label(\"Right 2-6\").align(.Top(.Right))\n\t                        )\n\t                    )\n\t                ]\n\t            )\n\t        ],\n\t        footer: div(style(height:100, backgroundColor:.yellowColor()),\n\t            label(\"Foot 1\").backgroundColor(.grayColor()),\n\t            label(\"Foot 2\").align(.Top(.Right)).backgroundColor(.orangeColor())\n\t        )\n\t    )\n\t}\n\t    \n\tfunc touchRow(indexPath:NSIndexPath) {\n\t    print(\"touch \\(indexPath.section) \\(indexPath.row)\")\n\t}\n}\n```\n\n### Basic datasource table\nThe UITableViewableController provides a number of convenience methods to support various types of datasources.  In the example below an array of Example objects is registered with the table controller through the \"foreach\" parameter of the section method.\n\nA crude copy of the UITableViewCellStyleDefault is provided through the defaultTableViewCell method.  It should be noted however that the UITableViewableController does not work with cells.  It instead works with UIViewable which is a subclass of UIView.\n\n```\nclass UIViewController: UITableViewableController {\n\n    let examples: [ExampleControllerDetails] = [\n        Example(name:\"Example 1\", controller:ViewController1.self),\n        Example(name:\"Example 2\", controller:ViewController2.self),\n        Example(name:\"Example 3\", controller:ViewController3.self)\n    ]\n    \n    override func viewDidLoad() {\n        super.viewDidLoad()\n        \n        self.title = \"UITableView examples\"\n        self.edgesForExtendedLayout = UIRectEdge.None;\n        \n        self.table(\n            sections: [\n                section(selectExample, foreach:examples) { (example:AnyObject) in\n                    let example = example as! Example\n                    return self.defaultTableViewCell(example.name)\n                }\n            ]\n        )   \n    }\n    \n    func selectExample(indexPath:NSIndexPath) {\n        let controller:UIViewController = self.examples[indexPath.row].controller.init()\n        controller.view.frame = view.bounds\n        controller.view.backgroundColor = .whiteColor()\n        controller.view.autoresizingMask = [UIViewAutoresizing.FlexibleWidth, UIViewAutoresizing.FlexibleHeight]\n        \n        self.navigationController?.pushViewController(controller, animated: true)\n    }\n}\n```\n\n### Advanced datasource table\nComplex datasource driven tables are also possible with UIViewprint.  Although this functionality does not support every possible use case it currently provides robust support for a large majority of scenarios.\n\nThe example below is the beginnnings of an App Store Editors' Choice table view. The header is \"sticky\" and the table supports basic swipe to delete capabilities. \n\n```\nclass ViewController: UITableViewableController {\n\n    override func viewDidLoad() {\n        super.viewDidLoad()\n\n        self.title = \"Datasource example\"\n        self.edgesForExtendedLayout = UIRectEdge.None;\n\n        let apps:[App] = [\n            App(name:\"iSlash Heroes\"),\n            App(name:\"Tomb of the Mask\"),\n            App(name:\"Day One 2 Journal + Notes + diary\"),\n            App(name:\"twofold inc.\"),\n            App(name:\"Guides by Lonely Planet\"),\n            App(name:\"Dungelot: Shattered Lands\")\n        ]\n\n        self.table(\n            header: div(style(height:100, backgroundColor:.lightGrayColor()),\n                label(\"Editors' Choice\", style:style(align:.Middle(.Center)))\n            ),\n            sections: [\n                section(touchRow,\n                    rows: [\n                        row(\n                            label(\"Our editors continually sift through the latest releases and updates, recognizing the most invaluable apps and the most entertaining games as Editors' Choice selections.\").display(.Block)\n                        )\n                    ]\n                ),\n                section(touchRow, foreach:apps) { (app:AnyObject) in\n                    let app = app as! App\n                    return div(\n                        div(style(height:10)),\n                        div(.Flex(.Row),\n                            div(style(width:10)),\n                            div(\n                                label(\"\\(app.name)\")\n                            )\n                        ),\n                        div(style(height:10))\n                    )\n                }\n            ],\n            footer: div(\n                button(\"Redeem\", touch:handleRedeem),\n                button(\"Send Gift\", touch:handleSendGift)\n            )\n        )\n\n    }\n\n    func touchRow(indexPath:NSIndexPath) {\n        print(\"touch \\(indexPath.section) \\(indexPath.row)\")\n    }\n    \n    func handleRedeem(button:UIButton) {\n    }\n    \n    func handleSendGift(button:UIButton) {\n    }\n}\n```\n\n## Advanced features\n\n### Example 6\n\nThis example demonstrates how layout compares to the popular [Stevia](https://github.com/s4cha/Stevia) framework.\n\n```swift\nvar emailTextField:UITextField?\nvar passwordTextField:UITextField?\n\noverride func viewDidLoad() {\n    super.viewDidLoad()\n\n    self.title = \"Example 6\"\n    self.edgesForExtendedLayout = UIRectEdge.None;\n    \n    self.view.addSubview(\n        div(.Flex(.Column),\n            div(),\n            div(style(padding:(0,right:10,0,left:10)),\n                input(\u0026emailTextField, placeholder:\"Email\", style:style(padding:(0,0,bottom:5,0)), appearance:largeFontRoundedCorners),\n                input(placeholder:\"Password\", appearance:largeFontRoundedCorners)\n            ),\n            div(),\n            div(\n                button(\"Login\", display:.Flex(.Row), align:.Bottom(.Left), height:80, touch:login)\n            )\n        )\n    )\n}\n    \nfunc login(button:UIButton) {\n    print(emailTextField!.text)\n}\n    \nfunc largeFontRoundedCorners(view:UIView) {\n    if let textField = view as? UITextField {\n        textField.borderStyle = .RoundedRect\n        textField.frame.size.height = 54\n        textField.font = UIFont(name: \"HelveticaNeue-Light\", size: 26)\n        textField.returnKeyType = .Done\n    }\n}\n```\n\nOf particular interest is the ability to associate wrapped components to variables via inout parameters. It is also easy to bind touch events to target functions within the markup directly via the \"touch:\" parameter. Additionally function callbacks allow styles to be reused across components and views.\n\n![UIViewprint Example 6](images/example6.png)\n\n### Example 6a\n\nThis example demonstrates a UIViewprint implementation of the Facebook profile page developed to highlight the features of the [Neon](https://github.com/mamaral/Neon) framework. Unlike Neon however all the code required to layout the view is listed below.\n \n```\nclass ViewController: UIViewableController {\n    var bannerView:UIViewable = UIViewable()\n    var avatarImageView:UIViewable?\n    \n    override func viewDidLoad() {\n        super.viewDidLoad()\n        \n        bannerView.layer.contents = UIImage(named:\"banner\")?.CGImage\n        \n        self.view\n            \u003c .view(.style(backgroundColor:.lightGrayColor()))+\u003e\n                \u003c bannerView+\u003e\n                    \u003c .view(.style(.Flex(.Row), align:.Bottom(.Left)))+\u003e\n                        \u003c .view(.style(.Flex(.Row)))+\u003e\n                            \u003c width(10)\u003e\u003e\n                            \u003c image(\u0026avatarImageView, name:\"avatar\")\u003e\u003e\n                            \u003c .view(.style(align:.Middle(.Left)))+\u003e\n                                \u003c label(\"Alex\", display:.Block, style:avatarNameStyle)\u003e\u003e\n                                \u003c label(\"Winston\", display:.Block, style:avatarNameStyle)\u003e\u003e\n                            \u003c .view/\u003e\n                        \u003c .view/\u003e\n                        \u003c image(\"camera\").align(.Bottom(.Left)).width(30).height(30)\u003e\u003e\n                        \u003c width(10)\u003e\u003e\n                    \u003c .view/\u003e\n                \u003c .view/\u003e\n                \u003c UIViewable().display(.Flex(.Row))+\u003e\n                    \u003c buttonBarItemView(\"Post\", labelAlign:.Top(.Center), imageName:\"post\", imageDisplay:.Block, imageAlign:.Top(.Center))\u003e\u003e\n                    \u003c buttonBarItemView(\"Update Info\", labelAlign:.Top(.Center), imageName:\"updateInfo\", imageDisplay:.Block, imageAlign:.Top(.Center))\u003e\u003e\n                    \u003c buttonBarItemView(\"Activity Log\", labelAlign:.Top(.Center), imageName:\"activityLog\", imageDisplay:.Block, imageAlign:.Top(.Center))\u003e\u003e\n                    \u003c buttonBarItemView(\"More\", labelAlign:.Top(.Center), imageName:\"more\", imageDisplay:.Block, imageAlign:.Top(.Center))\u003e\u003e\n                \u003c .view/\u003e\n                \u003c height(10)\u003e\u003e\n                \u003c UIViewable().display(.Flex(.Row))+\u003e\n                    \u003c width(10)\u003e\u003e\n                    \u003c imageLabelView(\"About\", imageName:\"about\")\u003e\u003e\n                    \u003c width(10)\u003e\u003e\n                    \u003c imageLabelView(\"Photos\", imageName:\"photos\")\u003e\u003e\n                    \u003c width(10)\u003e\u003e\n                    \u003c imageLabelView(\"Friends\", imageName:\"friends\")\u003e\u003e\n                    \u003c width(10)\u003e\u003e\n                \u003c .view/\u003e\n                \u003c height(10)\u003e\u003e\n                \u003c UIViewable().display(.Flex(.Row))+\u003e\n                    \u003c width(10)\u003e\u003e\n                    \u003c buttonBarItemView(\"Post\", imageName:\"post\")\u003e\u003e\n                    \u003c buttonBarItemView(\"Photo\", imageName:\"activityLog\")\u003e\u003e\n                    \u003c buttonBarItemView(\"Life Event\", imageName:\"more\")\u003e\u003e\n                    \u003c width(10)\u003e\u003e\n                \u003c .view/\u003e\n                \u003c height(10)\u003e\u003e\n            \u003c .view/\u003e\n    }\n    \n    func buttonBarItemView(labelText:String, labelAlign:UIViewableAlign? = .Middle(.Left), imageName:String, imageDisplay:UIViewableDisplay? = .Inline, imageAlign:UIViewableAlign? = .Top(.Left)) -\u003e UIView {\n        func buttonLabelStyle(label:UILabel) {\n            label.textColor = UIColor(red: 106/255.0, green: 113/255.0, blue: 127/255.0, alpha: 1.0)\n            label.font = .systemFontOfSize(13.0)\n        }\n        \n        return UIViewable().backgroundColor(.whiteColor())\n            \u003c image(imageName).display(imageDisplay!).align(imageAlign!).width(28).height(28)\u003e\u003e\n            \u003c label(labelText, style:buttonLabelStyle).align(labelAlign!)\u003e\u003e\n    }\n    \n    func imageLabelView(labelText:String, imageName:String) -\u003e UIView {\n        func imageLabelStyle(label:UILabel) {\n            label.textColor = UIColor.blackColor()\n            label.font = UIFont.boldSystemFontOfSize(14.0)\n        }\n        \n        return UIViewable().backgroundColor(.whiteColor())\n            \u003c image(imageName, contentMode:.ScaleAspectFill).display(.Block).align(.Top(.Center)).height(60)\u003e\u003e\n            \u003c label(labelText, style:imageLabelStyle).align(.Top(.Center))\u003e\u003e\n    }\n\n    func avatarNameStyle(label:UILabel) {\n        label.font = UIFont(name: \"HelveticaNeue-Light\", size: 33)\n        label.textColor = .whiteColor()\n    }\n\n    override func viewWillLayoutSubviews() {\n        super.viewWillLayoutSubviews()\n        \n        let isLandscape:Bool = UIDevice.currentDevice().orientation.isLandscape.boolValue\n        let bannerHeight:CGFloat = self.view.frame.height * 0.43\n        let avatarHeightMultipler:CGFloat = isLandscape ? 0.75 : 0.43\n        let avatarSize = bannerHeight * avatarHeightMultipler\n        \n        self.bannerView.height(bannerHeight)\n        self.avatarImageView!.width(avatarSize).height(avatarSize)\n    }\n}\n```\n\n![UIViewprint Example 6a](images/example6a.png)\n\n## View alignment\n\nUIViewprint makes view alignment within a parent relatively painless. Views can be aligned vertically (Top, Middle, Bottom) and horizontally (Left, Center, Right).\n\n### Example 7\n\n```\nself.view\n    \u003c .flexColumn()+\u003e\n        \u003c .flexRow()+\u003e\n            \u003c .flexColumn()+\u003e\n                \u003c label(\"Top Left\").align(.Top(.Left)).backgroundColor(.yellowColor())\u003e\u003e\n            \u003c .view/\u003e\n            \u003c .flexColumn()+\u003e\n                \u003c label(\"Top Center\").align(.Top(.Center)).backgroundColor(.orangeColor())\u003e\u003e\n            \u003c .view/\u003e\n            \u003c .flexColumn()+\u003e\n                \u003c label(\"Top Right\").align(.Top(.Right)).backgroundColor(.redColor())\u003e\u003e\n            \u003c .view/\u003e\n        \u003c .view/\u003e\n        \u003c .view(style(.Flex(.Row), backgroundColor:.lightGrayColor()))+\u003e\n            \u003c .flexColumn()+\u003e\n                \u003c label(\"Mid Left\").align(.Middle(.Left)).backgroundColor(.yellowColor())\u003e\u003e\n            \u003c .view/\u003e\n            \u003c .flexColumn()+\u003e\n                \u003c label(\"Mid Center\").align(.Middle(.Center)).backgroundColor(.orangeColor())\u003e\u003e\n            \u003c .view/\u003e\n            \u003c .flexColumn()+\u003e\n                \u003c label(\"Mid Right\").align(.Middle(.Right)).backgroundColor(.redColor())\u003e\u003e\n            \u003c .view/\u003e\n        \u003c .view/\u003e\n        \u003c .flexRow()+\u003e\n            \u003c .flexColumn()+\u003e\n                \u003c label(\"Bottom Left Test\").align(.Bottom(.Left)).backgroundColor(.yellowColor())\u003e\u003e\n            \u003c .view/\u003e\n            \u003c .flexColumn()+\u003e\n                \u003c label(\"Bottom Ctr\").align(.Bottom(.Center)).backgroundColor(.orangeColor())\u003e\u003e\n            \u003c .view/\u003e\n            \u003c .flexColumn()+\u003e\n                \u003c label(\"Bottom Right\").align(.Bottom(.Right)).backgroundColor(.redColor())\u003e\u003e\n            \u003c .view/\u003e\n        \u003c .view/\u003e\n    \u003c .view/\u003e\n```\n\n![UIViewprint Example 7](images/example7.png)\n\n## TODO\n* Consider using SwiftBox for better layout and performance\n\n## Contact\nTwitter: [@alex_winston](https://twitter.com/alex_winston)","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falexwinston%2FUIViewprint","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Falexwinston%2FUIViewprint","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falexwinston%2FUIViewprint/lists"}