Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/wangshengjia/leego

Declarative, configurable & highly reusable UI development as making Lego bricks.
https://github.com/wangshengjia/leego

autolayout component-driven declarative-ui ios swift uiview

Last synced: about 7 hours ago
JSON representation

Declarative, configurable & highly reusable UI development as making Lego bricks.

Awesome Lists containing this project

README

        

LeeGo

LeeGo is a lightweight Swift framework that helps you decouple & modularise your UI component into small pieces of LEGO style's bricks, to make UI development declarative, configurable and highly reusable.

## Rational behind
We all know that MVC pattern has some serious problems when dealing with a complex iOS project. Fortunately there are also a bunch of approaches that aim to fix the problems, most of them mainly address the `Controller` part, such as MVP, MVVM, MVSM or VIPER. But there is barely a thing which addresses the `View` part. Does that mean we just run out of all the problems in the `View` part ? I think the answer is NO, especially when we need our app to be fully responsive.

I’ve talked this idea on a [dotSwift’s talk](http://www.thedotpost.com/2016/01/victor-wang-build-ios-ui-in-the-way-of-lego-bricks), and also on my blog posts:
- [Build UI without UIView](http://allblue.me/swift/2016/05/12/reuse-everything-of-your-UI-components/)
- [Put your whole UI into struct & enum with LeeGo](http://allblue.me/swift/2016/05/25/LeeGo-UI-developpment/)

Please checkout through for more details.

LeeGo, replaces the `View` part of MVC by `Brick`.

LeeGo

## Features

**What may LeeGo helps you:**

- **Describe** your whole UI in small pieces of Lego style’s bricks. Let you configure your view as a `brick` whenever & wherever you want.
- No longer need to deal with a bunch of custom UIView’s subclasses. Instead, you only need to deal with different `Brick`s which is **lightweight** and **pure value type**.
- Designed to be **UIKit friendly** and **non-intrusive**. There is no need to inherit from other base class at all.
- Capable to **update remotely** almost everything via your JSON payload.
- Built-in convenience methods to make **UIStackView like layout** hassle-free.
- Built-in **self-sizing mechanism** to calculate cell’s height automatically.
- Method chaining syntax sugar.
- Benefits from Swift’s enum, let you put the whole UI in a single enum file.

**Compare with Facebook ComponentKit**

Both:
- Brings declarative, configurable & reusable UI development for iOS.

Pros:
- Written in Swift, built for Swift. No more Obj-C++ stuff.
- Lightweight and UIKit friendly. No inheritance, dealing with standard UIView and Auto Layout directly.
- Totally smooth to begin with integrating only a small part, also free to drop all without any side effect.
- Possible to update any part of UI which powered by LeeGo remotely via JSON payload.
- Powered by standard auto layout which you probably familiar with already.

Cons:
- Lack of high level features for the moment. Such as support of built-in configurable view controller, view animation, auto layout animation or UIControl component’s action.
- Powered by standard auto layout which may have some [potential performance issues](http://floriankugler.com/2013/04/22/auto-layout-performance-on-ios/) in some circumstances.
- Still requires the basic knowledge of standard auto layout and [Visual Format Language](https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/AutolayoutPG/VisualFormatLanguage.html).

## Full Documentation
- [Full Documentation](http://cocoadocs.org/docsets/LeeGo/0.6.0/)
- [Native UI powered by server side](Docs/Remote.md)
- [Configurable appearance](Docs/Appearance.md)
- [Built-in convenience methods for layout](Docs/Layout.md)
- [Dynamic cell height](Docs/Cell.md)

## Usages
#### Basic bricks
Create a `Brick` instance which named "title" as UILabel, with default text "Showcase" and gray background color

```swift
import LeeGo

let titleBrick: Brick = "title".build(UILabel).style([.text("Showcase"), .backgroundColor(.lightGrayColor())])
```
Configure an `UILabel` instance just as title brick

```swift
let titleLabel = UILabel()
titleLabel.lg_configureAs(titleBrick)
```


#### More complex bricks
Create the bricks inside a cell brick

```swift

let titleBrick = "title".build(UILabel).style([.numberOfLines(0), .text("title")])
let subtitleBrick = "subtitle".build(UILabel).style([.textColor(.lightGrayColor()), .numberOfLines(0), .font(.systemFontOfSize(14)), .text("subtitle")])
let imageBrick = "image".build(UIImageView).style([.ratio(1.5), .backgroundColor(.blueColor())]).width(68)

/// Create a brick stand for `UIView` which contains a `title`,
/// a `subtitle` and an `image` inside, layout them with
/// standard auto layout VFL.
let brickName = "cell"
let cellBrick = brickName.build().bricks(titleBrick, subtitleBrick, imageBrick) {
title, subtitle, image in
return Layout([
"H:|-left-[\(image)]-spaceH-[\(title)]-right-|",
"H:[\(image)]-spaceH-[\(subtitle)]-right-|",
"V:|-top-[\(title)]-spaceV-[\(subtitle)]-(>=bottom)-|",
"V:|-top-[\(image)]-(>=bottom)-|"], metrics: LayoutMetrics(20, 20, 20, 20, 10, 10))
}
```

Dequeue a standard `UICollectionViewCell` instance, then configure it as cell brick with `element` as data source

```swift
let cell = collectionView.dequeueCell…
cell.lg_configureAs(cellBrick, dataSource: element[indexPath.item])
```

#### UIStackView inspired layout
Create a brick stand for `UIView` which contains the 3 bricks (red, green & blue block), then lay them out with the `UIStackView` inspired layout helper method.

```swift
let bricks = ["red".build().style(Style.redBlockStyle).height(50),
"green".build().style(Style.greenBlockStyle).height(80),
"blue".build().style(Style.blueBlockStyle).height(30)]

let layout = Layout(bricks: bricks, axis: .Horizontal, align: .Top, distribution: .FillEqually, metrics: LayoutMetrics(10, 10, 10, 10, 10, 10))
let viewBrick = "view".build().style(Style.blocksStyle).bricks(bricks, layout: layout).height(100)
```

Configure a `UIView` instance just as the brick

```swift
view.lg_configureAs(viewBrick)
```


#### Union different bricks
Union different bricks to a new brick with `UIStackView` style’s layout.

```swift
let viewBrick = Brick.union("brickName", bricks: [
title,
subtitle,
Brick.union("blocks", bricks: [
redBlock.height(50),
greenBlock.height(80),
blueBlock.height(30)], axis: .Horizontal, align: .Top, distribution: .FillEqually, metrics: LayoutMetrics(10, 10, 10, 10, 10, 10)).style([.backgroundColor(.yellowColor())])
], axis: .Vertical, align: .Fill, distribution: .Flow(3), metrics: LayoutMetrics(20, 20, 20, 20, 10, 10))

```

Configure a `UIView` instance just as the brick

```swift
view.lg_configureAs(viewBrick)
```


#### More complex brick and build with an enum
An enum which implements `BrickBuilderType`, used to centralize all `brick` designs in a single enum file.

```swift
import LeeGo

enum TwitterBrickSet: BrickBuilderType {
// leaf bricks
case username, account, avatar, tweetText, tweetImage, date, replyButton, retweetButton, retweetCount, likeButton, likeCount

// complex bricks
case retweetView, likeView
case accountHeader, toolbarFooter, retweetHeader

// root bricks
case standardTweet

static let brickClass: [Twitter: AnyClass] = [username: UILabel.self, account: UILabel.self, avatar: UIImageView.self, tweetText: UITextView.self]

func brick() -> Brick {
switch self {
case .username:
return build().style([.font(.boldSystemFontOfSize(14))])
case .account:
return build().style([.font(.systemFontOfSize(14))])
case .avatar:
return build().style([.ratio(1), .backgroundColor(.lightGrayColor()), .cornerRadius(3)]).width(50)
case .tweetText:
return build().style([.scrollEnabled(false)])

case .standardTweet:
return build().style([.backgroundColor(.whiteColor())])
.bricks(
avatar.brick(),
accountHeader.brick(),
tweetText.brick(),
tweetImage.brick(),
toolbarFooter.brick()
) { (avatar, accountHeader, tweetText, image, toolbarFooter) in
Layout(["H:|-10-[\(avatar)]-10-[\(tweetText)]-10-|",
"H:[\(avatar)]-10-[\(accountHeader)]-10-|",
"H:[\(avatar)]-10-[\(image)]-10-|",
"H:[\(avatar)]-10-[\(toolbarFooter)]-10-|",
"V:|-10-[\(avatar)]-(>=10)-|",
"V:|-10-[\(accountHeader)]-10-[\(tweetText)]-10-[\(image)]-10-[\(toolbarFooter)]-(>=10)-|"])
}
}
}
}

/// Configure your cell
let cell = collectionView.dequeueCell…
cell.lg_configureAs(TwitterBrickSet.standardTweet.brick(), dataSource: element[indexPath.item])
```


## Update UI remotely
`Brick` is designed to be JSON convertible, which makes possible that you can control your app’s interface, from tweak some UIKit appearances to create view/cell with brand new design **remotely** via JSON payload. Please check out ["JSON encodable & decodable"](Docs/Remote.md) for more details.

## Best practices
For best practices and more design details, please checkout [more design details](Docs/Design.md)

## Installation
#### CocoaPods
LeeGo is available through [CocoaPods](http://cocoapods.org). To install it, simply add the following line to your Podfile:

```ruby
source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '8.0'
use_frameworks!

pod "LeeGo"
```
#### Carthage
To integrate LeeGo into your Xcode project using Carthage, specify it in your Cartfile:

```
github "wangshengjia/LeeGo"
```

Then, run the following command to build the LeeGo framework:

```
$ carthage update
```

At last, you need to set up your Xcode project manually to add the LeeGo framework.

## Contributing

If you like LeeGo and willing to make it better, you are more than welcomed to send pull request for:

- Proposing new features.
- Answering questions on issues.
- Improving documentation.
- Reviewing pull requests.
- Finding, reporting or fixing bugs.

Please note that this project is released with a Contributor Code of Conduct. By participating in this project you agree to abide by [its terms](Docs/Code of Conduct.md).

## Well...
If you have any thing want to tell me, please ping me on [Twitter](http://twitter.com/wangshengjia), on [Weibo](http://www.weibo.com/1739447693) or just fire the issue.

I'd like to thank every one who helped me, inspired me and encouraged me. Also thank to my team at [Le Monde](http://lemonde.fr/), especially [Vincent](https://twitter.com/vipom) & [Blaise](https://github.com/bsarr007).

Enjoy~ 🎉 🎉 🍻 🍻