(Swift) iOS UIView layout reimagined

Related tags

Layout UIViewprint
Overview

<UIViewprint/>

Language: Swift 2 Platform: iOS 8+ License: MIT

iOS view layout completely reimagined

Blueprint /ˈbluːˌprɪnt/ : a detailed outline or plan of action: a blueprint for success.

class ViewController: UIScrollViewableController {
    
    var sellerLabel:UIViewable?
    var categoryLabel:UIViewable?
    var compatibilityLabel:UIViewable?

    override func viewDidLoad() {
        super.viewDidLoad()

        let appDetails = AppDetails()
        appDetails.name = "Day One 2 Journal + Notes"
        appDetails.developer = "Bloom Built, LLC"
        appDetails.rating = " 4+"
        appDetails.category = "Lifestyle"
        appDetails.price = "4.99"
        appDetails.reviews = 385
        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."

        super.scrollView.addSubview(
            div(style(padding:(10,0,0,0)),
                div(style(display:.Flex(.Row), padding:(0,right:10,0,10)),
                    image("logo", style:style(width:80, height:80), appearance:logoStyle),
                    div(style(padding:(0,0,0,10)),
                        div(.Flex(.Row),
                            label(appDetails.name),
                            label(appDetails.rating, style:style(width:20, padding:(0,right:10,0,0)), appearance:ratingLabelStyle)
                        ),
                        div(
                            label("\(appDetails.developer)", font:.systemFontOfSize(12)),
                            label(" >", font:.systemFontOfSize(11))
                        ),
                        div(
                            label("Editors' Choice", style:style(padding:(1,5,1,5)), appearance:editorsChoiceLabelStyle)
                        ),
                        div(style(display:.Flex(.Row)),
                            div(style(display:.Flex(.Row), align:.Bottom(.Left)),
                                div(style(display:.Flex(.Row), align:.Middle(.Left), width:55), subviews:
                                    div(foreach:[Int](1...5)) { (i:Int) in
                                        return StarView(frame:CGRect(x:0, y:0, width:10, height:10))
                                    }
                                ),
                                label("(\(appDetails.reviews))", font:.systemFontOfSize(10))
                            ),
                            div(style(width:52, height:24), appearance:priceStyle,
                                label("+", font:.boldSystemFontOfSize(8), style:style(align:.Top(.Left), padding:(0,0,0,3))),
                                label("$\(appDetails.price)", font:.boldSystemFontOfSize(12)).align(.Middle(.Left))
                            )
                        )
                    )
                ),
                div(style(display:.Flex(.Row), padding:(10,10,0,10)),
                    image("logo", style:style(width:15,height:15), appearance:watchLogoStyle),
                    label("Offers Apple Watch App for iPhone", style:style(align:.Middle(.Left), padding:(0,0,0,5)), appearance:watchLabelStyle)
                ),
                segment(style(align:.Top(.Center), padding:(10,0,0,0)), items:["Details", "Reviews", "Related"], color:.grayColor()),
                hr(padding:(top:10,0,0,0), color:.lightGrayColor()),
                div(style(padding:(0,0,0,left:10)),
                    div(style(height:40),
                        label("App Store Editors' Notes").align(.Middle(.Left))
                    ),
                    label(appDetails.editorsNotes, style:style(display:.Block), font:.systemFontOfSize(12)),
                    hr(padding:(top:10,0,0,0), color:.lightGrayColor()),
                    div(style(display:.Flex(.Row), padding:(top:10,0,0,0)),
                        image("gamecenter").width(30).height(30),
                        div(style(padding:(0,0,0,left:10)),
                            label("Game Center", font:.systemFontOfSize(12), style:style(display:.Block)),
                            label("Challenge friends and check leaderboards and achievements.", style:style(display:.Block), appearance:informationLabelStyle)
                        )
                    ),
                    hr(padding:(top:10,0,bottom:10,0), color:.lightGrayColor()),
                    label("Information", style:style(display:.Block)),
                    div(style(padding:(top:5,0,0,0)),
                        informationItem(&sellerLabel, title:"Seller", description:appDetails.developer),
                        informationItem(&categoryLabel, title:"Category", description:appDetails.category),
                        informationItem(&compatibilityLabel, title:"Compatibility", description:"Requires iOS 9.0 or later. Compatible with iPhone, iPad, and iPos touch.")
                    )
                )
            )
        )
    }

    func informationItem(inout view:UIViewable?, title:String, description:String) -> UIViewable {
        return
            div(.Flex(.Row),
                div(&view, style(padding:(0,right:10,0,0)),
                    label(title, appearance:informationLabelStyle).align(.Top(.Right))
                ),
                label(description, font:.systemFontOfSize(12))
            )
    }

    func layerStyle(layer:CALayer, borderWidth:CGFloat, borderColor:UIColor, cornerRadius:CGFloat = 0) {
        layer.borderWidth = borderWidth
        layer.borderColor = borderColor.CGColor
        layer.cornerRadius = cornerRadius
    }

    func logoStyle(view:UIImageView) {
        layerStyle(view.layer, borderWidth:0.5, borderColor:.grayColor(), cornerRadius:15.0)
        view.clipsToBounds = true
    }

    func watchLogoStyle(view:UIView) {
        layerStyle(view.layer, borderWidth:0.5, borderColor:.grayColor(), cornerRadius:7.5)
        view.clipsToBounds = true
    }

    func watchLabelStyle(label:UILabel) {
        label.textColor = UIColor(red: 106/255.0, green: 113/255.0, blue: 127/255.0, alpha: 1.0)
        label.font = .boldSystemFontOfSize(10.0)
    }

    func ratingLabelStyle(label:UILabel) {
        layerStyle(label.superview!.layer, borderWidth:1, borderColor:.grayColor())
        label.textColor = .lightGrayColor()
        label.font = .systemFontOfSize(11)
        label.textAlignment = .Right
    }

    func editorsChoiceLabelStyle(label:UILabel) {
        label.superview!.backgroundColor = .grayColor()
        label.superview!.layer.cornerRadius = 7
        label.superview!.clipsToBounds = true
        
        label.textColor = .whiteColor()
        label.font = .systemFontOfSize(10.0)
    }

    func priceStyle(view:UIView) {
        layerStyle(view.layer, borderWidth:1, borderColor:view.self.tintColor!, cornerRadius:4.0)
        view.clipsToBounds = true
    }

    func informationLabelStyle(label:UILabel) {
        label.textColor = .lightGrayColor()
        label.font = .systemFontOfSize(12.0)
    }

    override func viewWillLayoutSubviews() {
        super.viewWillLayoutSubviews()
        
        let informationItemWidth = CGFloat(self.view.frame.width * 0.3)
        self.sellerLabel!.width(informationItemWidth)
        self.categoryLabel!.width(informationItemWidth)
        self.compatibilityLabel!.width(informationItemWidth)
    }
}

Disclaimer

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.

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.

In addition this project attemps to borrow ideas from React Native but tailored specifically to iOS.

Deprecated syntax

The 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.

Example 6 deprecated syntax

var emailTextField:UITextField?
var passwordTextField:UITextField?
    
self.view
    < .flexColumn()+>
        < .view>>
        < .flexRow()+>
            < .view(.style(width:10))>>
            < .view+>
                < .input(&emailTextField, "Email", style:largeRoundedStyle)>>
                < .view(.style(height:10))>>
                < .input(&passwordTextField, "Password", style:largeRoundedStyle)>>
            < .view/>
            < .view(.style(width:10))>>
        < .view/>
        < .view>>
        < .view+>
            < .button("Login", display:.Flex(.Row), height:80, touch:login).align(.Bottom(.Left))>>
        < .view/>
    < .view/>
 
func login(button:UIButton) {
    print(emailTextField!.text)
}
    
func largeRoundedStyle(view:UIView) {
    if let textField = view as? UITextField {
        textField.borderStyle = .RoundedRect
        textField.frame.size.height = 60
        textField.font = UIFont(name: "HelveticaNeue-Light", size: 26)
    }
}

The Basics

UIViewprint 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.

Specifically 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.

Example 1

UIViewprint can be utilized from a controller or any code that initializes a view. In the examples below a basic UIViewController is assumed.

override func viewDidLoad() {
    super.viewDidLoad()
    
    self.view.addSubview(
        div()
    )
}

In 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.

The div function is the primary mechanism to initialize a new view (UIViewable).

Example 2

In 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.

override func viewDidLoad() {
    super.viewDidLoad()
    
    self.view.addSubview(
        div(style(height:10, backgroundColor:.orangeColor()))
    )
    // An alternative fluent style syntax exists that would look like the following
    // div().height(10).backgroundColor(.orangeColor())
}

UIViewprint Example 2

Example 3

In this example UILabels are introduced along with display types which is inherent to each UIViewable.

override func viewDidLoad() {
    super.viewDidLoad()
    
    self.view.addSubview(
	    div(
	        label("Label 1"),
	        label("Label 2"),
	        label("Label 3", style:style(display:.Block))
	    )
	)
}

UIViewprint Example 3

Of 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".

This is similar to the following HTML

	<div>
		Label 1
		Label 2
		<div>Label3</div>
	</div>

Flex rows

Flex 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.

Example 4

override func viewDidLoad() {
    super.viewDidLoad()
    
    self.view.addSubview(
        div(style(display:.Flex(.Row)),
            label("Label 1"),
            label("Label 2"),
            label("Label 3")
        )
    )
}

UIViewprint Example 4

As 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.

If however the first label specifies a width of 100 then the remaining views divide the space evenly after accounting for the fixed width view.

override func viewDidLoad() {
    super.viewDidLoad()
    
    self.view.addSubview(
        div(style(display:.Flex(.Row)),
            label("Label 1").width(100),
            label("Label 2"),
            label("Label 3")
        )
    )
}

Flex columns

Flex 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.

Example 5

override func viewDidLoad() {
    super.viewDidLoad()
    
    self.view.addSubview(
        div(style(display:.Flex(.Column)),
            div(
                label("Label 1")
            ),
            div(
                label("Label 2")
            ),
            div(
                label("Label 3")
            )
        )
    )
}

UIViewprint Example 5

UIKit Controls

UIViewprint provides wrappers for a large number of default iOS UIKit controls. Detailed examples are provided in the project source repository.

  • UILabel
  • UIButton
  • UITextField
  • UIImageView
  • UISegmentedControl
  • UITableView

Please file bug reports or pull requests for controls that are not currently supported or do not provide adequate configuration.

Padding

UIViewprint 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.

div(style(padding:(top:10, right:10, bottom:10, left:10), backgroundColor:.orangeColor()),
	div(style(padding:(top:10, right:10, bottom:10, left:10), backgroundColor:.yellowColor()),
		label("Padded").backgroundColor(.lightGrayColor())
	)
)

UIViewprint Example 8

UITablewView support

UIViewprint 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.

Static table

The 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.

class ViewController: UITableViewableController {
	override func viewDidLoad() {
	    super.viewDidLoad()
	    
	    self.title = "Static table"
	    self.edgesForExtendedLayout = UIRectEdge.None;
	    
	    self.table(
	        header: label("Header"),
	        sections: [
	            section(touchRow,
	                header: "Section One",
	                rows: [
	                    row(
	                        div(
	                            label("Left 1"),
	                            label("Right 1").align(.Top(.Right))
	                        )
	                    ),
	                    row(label("Row 2")),
	                    row(label("Row 3")),
	                    row(label("Row 4")),
	                    row(label("Row 5")),
	                    row(
	                        div(
	                            label("Left 6"),
	                            label("Right 6").align(.Top(.Right))
	                        )
	                    )
	                ]
	            ),
	            section(touchRow,
	                header: "Section Two",
	                rows: [
	                    row(
	                        div(
	                            label("Left 2-1"),
	                            label("Right 2-1").align(.Top(.Right))
	                        )
	                    ),
	                    row(label("Row 2-2")),
	                    row(label("Row 2-3")),
	                    row(label("Row 2-4")),
	                    row(label("Row 2-5")),
	                    row(
	                        div(
	                            label("Left 2-6"),
	                            label("Right 2-6").align(.Top(.Right))
	                        )
	                    )
	                ]
	            )
	        ],
	        footer: div(style(height:100, backgroundColor:.yellowColor()),
	            label("Foot 1").backgroundColor(.grayColor()),
	            label("Foot 2").align(.Top(.Right)).backgroundColor(.orangeColor())
	        )
	    )
	}
	    
	func touchRow(indexPath:NSIndexPath) {
	    print("touch \(indexPath.section) \(indexPath.row)")
	}
}

Basic datasource table

The 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.

A 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.

class UIViewController: UITableViewableController {

    let examples: [ExampleControllerDetails] = [
        Example(name:"Example 1", controller:ViewController1.self),
        Example(name:"Example 2", controller:ViewController2.self),
        Example(name:"Example 3", controller:ViewController3.self)
    ]
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        self.title = "UITableView examples"
        self.edgesForExtendedLayout = UIRectEdge.None;
        
        self.table(
            sections: [
                section(selectExample, foreach:examples) { (example:AnyObject) in
                    let example = example as! Example
                    return self.defaultTableViewCell(example.name)
                }
            ]
        )   
    }
    
    func selectExample(indexPath:NSIndexPath) {
        let controller:UIViewController = self.examples[indexPath.row].controller.init()
        controller.view.frame = view.bounds
        controller.view.backgroundColor = .whiteColor()
        controller.view.autoresizingMask = [UIViewAutoresizing.FlexibleWidth, UIViewAutoresizing.FlexibleHeight]
        
        self.navigationController?.pushViewController(controller, animated: true)
    }
}

Advanced datasource table

Complex 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.

The 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.

class ViewController: UITableViewableController {

    override func viewDidLoad() {
        super.viewDidLoad()

        self.title = "Datasource example"
        self.edgesForExtendedLayout = UIRectEdge.None;

        let apps:[App] = [
            App(name:"iSlash Heroes"),
            App(name:"Tomb of the Mask"),
            App(name:"Day One 2 Journal + Notes + diary"),
            App(name:"twofold inc."),
            App(name:"Guides by Lonely Planet"),
            App(name:"Dungelot: Shattered Lands")
        ]

        self.table(
            header: div(style(height:100, backgroundColor:.lightGrayColor()),
                label("Editors' Choice", style:style(align:.Middle(.Center)))
            ),
            sections: [
                section(touchRow,
                    rows: [
                        row(
                            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)
                        )
                    ]
                ),
                section(touchRow, foreach:apps) { (app:AnyObject) in
                    let app = app as! App
                    return div(
                        div(style(height:10)),
                        div(.Flex(.Row),
                            div(style(width:10)),
                            div(
                                label("\(app.name)")
                            )
                        ),
                        div(style(height:10))
                    )
                }
            ],
            footer: div(
                button("Redeem", touch:handleRedeem),
                button("Send Gift", touch:handleSendGift)
            )
        )

    }

    func touchRow(indexPath:NSIndexPath) {
        print("touch \(indexPath.section) \(indexPath.row)")
    }
    
    func handleRedeem(button:UIButton) {
    }
    
    func handleSendGift(button:UIButton) {
    }
}

Advanced features

Example 6

This example demonstrates how layout compares to the popular Stevia framework.

var emailTextField:UITextField?
var passwordTextField:UITextField?

override func viewDidLoad() {
    super.viewDidLoad()

    self.title = "Example 6"
    self.edgesForExtendedLayout = UIRectEdge.None;
    
    self.view.addSubview(
        div(.Flex(.Column),
            div(),
            div(style(padding:(0,right:10,0,left:10)),
                input(&emailTextField, placeholder:"Email", style:style(padding:(0,0,bottom:5,0)), appearance:largeFontRoundedCorners),
                input(placeholder:"Password", appearance:largeFontRoundedCorners)
            ),
            div(),
            div(
                button("Login", display:.Flex(.Row), align:.Bottom(.Left), height:80, touch:login)
            )
        )
    )
}
    
func login(button:UIButton) {
    print(emailTextField!.text)
}
    
func largeFontRoundedCorners(view:UIView) {
    if let textField = view as? UITextField {
        textField.borderStyle = .RoundedRect
        textField.frame.size.height = 54
        textField.font = UIFont(name: "HelveticaNeue-Light", size: 26)
        textField.returnKeyType = .Done
    }
}

Of 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.

UIViewprint Example 6

Example 6a

This example demonstrates a UIViewprint implementation of the Facebook profile page developed to highlight the features of the Neon framework. Unlike Neon however all the code required to layout the view is listed below.

class ViewController: UIViewableController {
    var bannerView:UIViewable = UIViewable()
    var avatarImageView:UIViewable?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        bannerView.layer.contents = UIImage(named:"banner")?.CGImage
        
        self.view
            < .view(.style(backgroundColor:.lightGrayColor()))+>
                < bannerView+>
                    < .view(.style(.Flex(.Row), align:.Bottom(.Left)))+>
                        < .view(.style(.Flex(.Row)))+>
                            < width(10)>>
                            < image(&avatarImageView, name:"avatar")>>
                            < .view(.style(align:.Middle(.Left)))+>
                                < label("Alex", display:.Block, style:avatarNameStyle)>>
                                < label("Winston", display:.Block, style:avatarNameStyle)>>
                            < .view/>
                        < .view/>
                        < image("camera").align(.Bottom(.Left)).width(30).height(30)>>
                        < width(10)>>
                    < .view/>
                < .view/>
                < UIViewable().display(.Flex(.Row))+>
                    < buttonBarItemView("Post", labelAlign:.Top(.Center), imageName:"post", imageDisplay:.Block, imageAlign:.Top(.Center))>>
                    < buttonBarItemView("Update Info", labelAlign:.Top(.Center), imageName:"updateInfo", imageDisplay:.Block, imageAlign:.Top(.Center))>>
                    < buttonBarItemView("Activity Log", labelAlign:.Top(.Center), imageName:"activityLog", imageDisplay:.Block, imageAlign:.Top(.Center))>>
                    < buttonBarItemView("More", labelAlign:.Top(.Center), imageName:"more", imageDisplay:.Block, imageAlign:.Top(.Center))>>
                < .view/>
                < height(10)>>
                < UIViewable().display(.Flex(.Row))+>
                    < width(10)>>
                    < imageLabelView("About", imageName:"about")>>
                    < width(10)>>
                    < imageLabelView("Photos", imageName:"photos")>>
                    < width(10)>>
                    < imageLabelView("Friends", imageName:"friends")>>
                    < width(10)>>
                < .view/>
                < height(10)>>
                < UIViewable().display(.Flex(.Row))+>
                    < width(10)>>
                    < buttonBarItemView("Post", imageName:"post")>>
                    < buttonBarItemView("Photo", imageName:"activityLog")>>
                    < buttonBarItemView("Life Event", imageName:"more")>>
                    < width(10)>>
                < .view/>
                < height(10)>>
            < .view/>
    }
    
    func buttonBarItemView(labelText:String, labelAlign:UIViewableAlign? = .Middle(.Left), imageName:String, imageDisplay:UIViewableDisplay? = .Inline, imageAlign:UIViewableAlign? = .Top(.Left)) -> UIView {
        func buttonLabelStyle(label:UILabel) {
            label.textColor = UIColor(red: 106/255.0, green: 113/255.0, blue: 127/255.0, alpha: 1.0)
            label.font = .systemFontOfSize(13.0)
        }
        
        return UIViewable().backgroundColor(.whiteColor())
            < image(imageName).display(imageDisplay!).align(imageAlign!).width(28).height(28)>>
            < label(labelText, style:buttonLabelStyle).align(labelAlign!)>>
    }
    
    func imageLabelView(labelText:String, imageName:String) -> UIView {
        func imageLabelStyle(label:UILabel) {
            label.textColor = UIColor.blackColor()
            label.font = UIFont.boldSystemFontOfSize(14.0)
        }
        
        return UIViewable().backgroundColor(.whiteColor())
            < image(imageName, contentMode:.ScaleAspectFill).display(.Block).align(.Top(.Center)).height(60)>>
            < label(labelText, style:imageLabelStyle).align(.Top(.Center))>>
    }

    func avatarNameStyle(label:UILabel) {
        label.font = UIFont(name: "HelveticaNeue-Light", size: 33)
        label.textColor = .whiteColor()
    }

    override func viewWillLayoutSubviews() {
        super.viewWillLayoutSubviews()
        
        let isLandscape:Bool = UIDevice.currentDevice().orientation.isLandscape.boolValue
        let bannerHeight:CGFloat = self.view.frame.height * 0.43
        let avatarHeightMultipler:CGFloat = isLandscape ? 0.75 : 0.43
        let avatarSize = bannerHeight * avatarHeightMultipler
        
        self.bannerView.height(bannerHeight)
        self.avatarImageView!.width(avatarSize).height(avatarSize)
    }
}

UIViewprint Example 6a

View alignment

UIViewprint makes view alignment within a parent relatively painless. Views can be aligned vertically (Top, Middle, Bottom) and horizontally (Left, Center, Right).

Example 7

self.view
    < .flexColumn()+>
        < .flexRow()+>
            < .flexColumn()+>
                < label("Top Left").align(.Top(.Left)).backgroundColor(.yellowColor())>>
            < .view/>
            < .flexColumn()+>
                < label("Top Center").align(.Top(.Center)).backgroundColor(.orangeColor())>>
            < .view/>
            < .flexColumn()+>
                < label("Top Right").align(.Top(.Right)).backgroundColor(.redColor())>>
            < .view/>
        < .view/>
        < .view(style(.Flex(.Row), backgroundColor:.lightGrayColor()))+>
            < .flexColumn()+>
                < label("Mid Left").align(.Middle(.Left)).backgroundColor(.yellowColor())>>
            < .view/>
            < .flexColumn()+>
                < label("Mid Center").align(.Middle(.Center)).backgroundColor(.orangeColor())>>
            < .view/>
            < .flexColumn()+>
                < label("Mid Right").align(.Middle(.Right)).backgroundColor(.redColor())>>
            < .view/>
        < .view/>
        < .flexRow()+>
            < .flexColumn()+>
                < label("Bottom Left Test").align(.Bottom(.Left)).backgroundColor(.yellowColor())>>
            < .view/>
            < .flexColumn()+>
                < label("Bottom Ctr").align(.Bottom(.Center)).backgroundColor(.orangeColor())>>
            < .view/>
            < .flexColumn()+>
                < label("Bottom Right").align(.Bottom(.Right)).backgroundColor(.redColor())>>
            < .view/>
        < .view/>
    < .view/>

UIViewprint Example 7

TODO

  • Consider using SwiftBox for better layout and performance

Contact

Twitter: @alex_winston

You might also like...
UIView based progress bar that shows a progress based on duration in seconds

DurationProgressBar Create a progress bar based on a duration in seconds. The view is fully customisable. Install Add this repository to your swift pa

A tiny category on UIView that allows you to set one property:
A tiny category on UIView that allows you to set one property: "parallaxIntensity" to achieve a parallax effect with UIMotionEffect

NGAParallaxMotion A tiny category on UIView that allows you to set one property: parallaxIntensity to achieve a parallax effect with UIMotionEffect. S

 BrickKit is a delightful layout library for iOS and tvOS. It is written entirely in Swift!
BrickKit is a delightful layout library for iOS and tvOS. It is written entirely in Swift!

BrickKit is a delightful layout library for iOS and tvOS. It is written entirely in Swift! Deprecated BrickKit is being phased out at Wayfair, and the

Fast Swift Views layouting without auto layout. No magic, pure code, full control and blazing fast. Concise syntax, intuitive, readable & chainable. [iOS/macOS/tvOS/CALayer]
Fast Swift Views layouting without auto layout. No magic, pure code, full control and blazing fast. Concise syntax, intuitive, readable & chainable. [iOS/macOS/tvOS/CALayer]

Extremely Fast views layouting without auto layout. No magic, pure code, full control and blazing fast. Concise syntax, intuitive, readable & chainabl

An easy way to create and layout UI components for iOS (Swift version).
An easy way to create and layout UI components for iOS (Swift version).

Introduction Cupcake is a framework that allow you to easily create and layout UI components for iOS 8.0+. It use chaining syntax and provides some fr

An Impressive Auto Layout DSL for  iOS, tvOS & OSX. & It is written in pure swift.
An Impressive Auto Layout DSL for iOS, tvOS & OSX. & It is written in pure swift.

KVConstraintKit KVConstraintKit is a DSL to make easy & impressive Auto Layout constraints on iOS, tvOS & OSX with Swift Installation Using CocoaPods

The ultimate API for iOS & OS X Auto Layout — impressively simple, immensely powerful. Objective-C and Swift compatible.
The ultimate API for iOS & OS X Auto Layout — impressively simple, immensely powerful. Objective-C and Swift compatible.

The ultimate API for iOS & OS X Auto Layout — impressively simple, immensely powerful. PureLayout extends UIView/NSView, NSArray, and NSLayoutConstrai

LayoutKit is a fast view layout library for iOS, macOS, and tvOS.
LayoutKit is a fast view layout library for iOS, macOS, and tvOS.

🚨 UNMAINTAINED 🚨 This project is no longer used by LinkedIn and is currently unmaintained. LayoutKit is a fast view layout library for iOS, macOS, a

A declarative UIKit for improve layout productivity when developing an iOS application

TifoKit A declarative UIKit for improve layout productivity when developing an iOS application Requirements Min. iOS 11 Swift 5+ Installation Currentl

Owner
Alex Winston
Alex Winston
UIView category which makes it easy to create layout constraints in code

FLKAutoLayout FLKAutoLayout is a collection of categories on UIView which makes it easy to setup layout constraints in code. FLKAutoLayout creates sim

Florian Kugler 1.5k Nov 24, 2022
FlightLayout is a light weight, and easy to learn layout framework as an extension of the UIView.

FlightLayout Introduction FlightLayout is a light weight, and easy to learn layout framework as an extension of the UIView. Functionally, it lives som

Anton 23 Apr 21, 2022
AppStoreClone - Understanding the complex layout of app store using UICompositional layout in swift

AppStoreClone Understanding the complex layout of app store using UICompositiona

Dheeraj Kumar Sharma 8 Dec 28, 2022
Auto Layout (and manual layout) in one line.

Auto Layout (and manual layout) in one line. Quick Look view.bb.centerX().below(view2).size(100) It’s equivalent to iOS 9 API: view.centerXAnchor.cons

Javier Zhang 74 Oct 19, 2022
Auto Layout made easy with the Custom Layout.

Auto Layout made easy with the Custom Layout. Getting started CocoaPods CocoaPods is a dependency manager for Cocoa projects. You can install it with

Malith Nadeeshan 1 Jan 16, 2022
A custom layout built on top of SwiftUI's Layout API that lays elements out in multiple lines. Similar to flex-wrap in CSS, CollectionViewFlowLayout.

WrapLayout A custom layout built on top of SwiftUI's Layout API that lays elements out in multiple lines. Similar to flex-wrap in CSS, CollectionViewF

Hiroshi Kimura 6 Sep 27, 2022
Demo App for Picture-in-Picture of Arbitrary UIView in iOS

> 日本語 UIPiPDemo This is a demo app for displaying an arbitrary UIView in iOS using picture-in-picture. It can be used to display information that chan

Akihiro Urushihara 61 Sep 26, 2022
Powerful autolayout framework, that can manage UIView(NSView), CALayer and not rendered views. Not Apple Autolayout wrapper. Provides placeholders. Linux support.

CGLayout Powerful autolayout framework, that can manage UIView(NSView), CALayer and not rendered views. Has cross-hierarchy coordinate space. Implemen

Koryttsev Denis 45 Jun 28, 2022
UIView extension that adds dragging capabilities

YiViewDrag Installation YiViewDrag is available through CocoaPods. To install it, simply add the following line to your Podfile: pod 'YiViewDrag' Usag

coderyi 1 Jan 20, 2022
This library allows you to make any UIView tap-able like a UIButton.

UIView-TapListnerCallback Example To run the example project, clone the repo, and run pod install from the Example directory first. Installation UIVie

wajeehulhassan 8 May 13, 2022