MondrianLayout
๐ A way to build AutoLayout rapidly than using InterfaceBuilder(XIB, Storyboard) in iOS.
Install / Use
/learn @FluidGroup/MondrianLayoutREADME
MondrianLayout
A way to build AutoLayout rapidly than using InterfaceBuilder(XIB, Storyboard) in iOS.
Describing the layout ergonomically in the code
๐ Structured Layout API
Mondrian.buildSubviews(on: view) {
VStackBlock {
titleLabel
HStackBlock {
cancelButton
sendButton
}
}
.padding(24)
}
๐ To preserve flexibility of AutoLayout, we have classical style API
Classical Layout API
sendButton.mondrian.layout
.width(120)
.top(.toSuperview)
.trailing(.toSuperview)
.leading(.to(cancelButton).trailing)
.activate()
Support the project
<a href="https://www.buymeacoffee.com/muukii"> <img width="545" alt="yellow-button" src="https://user-images.githubusercontent.com/1888355/146226808-eb2e9ee0-c6bd-44a2-a330-3bbc8a6244cf.png"> </a><img width="246" alt="CleanShot 2021-06-17 at 21 12 03@2x" src="https://user-images.githubusercontent.com/1888355/122394225-b1da4e80-cfb0-11eb-9e62-f5627c817b66.png">๐คต๐ปโโ๏ธ๐ญ We guess we still don't cover the all of use-cases. Please feel free to ask what you've faced case in Issues!
This image laid out by MondrianLayout
<details><summary>Layout code</summary> <p>HStackBlock(spacing: 2, alignment: .fill) {
VStackBlock(spacing: 2, alignment: .fill) {
UIView.mock(
backgroundColor: .mondrianRed,
preferredSize: .init(width: 28, height: 28)
)
UIView.mock(
backgroundColor: .layeringColor,
preferredSize: .init(width: 28, height: 50)
)
UIView.mock(
backgroundColor: .mondrianYellow,
preferredSize: .init(width: 28, height: 28)
)
UIView.mock(
backgroundColor: .layeringColor,
preferredSize: .init(width: 28, height: 28)
)
HStackBlock(alignment: .fill) {
UIView.mock(
backgroundColor: .layeringColor,
preferredSize: .init(width: 28, height: 28)
)
UIView.mock(
backgroundColor: .layeringColor,
preferredSize: .init(width: 28, height: 28)
)
}
}
VStackBlock(spacing: 2, alignment: .fill) {
HStackBlock(spacing: 2, alignment: .fill) {
UIView.mock(
backgroundColor: .layeringColor,
preferredSize: .init(width: 28, height: 28)
)
VStackBlock(spacing: 2, alignment: .fill) {
HStackBlock(spacing: 2, alignment: .fill) {
UIView.mock(
backgroundColor: .mondrianYellow,
preferredSize: .init(width: 28, height: 28)
)
UIView.mock(
backgroundColor: .layeringColor,
preferredSize: .init(width: 28, height: 28)
)
}
UIView.mock(
backgroundColor: .layeringColor,
preferredSize: .init(width: 28, height: 28)
)
}
}
HStackBlock(spacing: 2, alignment: .fill) {
VStackBlock(spacing: 2, alignment: .fill) {
UIView.mock(
backgroundColor: .layeringColor,
preferredSize: .init(width: 28, height: 28)
)
UIView.mock(
backgroundColor: .mondrianBlue,
preferredSize: .init(width: 28, height: 28)
)
}
UIView.mock(
backgroundColor: .layeringColor,
preferredSize: .init(width: 28, height: 28)
)
VStackBlock(spacing: 2, alignment: .fill) {
UIView.mock(
backgroundColor: .layeringColor,
preferredSize: .init(width: 28, height: 28)
)
UIView.mock(
backgroundColor: .layeringColor,
preferredSize: .init(width: 28, height: 28)
)
}
}
HStackBlock(spacing: 2, alignment: .fill) {
UIView.mock(
backgroundColor: .mondrianRed,
preferredSize: .init(width: 28, height: 28)
)
VStackBlock(spacing: 2, alignment: .fill) {
UIView.mock(
backgroundColor: .layeringColor,
preferredSize: .init(width: 28, height: 28)
)
UIView.mock(
backgroundColor: .layeringColor,
preferredSize: .init(width: 28, height: 28)
)
}
}
}
}
.overlay(
UILabel.mockMultiline(text: "Mondrian Layout", textColor: .white)
.viewBlock
.padding(4)
.background(
UIView.mock(
backgroundColor: .layeringColor
)
.viewBlock
)
.relative(bottom: 8, right: 8)
)
</p>
</details>
Structured layout API and Classical layout API
- ๐ Ergonomically Enables us to describe layout by DSL (like SwiftUI's layout).
- ๐ Automatic adding subviews according to layout representation.
- ๐ Supports integeration with system AutoLayout API.
- ๐ Provides classical layout API that describing constraints each view.
Introduction
A DSL based layout builder with AutoLayout
AutoLayout is super powerful to describe the layout and how it changes according to the bounding box.
What if we get a more ergonomic interface to declare the constraints.
Structured layout API
| | | |---|---| |<img width="364" src="https://user-images.githubusercontent.com/1888355/122650919-75555100-d170-11eb-8edf-834497dec211.png" />|<img width="364" alt="CleanShot 2021-06-17 at 21 13 11@2x" src="https://user-images.githubusercontent.com/1888355/122394356-d9311b80-cfb0-11eb-8c8c-f5117593ffbe.png">|
<img width="649" alt="CleanShot 2021-06-17 at 21 14 10@2x" src="https://user-images.githubusercontent.com/1888355/122394462-f9f97100-cfb0-11eb-9838-91f22c148bd9.png">Unstructured layout API (classic style)
<img width="330" alt="classic@2x" src="https://user-images.githubusercontent.com/1888355/124358722-84ea9480-dc5c-11eb-95f7-e0250779b65c.png">Future direction
- Optimize the code - still verbose implementation because we're not sure if the API can be stable.
- Brushing up the DSL - to be stable in describing.
- Adding more modifiers for fine tuning in layout.
- Tuning up the stack block's behavior.
- Adding a way to setting constraints independently besides DSL
- AutoLayout is definitely powerful to describe the layout. We might need to set the constraints additionally since DSL can't describe every pattern of the layout.
Demo app
You can see many layout examples from the demo application.
https://user-images.githubusercontent.com/1888355/122651186-142e7d00-d172-11eb-8bde-f4432d0a0ac9.mp4
Overview
MondrianLayout enables us to describe layouts of subviews by DSL (powered by resultBuilders)
It's like describing in SwiftUI, but this behavior differs a bit since laying out by AutoLayout system.
To describe layout, use buildSubviews as entrypoint.
This method creates a set of NSLayoutConstraint, UILayoutGuide, and modifiers of UIView.
Finally, those apply. You don't need to call addSubview. that goes automatically according to hierarchy from layout descriptions.
class MyView: UIView {
let nameLabel: UILabel
let detailLabel: UILabel
init() {
super.init(frame: .zero)
// Seting up constraints constraints, layoutGuides and adding subviews
Mondrian.buildSubviews(on: self) {
VStackBlock {
nameLabel
detailLabel
}
}
// Seting up constraints for the view itself.
Mondrian.layout {
self.mondrian.layout.width(200) // can be method cain.
}
}
}
Examples
Sample code assumes run in UIView. (self is UIView)
You can replace it with UIViewController.view.
Layout subviews inside safe-area
Attaching to top and bottom safe-area.
Mondrian.buildSubviews(on: self) {
LayoutContainer(attachedSafeAreaEdges: .vertical) {
VStackBlock {
...
}
}
}
or
Mondrian.buildSubviews(on: self) {
VStackBlock {
...
}
.container(respectingSafeAreaEdges: .vertical)
}
Put a view snapping to edge
Mondrian.buildSubviews(on: self) {
ZStackBlock {
backgroundView.viewBlock.relative(0)
}
}
synonyms:
ZStackBlock(alignment: .attach(.all)) {
backgroundView
}
ZStackBlock {
backgroundView.viewBlock.alignSelf(.attach(.all))
}
Add constraints to view itself - using classic layout
Mondrian.layout {
self.mondrian.layout.width(...).height(...)
}
or
self.mondrian.layout.width(...).height(...).activate()
Stacking views on Z axis
relative(0) fills to the edges of ZStackBlock.
Mondrian.buildSubviews(on: self) {
ZStackBlock {
profileImageView.viewBlock.relative(0)
textOverlayView.viewBlock.relative(0)
}
}
Centering a label with minimum padding
ZStackBlock {
myLabel
.relative(.all, .min(20))
}
ZStackBlock {
ZStackBlock {
myLabel
}
.padding(20) /// a minimum padding for the label in the container
}
Detail
Vertically and Horizontally Stack layout
VStackBlock
Alignment | center(default) | leading | trailing | fill | |---|---|---|---| |<img width="155" alt="CleanShot 2021-06-17 at 00 06 10@2x" src="https://user-images.githubusercontent.com/1888355/122244438-d75b4f80-ceff-11eb-90ea-8982758ed0b0.png">|<img width="151" alt="CleanShot 2021-06-17 at 00 05 19@2x" src="https://user-images.githubusercontent.com/1888355/122244276-b7c42700-ceff-11eb-90d0-492c3fbc5076.png">|<img width="159" alt="CleanShot 2021-06-17 at 00 05 33@2x" src="https://user-images.githubusercontent.com/1888355/122244312-c01c6200-ceff-11eb-888d-0a37b63f666a.png">|<img width="159" alt="CleanShot 2021-06-17 at 00 05 42@2x" src="https://user-images.githubusercontent.com/1888355/122244341-c6124300-ceff-11eb-9da8-dcbb4425909a.png">|
HStackBlock
| center(default) | top | bottom | fill | |---|---|---|---| |<img width="358" alt="CleanShot 2021-06-17 at 00 09 43@2x" src="https://user-images.githubusercontent.com/1888355/122245037-5486c480-cf00-11eb-872a-e98cfce7262e.png">|<img width="359" alt="CleanShot 2021-06-17 at 00 09 51@2x" src="https://user-images.githubusercontent.com/1888355/122245054-58b2e200-cf00-11eb-9691-607a
Related Skills
node-connect
343.1kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
90.0kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
343.1kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
qqbot-media
343.1kQQBot ๅฏๅชไฝๆถๅ่ฝๅใไฝฟ็จ <qqmedia> ๆ ็ญพ๏ผ็ณป็ปๆ นๆฎๆไปถๆฉๅฑๅ่ชๅจ่ฏๅซ็ฑปๅ๏ผๅพ็/่ฏญ้ณ/่ง้ข/ๆไปถ๏ผใ
