MondrianLayout

一种比在 iOS 中使用 InterfaceBuilder(XIB、Storyboard)更快速地构建 AutoLayout 的方法。

在代码中符合人体工程学地描述布局

🏗 结构化布局 API

Mondrian.buildSubviews(on: view) {
  VStackBlock {
  
    titleLabel
    
    HStackBlock {
      cancelButton
      sendButton
    }      
  }
  .padding(24)
}

💃 为了保留 AutoLayout 的灵活性,我们提供了经典的 API 风格

经典布局 API

sendButton.mondrian.layout
  .width(120)
  .top(.toSuperview)
  .trailing(.toSuperview)
  .leading(.to(cancelButton).trailing)
  .activate()

支持本项目

yellow-button

🤵🏻‍♂️💭 我们认为我们仍然没有涵盖所有使用场景。 请随时在 Issues 中提出您遇到的情况!

CleanShot 2021-06-17 at 21 12 03@2x

此图像由 MondrianLayout 布局

布局代码

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)
)

结构化布局 API 和经典布局 API

简介

基于 DSL 的 AutoLayout 布局构建器

AutoLayout 在描述布局以及它如何根据边界框变化方面非常强大。
如果我们能获得更符合人体工程学的接口来声明约束会怎样。

结构化布局 API

CleanShot 2021-06-17 at 21 13 11@2x

CleanShot 2021-06-17 at 21 14 10@2x

非结构化布局 API(经典风格)

classic@2x

未来方向

演示应用程序

您可以从演示应用程序中看到许多布局示例。

Simulator.Screen.Recording.-.iPhone.8.-.2021-06-20.at.02.48.38.mp4

概述

MondrianLayout 使我们能够通过 DSL(由 resultBuilders 提供支持)描述子视图的布局
它就像在 SwiftUI 中描述一样,但此行为略有不同,因为它是由 AutoLayout 系统布局的。

要描述布局,请使用 buildSubviews 作为入口点。
此方法创建一组 NSLayoutConstraint、UILayoutGuide 和 UIView 的修饰符。
最后,这些适用。 您不需要调用 addSubview。 这会根据布局描述的层次结构自动进行。

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

例子

示例代码假定在 UIView 中运行。(self 是 UIView
您可以将其替换为 UIViewController.view

在安全区域内布局子视图

附加到顶部和底部安全区域。

Mondrian.buildSubviews(on: self) {
  LayoutContainer(attachedSafeAreaEdges: .vertical) {
    VStackBlock {
      ...
    }
  }
}

或者

Mondrian.buildSubviews(on: self) {
  VStackBlock {
    ...
  }
  .container(respectingSafeAreaEdges: .vertical)
}

放置一个视图,捕捉到边缘

Mondrian.buildSubviews(on: self) {
  ZStackBlock {
    backgroundView.viewBlock.relative(0)    
  }
}

同义词

ZStackBlock(alignment: .attach(.all)) {
  backgroundView
}
ZStackBlock {
  backgroundView.viewBlock.alignSelf(.attach(.all))
}

将约束添加到视图本身 - 使用经典布局

Mondrian.layout {
  self.mondrian.layout.width(...).height(...)
}

或者

self.mondrian.layout.width(...).height(...).activate()

在 Z 轴上堆叠视图

relative(0) 填充到 ZStackBlock 的边缘。

Mondrian.buildSubviews(on: self) {
  ZStackBlock {
    profileImageView.viewBlock.relative(0)
    textOverlayView.viewBlock.relative(0)
  }
}

居中带有最小填充的标签

ZStackBlock {
  myLabel
    .relative(.all, .min(20))
}
ZStackBlock {
  ZStackBlock {
    myLabel
  }
  .padding(20) /// a minimum padding for the label in the container
}

详情

垂直和水平堆叠布局

VStackBlock

对齐

center(默认) leading trailing fill
CleanShot 2021-06-17 at 00 06 10@2x CleanShot 2021-06-17 at 00 05 19@2x CleanShot 2021-06-17 at 00 05 33@2x CleanShot 2021-06-17 at 00 05 42@2x

HStackBlock

center(默认) top bottom fill
CleanShot 2021-06-17 at 00 09 43@2x CleanShot 2021-06-17 at 00 09 51@2x CleanShot 2021-06-17 at 00 09 59@2x CleanShot 2021-06-17 at 00 10 06@2x
Mondrian.buildSubviews(on: self) {
  VStackBlock(spacing: 4, alignment: alignment) {
    UILabel.mockMultiline(text: "Hello", textColor: .white)
      .viewBlock
      .padding(8)
      .background(UIView.mock(backgroundColor: .mondrianYellow))
    UILabel.mockMultiline(text: "Mondrian", textColor: .white)
      .viewBlock
      .padding(8)
      .background(UIView.mock(backgroundColor: .mondrianRed))
    UILabel.mockMultiline(text: "Layout!", textColor: .white)
      .viewBlock
      .padding(8)
      .background(UIView.mock(backgroundColor: .mondrianBlue))
  }
}

StackingSpacer

在堆叠布局块中添加一个空格。

Background 修饰符

label
  .viewBlock // To enable view describes layout
  .padding(8)
  .background(backgroundView)

CleanShot 2021-06-17 at 00 14 52@2x

Overlay 修饰符

label
  .viewBlock // To enable view describes layout
  .padding(8)
  .overlay(overlayView)

Relative 修饰符

.relative 修饰符描述内容附加到带有填充的指定边缘。
未指定的边缘没有到边缘的约束。 因此,大小取决于固有内容大小。

您可以使用此修饰符将边缘固定为叠加内容。

ZStackBlock {
  VStackBlock {
    ...
  }
  .relative(bottom: 8, right: 8)
}

Padding 修饰符

.padding 修饰符与 .relative 相似,但有所不同。
不同之处在于,未指定的边缘以 0 填充固定到边缘。

ZStackBlock {
  VStackBlock {
    ...
  }
  .padding(.horizontal, 10) // other edges work with 0 padding.
}

ZStackBlock

CleanShot 2021-06-25 at 04 50 27@2x CleanShot 2021-06-25 at 04 51 37@2x

在 Z 轴上堆叠视图(在中心对齐)

ZStackBlock {
  view1
  view2
  view3
}

将每个视图扩展到指定的边缘

ZStackBlock(alignment: .attach(.all)) {
  view1
  view2
  view3
}

指定每个视图的对齐方式

ZStackBlock {

  view1.viewBlock.alignSelf(.attach(.all))

  view2.viewBlock.alignSelf(.attach([.top, .bottom]))
  
  view3.viewBlock.alignSelf(.attach(.top))
}

动态更新布局

LayoutManager 支持它。
如果我们需要根据某些条件(例如取决于特征)更改布局,则此对象可以帮助实现。

动画

// TODO: #19

经典布局 API

结构化布局 API(DSL)不涵盖所有使用场景。
有时我们仍然需要一种描述复杂布局约束的方法。

MondrianLayout 也提供它,就像其他 AutoLayout 库一样。

独立激活约束

view.mondrian.layout
  .width(10)
  .top(.toSuperview)
  .right(.toSuperview)
  .leading(.toSuperview)
  .activate() // activate constraints and returns `ConstraintGroup`

批量布局**

// returns `ConstraintGroup`
Mondrian.layout {

  box1.mondrian.layout
    .top(.toSuperview)
    .left(.toSuperview)
    .right(.to(box2).left)
    .bottom(.toSuperview)

  box2.mondrian.layout
    .top(.toSuperview.top, .exact(10))
    .right(.toSuperview)
    .bottom(.toSuperview)
}

例子

水平附加

view.layout.horizontal(.toSuperview, .exact(10))

垂直附加

view.layout.vertical(.toSuperview, .exact(10))

边缘附加到另一个边缘

view.layout.edge(.toSuperview)
view.layout.edge(.to(myLayoutGuide))

安装

CocoaPods

pod "MondrianLayout"

SwiftPM

dependencies: [
    .package(url: "https://github.com/muukii/MondrianLayout.git", exact: "<VERSION>")
]

LICENSE

MondrianLayout 在 MIT 许可证下发布。