Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/ernest0-production/declarativelayoutkit
UIKit declarative layout helpers
https://github.com/ernest0-production/declarativelayoutkit
autolayout constraints nslayoutconstraints snapkit swiftui uikit uistackview
Last synced: 2 days ago
JSON representation
UIKit declarative layout helpers
- Host: GitHub
- URL: https://github.com/ernest0-production/declarativelayoutkit
- Owner: Ernest0-Production
- License: mit
- Created: 2020-07-18T16:08:21.000Z (over 4 years ago)
- Default Branch: master
- Last Pushed: 2021-07-15T08:08:58.000Z (over 3 years ago)
- Last Synced: 2024-05-22T20:22:21.168Z (8 months ago)
- Topics: autolayout, constraints, nslayoutconstraints, snapkit, swiftui, uikit, uistackview
- Language: Swift
- Homepage:
- Size: 1.35 MB
- Stars: 45
- Watchers: 1
- Forks: 3
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- License: LICENSE.md
Awesome Lists containing this project
README
# DeclarativeLayoutKit
Declarative and type-safe framework for fast UI layout
| | Advantages |
|--- |--------------------------------------------------------------------------------- |
| đ | Fast view configuration using [property chains](#đ-property-chaining)|
| đ | Easily layout using [simplest DSL](#đ§Š-declarative-constraint-builder)|
| đ | ***extra_most_usefull*** helpers for [UIStackView](#đ¨âđ¨âđĻâđĻ-UIStackView-extra_most_usefull-helpers)|
| 𧊠| [**SCALABLE**!](#how-to-extend-chaining-functionality)|## Overview
- [Requirements](#requirements)
- [Usage](#usage)
- [How this framework differs from the others?](#how-this-framework-differs-from-the-others?)
- [Installation](#installation)
- [Credits](#credits)
- [License](#license)## Requirements
- iOS 11.0
- Xcode 12.5
- Swift 5.4# Usage
## đ Property chaining
Absolutely **all mutable properties** are represented in the function using code generation powered by [Sourcery](https://github.com/krzysztofzablocki/Sourcery).```swift
let myLabel = UILabel()
.numberOfLines(0)
.text("Hello buddy")
.backgroundColor(.blue)
.isHighlighted(true)
.borderWidth(1)
.borderColor(.cyan)
```Currently, property chaining is supported in the following types:
`UIView`, `UIControl`, `UILabel`, `UIImageView`, `UIScrollView`, `UITextView`, `UITableView`, `UICollectionView`, `UITextField`, `UIButton`, `UISlider`, `UISwitch`, `UIStackView`.> You can also easily generate functions for other types â [see how](#add-property-chaining-to-another-type)
### đ And some extra syntactic sugar:
Assignable to variable:
```swift
class ViewController: UIViewController {
weak var myLabel: UILabel!override func loadView() {
...view.addSubview(
UILabel()
.numberOfLines(0)
.text("Voila")
// sets a reference of object that calls function(in this case, created UILabel instance) to passed variable
.assign(to: &myLabel)
)...
}
}
```
Closure based actions and gestures:
```swift
UIControl()
.addAction(for: .valueChanged, { print("value changed") })UIButton()
.title("Tap me")
.onTap({ print("didTap") }) // wrap .addAction(for: .touchUpInside, { .. })UIView()
.onTapGesture({ print("Kek") })
.onLongTapGesture({ print("Cheburek") })// â ī¸ Don't forget about ARC when use some parent view in action closure, to prevent retain cycle
```
## đ¨âđ¨âđĻâđĻ UIStackView **extra_most_usefull** helpersPreconfigured stack initializer:
```swift
HorizontalStack([...]) // axis = .horizontalHorizontalCenterStack([...]) // axis = .horizontal; alignment = .center
TopStack([...]) // axis = .horizontal; alignment = .top
BottomStack([...]) // axis = .horizontal; alignment = .bottom
VerticalStack([...]) // axis = .vertical
VerticalCenterStack([...]) // axis = .vertical; alignment = .center
LeftStack([...]) // axis = .vertical; alignment = .leading
RightStack([...]) // axis = .vertical; alignment = .trailing
```Declarative spacing:
```swift
VerticalStack([
SomeLabel(),UIStackViewSpace(12)
SomeButton()
UIStackViewSpace(15)
UIImageView(image: ...)
SomeView()
...
])
```@ArrangedViewBuilder functionBuilder:
```swift
HorizontalStack {
AvatarImageView()UIStackViewSpace(16)
TopStack {
NameLabel()if user.isPremium {
PremiumMarkerView()
}UIStackViewSpace(8)
HStackView(tagsViews)
}HorizontalCenterStack {
ShareButton()
DisclosureButton()
}.spacing(4).distribution(.fillEquality)
}
```## 𧊠Declarative Constraint Builder
You can set constraints using the same chainable style with anchor constraint.The return type of builder will be `AutoLayoutItem` â a simple storage of constants instructions. To build and activate them, just call `activate()` function.
```swift
let myLabel = UILabel()
.numberOfLines(0) // -> UIView
...
.heightAnchor(0) // -> AutoLayoutItem (same below)
.topAnchor(16.from(anotherView.topAnchor))
.leftAnchor(24)
.rightAnchor(24.orLess.pririty(750))
.verticalAnchor(backgroundView)
.activate() // -> UIView (with applyed constraints)
```### 𧎠Constraint Builder DSL Specification
#### Attributes
- `constant` - Int/Float/CGFloat/...
- target (only relative constraints):
- - from(target: NSLayoutAnchor) - from the outside of the view.\
Example: `firstView.rightAnchor(24.from(secondView.leftAnchor))`- - to(target: NSLayoutAnchor) - from the inside of the view.\
Example: `subview.leftAnchor(16.to(superview.leftAnchor))`- - without this attribute builder will apply instruction to superview.\
Example: `subview.leftAnchor(16)` equivalent to the previous example- relationType
- - `Equal` (by default)
- - `orLess`\
Example: `15.orLess`; `15.to(secondView.leftAnchor).orLess`
- - `orGreater`\
Example: `15.orGreater`; `15.to(secondView.leftAnchor).orGreater`- priorioty
- - `15.priority(.defaultLow)`
- - or `15.priority(250)`
- - **â ī¸ without this attribute builder will apply instruction with priority 999**- multiplier (only dimension constraints)\
Example: `headerView.heightAnchor(backgroundView.multiplied(0.5).orLess)`Final Formula:
```
constant.from|to(_ target: NSLayoutAnchor).priority(NSLayoutPriority).orLess|orGreater
```
The order of the attributes is **arbitrary**:
```swift
15.from(secondView.topAnchor).orLess
20.orLess.to(secondView.bottomAnchor).priority(1000)
8.priority(.required).orGreater
```Extra anchors:
- `horizontalAnchor` â leftAnchor + rightAnchor\
Example: `subview.horizontalAnchor(16)`- `verticalAnchor` â topAnchor + bottomAnchor\
Example: `subview.verticalAnchor(16.orLess)`- `centerAnchor` - centerXAnchor + centerYAnchor\
Example: `avatarView.centerAnchor(backgroundView)`- `sizeAnchor` - widthAnchor + heightAnchor\
Example: `avatarView.sizeAnchor(60)`- `edgesAnchors(insets: UIEdgeInsets, to target: UIView?, priority: UILayoutPriority)` - combination of the left|top|right|bottom anchors.
- `layout(_ builder: (UIView) -> AutoLayoutItem)` - self-related anchor\
Example: `myView.layout({ $0.heightAnchor($0.widthAnchor) })`#### 𧊠View/Builder Composition
```swift
private(set) weak var avatarView: UIImageView!...
let profileView = UIView()
.backgroundColor(.gray)
.heightAnchor(100)
.add({
UIImageView()
.assign(to: &avatarView)
.contentMode(.scaleAspectFit)
.sizeAnchor(40)
.leftAnchor(16)
.verticalAnchor(0)UILabel()
.numberOfLines(2)
.rightAnchor(0)
.leftAnchor(8.from(avatarView.leftAnchor).priority(.required))
})
.activate()
```
Or using a convenience initializer
```swiftlet profileView = UIView {
UIImageView()
.assign(to: &avatarView)
.contentMode(.scaleAspectFit)
.sizeAnchor(40)
.leftAnchor(16)
.verticalAnchor(0)UILabel()
.numberOfLines(2)
.rightAnchor(0)
.leftAnchor(8.from(avatarView.leftAnchor).priority(.required))
}
.backgroundColor(.gray)
.heightAnchor(100)
.activate()
```**âšī¸ NOTE:** if you add constraint-builders to a `UIView` (i.e. don't use `anchor-chaining` before call `add(...)`), constraints activations will occur **immediately after adding** in `superview`. In other words, the return type of the `activate()` function will be `Self` (i.e. `UIView`)
```swift
let profileView = UIView()
.backgroundColor(.gray)
.add({
UIImageView()
.assign(to: &avatarView)
.contentMode(.scaleAspectFit)
.sizeAnchor(40)
.leftAnchor(16)
.verticalAnchor(0)
UILabel()
.numberOfLines(2)
.rightAnchor(0)
.leftAnchor(8.from(avatarView.leftAnchor).priority(.required))
}) // -> UIView (with already added subviews)
// and you can continue chainable-configuration (for example by specifying own anchors)
.heightAnchor(100) // -> AutoLayoutItem
.activate() // -> UIView
```## How to extend chaining functionality?
First way â write type extension with function that return self:
```swift
extension MyCustomView {
func myProperty(_ value: ValueType) -> Self {
self.myProperty = value
return self
}
}
```
Second way â using [Sourcery](https://github.com/krzysztofzablocki/Sourcery) apply [Chainable template](https://github.com/Ernest0-Production/DeclarativeLayoutKit/blob/master/Sources/Generator/Chainable.stencil) with your custom view
([see tutorial](https://www.caseyliss.com/2017/3/31/the-magic-of-sourcery)).## How this framework differs from the others?
There is **huge** number of layout frameworks...\
The **main goal** of creating another one framework was not to create another one framework, but to **make tools for rapid writing of layout code*\
SnapKit, many people know, is one of these solutions, but I decided to make **a more declarative and simple solution**.\
> P.S. in previous versions the Framework was based on DSL SnapKit, but now has its own, more type-safe.This `README` decription is all you need to know, no redundant documentation needed.\
### đ You can **easily integrate** the framework into project and **combine it with old/existing layout code**.
## Installation
#### [CocoaPods](https://guides.cocoapods.org/using/using-cocoapods.html)
```ruby
# Podfile
use_frameworks!target 'YOUR_TARGET_NAME' do
pod 'DeclarativeLayoutKit'
end
```Replace `YOUR_TARGET_NAME` and then, in the `Podfile` directory, type:
```bash
$ pod install
```#### [Swift Package Manager](https://github.com/apple/swift-package-manager)
Create a `Package.swift` file.
```swift
// swift-tools-version:5.0import PackageDescription
let package = Package(
name: "YOUR_PROJECT_NAME",
dependencies: [
.package(url: "https://github.com/Ernest0-Production/DeclarativeLayoutKit.git", from: "3.0.2")
],
targets: [
.target(name: "YOUR_TARGET_NAME", dependencies: ["DeclarativeLayoutKit"])
]
)
```### Credits
- [Telegram](https://t.me/Ernest0n)
### License
DeclarativeLayoutKit is released under the MIT license. See [LICENSE](https://github.com/Ernest0-Production/DeclarativeLayoutKit/blob/master/LICENSE.md) for details.