EasyPeasy 1.10.0

EasyPeasy 1.10.0

测试已测试
语言语言 SwiftSwift
许可证 MIT
发布上次发布2021年1月
SPM支持 SPM

Carlos Vidal 维护。



EasyPeasy 1.10.0

EasyPeasy Logo

CI Status Version Carthage compatible Coverage

EasyPeasy 是一个让它在 Swift 框架中创建无头痛和永不结束的样板代码的自动布局约束的技术。除了基本功能外,EasyPeasy 解决了大多数约束冲突,并可以附加在约束条件闭包之前进行评估,这样您就可以根据平台、大小类、方向……或控制器状态安装一个自动布局约束,简单快捷!

在本快速浏览中,我们假设您已经知道不同自动布局 API 的优缺点,因此在这里不会进行比较,只需阅读并决定 EasyPeasy 是否适合您。

EasyPeasy 入门

下面的示例相当简单,但展示了使用 EasyPeasy 无需麻烦就能实现的结果。


First touch

特性

  • 兼容 iOS、tvOS 和 macOS。
  • 轻量级且易于使用的特定领域语言。
  • 解决自动布局冲突。
  • 快速轻松地更新约束。
  • 条件应用约束。
  • 支持 UILayoutGuideNSLayoutGuide

指南

目录

安装

Swift 兼容性

  • 要使用 Swift 2.2,请使用 EasyPeasy v.1.2.1 或更早版本的库。
  • 要使用 Swift 2.3,请使用 EasyPeasy v.1.3.1
  • 要使用 Swift 3,请使用 EasyPeasy v.1.4.2
  • 要使用 Swift 4,请使用 EasyPeasy v.1.8.0
  • 要使用 Swift 5,请使用 EasyPeasy v.1.9.0 及以上版本。(感谢 Bas van Kuijck)。

Cocoapods

EasyPeasy 通过 Cocoapods 提供。要安装它,只需将以下行添加到您的 Podfile 中

pod "EasyPeasy"

Carthage

EasyPeasy兼容Carthage。要将EasyPeasy添加为项目依赖,只需在Cartfile中添加以下行:

github "nakiostudio/EasyPeasy"

然后像平常一样运行carthage update

兼容性

EasyPeasy与iOS(8及以上)、tvOS(9及以上)和OS X(10.10及以上)兼容。此框架已在Xcode 7和Swift 2.0上进行了测试,但请不要犹豫,报告您可能发现的任何不同版本的问题。

使用说明

EasyPeasy是一组位置和尺寸属性,您可以将其应用到视图上。您可以通过Autolayout(与Auto Layout合作的UI类,视图子类、布局引导等)中可用的easy属性来管理这些属性。

例如,要将宽度设置为200px的视图,您会创建一个具有常量值200Width类属性,然后通过使用easy.layout(_:)方法将该属性应用到视图上。

myView.easy.layout(Width(200))

因为我们没有宽度的视图什么都不是,所以您可以一次性应用多个属性,如下所示:

myView.easy.layout(
  Width(200),
  Height(120)
)

在上面的示例中,已应用了两个属性,因此创建了两个约束:一个具有constant = 200的宽度约束和一个具有constant = 120的高度约束。

常量

不知道自己,刚刚创建了一个包含常量、乘数和NSLayoutConstraint关系的EasyPeasy Constant结构体。

关系

EasyPeasy提供了一种简单的方法来创建具有不同NSLayoutRelations的常量。

  • .Equal:它创建方式如我们之前的例子Width(200)所展示。
  • .GreaterThanOrEqual:它创建得像这样简单Width(>=200),意味着我们的视图的宽度大于或等于200px。
  • .LessThanOrEqual:它创建如下所示Width(<=200)

倍数器

有一个自定义运算符,可以简化创建 NSLayoutConstraint 倍数器的操作。您可以使用它如下 Width(*2),这意味着我们的视图宽度是 某个值 的两倍,我们稍后将介绍如何建立与这个 某个值 的关系。

此外,您可以将 倍数器Equal.GreaterThanOrEqualLessThanOrEqual 关系结合使用。例如,Width(>=10.0*0.5) 创建一个 NSLayoutConstraint,其 value = 10.0relation = .GreaterThanOrEqualmultiplier = 0.5,而 Width(==10.0*0.5) 创建一个 NSLayoutConstraint,其 value = 10.0relation = .Equalmultiplier = 0.5

属性

EasyPeasy 提供了与 NSLayoutConstraint 具有相同数量的 Attribute 类,此外还提供了一些我们称为 CompoundAttributes 的属性(我们将在稍后解释这些属性)。

尺寸属性

仅提供了两种尺寸属性 WidthHeight。您可以通过使用方法 func like(view: UIView) -> Self,在您的视图 DimensionAttribute 和其他视图之间创建一个 Auto Layout 关系。示例

contentLabel.easy.layout(Width().like(headerView))

这一行代码将创建一个约束,将其设置为 contentLabel 的宽度等于 headerView 的宽度。

位置属性

下表显示了可用的不同位置属性。因为它们的行为类似于 NSLayoutConstraint 属性,您可以在 Apple 文档 中找到它们的完整描述。

属性 属性 属性 属性
Left Right Top Bottom
Leading Trailing CenterX CenterY
LeftMargin RightMargin TopMargin BottomMargin
LeadingMargin TrailingMargin CenterXWithinMargins CenterYWithinMargins(页面内容垂直居中相对于边距)
FirstBaseline(基准线下方第一行) LastBaseline(基准线下方最后一行) -- --

除了DimensionAttributeslike:方法来建立自动布局关系外,你还可以使用类似的方法来处理PositionAttributes。这个方法是

func to(view: UIView, _ attribute: ReferenceAttribute? = nil) -> Self

以下示例将contentLabelheaderView下方10px的位置进行定位,且与headerView具有相同的左边距。

contentLabel.easy.layout(
  Top(10).to(headerView),
  Left().to(headerView, .Left)
)

CompoundAttributes

这些属性在内部会创建多个DimensionAttributesPositionAttributes。例如,Size属性将创建一个包含宽度与高度的NSLayoutConstraintsWidthHeight属性。

以下是可用的CompoundAttributes

  • Size:如前所述,此属性将向视图应用宽度和高度属性。它可以通过多种方式初始化,结果可能会因此有所不同。以下是一些示例
// Apply width = 0 and height = 0 constraints
view.easy.layout(Size())
// Apply width = referenceView.width and height = referenceView.height constraints
view.easy.layout(Size().like(referenceView))
// Apply width = 100 and height = 100 constraints
view.easy.layout(Size(100))
// Apply width = 200 and height = 100 constraints
view.easy.layout(Size(CGSize(width: 200, height: 100)))
  • Edges:此属性同时创建LeftRightTopBottom属性。示例
// Apply left = 0, right = 0, top = 0 and bottom = 0 constraints to its superview
view.easy.layout(Edges())
// Apply left = 10, right = 10, top = 10 and bottom = 10 constraints to its superview
view.easy.layout(Edges(10))
// Apply left = 10, right = 10, top = 5 and bottom = 5 constraints to its superview
view.easy.layout(Edges(UIEdgeInsets(top: 5, left: 10, bottom: 5, right: 10)))
  • Center:它创建CenterXCenterY属性。示例
// Apply centerX = 0 and centerY = 0 constraints to its superview
view.easy.layout(Center())
// Apply centerX = 10 and centerY = 10 constraints to its superview
view.easy.layout(Center(10))
// Apply centerX = 0 and centerY = 50 constraints to its superview
view.easy.layout(Center(CGPoint(x: 0, y: 50)))
  • Margins:此属性同时创建LeftMarginRightMarginTopMarginBottomMargin属性。示例
// Apply leftMargin = 0, rightMargin = 0, topMargin = 0 and bottomMargin = 0 constraints to its superview
view.easy.layout(Margins())
// Apply leftMargin = 10, rightMargin = 10, topMargin = 10 and bottomMargin = 10 constraints to its superview
view.easy.layout(Margins(10))
// Apply leftMargin = 10, rightMargin = 10, topMargin = 5 and bottomMargin = 5 constraints to its superview
view.easy.layout(Margins(UIEdgeInsets(top: 5, left: 10, bottom: 5, right: 10)))
  • CenterWithinMargins:它创建CenterXWithinMarginsCenterYWithinMargins属性。示例
// Apply centerXWithinMargins = 0 and centerYWithinMargins = 0 constraints to its superview
view.easy.layout(CenterWithinMargins())
// Apply centerXWithinMargins = 10 and centerYWithinMargins = 10 constraints to its superview
view.easy.layout(CenterWithinMargins(10))
// Apply centerXWithinMargins = 0 and centerYWithinMargins = 50 constraints to its superview
view.easy.layout(CenterWithinMargins(CGPoint(x: 0, y: 50)))

Priorities

Priority枚举与UILayoutPriority执行相同的功能,并分为五个情况

  • low:它创建一个具有Float1的较低的自动布局优先级。

  • medium:它创建一个具有Float500的中等优先级。

  • high:它创建一个具有Float750的高优先级。

  • required:它创建一个具有Float1000的必需优先级。

  • custom:它指定开发者在相关值value中定义的自动布局优先级。示例:.custom(value: 650.0)

为了将这些优先级应用于任何Attribute,必须使用方法.with(priority: Priority)。以下示例将UILayoutPriority的值500应用于应用于viewTop Attribute

view.easy.layout(Top(>=50).with(.medium))

您还可以将一个Priority应用于一个Attributes数组(此操作将覆盖应用到Attribute的先前优先级)。

view.easy.layout([
  Width(200),
  Height(200)
].with(.medium))

条件

EasyPeasy的一个独特之处在于使用条件或闭包,以评估是否应该对视图应用约束。

方法when(condition: Condition)条件闭包设置到属性

有很多用例,下面的示例展示了如何根据自定义变量应用不同的约束:

var isCenterAligned = true
...
view.easy.layout(
  Top(10),
  Bottom(10),
  Width(250),
  Left(10).when { !isCenterAligned },
  CenterX(0).when { isCenterAligned }
)

条件重新评估

这些条件闭包可以在视图的生命周期内重新评估,为此您只需调用便利方法easy.reload()

view.easy.reload()

请注意,这些条件闭包存储在属性中,因此您需要捕获闭包内访问的变量。例如:

descriptionLabel.easy.layout(
  Height(100).when { [weak self] in
    return self?.expandDescriptionLabel ?? false
  }
)

您还可以将一个条件应用于一个属性数组(此操作将覆盖之前应用于属性的限制)。

view.easy.layout([
  Width(200),
  Height(240)
].when { isFirstItem })

view.easy.layout([
  Width(120),
  Height(140)
].when { !isFirstItem })

ContextualConditions

这仅适用于iOS的特性是条件闭包的一个变种,它不接受参数并返回一个布尔值。取而代之的是,传递一个上下文结构体作为参数,提供一些基于即将应用的UIViewUITraitCollection额外信息。

上下文结构体上的可用属性有:

  • isPad:如果当前设备是iPad则为true。
  • isPhone:如果当前设备是iPhone则为true。
  • isHorizontalVerticalCompact:如果水平和垂直大小类都是.Compact则为true。
  • isHorizontalCompact:如果水平大小类是.Compact则为true。
  • isVerticalCompact:如果垂直大小类是.Compact则为true。
  • isHorizontalVerticalRegular:如果水平和垂直大小类都是.Regular则为true。
  • isHorizontalRegular:如果水平大小类是.Regular则为true。
  • isVerticalRegular:如果垂直大小类是.Regular则为true。

以下是应用于数组属性的上下文条件示例

view.easy.layout([
  Size(250),
  Center(0)
].when { $0.isHorizontalRegular })

view.easy.layout([
  Top(0),
  Left(0),
  Right(0),
  Height(250)
].when { $0.isHorizontalCompact })
上下文条件重新评估

如我们之前看到的,您可以通过调用easy.reload()便捷方法来重新评估Condition闭包。这也适用于ContextualConditions,因此如果您希望在您的视图UITraitCollection发生更改时更新约束,则需要在traitCollectionDidChange(_:)中调用easy.reload()方法。

另外,EasyPeasy可以自动完成这一步。默认情况下,它是禁用的,因为这需要方法交换;要启用它,只需在-D EASY_RELOAD编译器标志的框架中进行编译。

UILayoutGuides

从版本v.0.2.3(以及iOS 9项目及以上)开始,EasyPeasy集成了UILayoutGuides支持。

应用约束

将约束应用于UILayoutGuide与我们在前几节中讨论的一样简单,只需使用easy.layout(_:)方法应用您想要的EasyPeasy属性。

func viewDidLoad() {
  super.viewDidLoad()

  let layoutGuide = UILayoutGuide()
  self.view.addLayoutGuide(layoutGuide)

  layoutGuide.easy.layout(
    Top(10),
    Left(10),
    Right(10),
    Height(100).when { Device() == .iPad },
    Height(60).when { Device() == .iPhone }
  )
}

如您所见,EasyPeasyUIViews提供的所有不同属性和好东西也适用于UILayoutGuides

连接 UILayoutGuides 和 UIViews

属性部分所述,您可以使用to(_:_)like(_:_)方法在UIView属性与其他UIViews属性之间创建约束关系。现在您可以利用这些方法在您的UIView属性和UILayoutGuide之间创建关系。

let layoutGuide = UILayoutGuide()
let separatorView: UIView
let label: UILabel

func setupLabel() {
  self.label.easy.layout(
    Top(10).to(self.layoutGuide),
    CenterX(0),
    Size(60)
  )

  self.separatorView.easy.layout(
    Width(0).like(self.layoutGuide),
    Height(2),
    Top(10).to(self.label),
    CenterX(0).to(self.label)
  )
}

最后一点

在这一节中,我们将解释如何使用easy.layout(_:)方法,在将属性应用于UIView之后与之交互,这一点虽然非最后但同样重要。

更新约束

在介绍部分我们简要提到了EasyPeasy可以解决大多数约束冲突,这是真的。通常,为了更新一个约束或其常量,你必须保留对NLayoutConstraint的引用,并在需要时更新常量。使用EasyPeasy,你只需将另一个Attribute应用到同一个或不同类型的UIView上。以下示例中有两个方法,一个是设置约束的方法viewDidLoad(),另一个是我们要更新headerViewTop属性的方法。

func viewDidLoad() {
  super.viewDidLoad()

  headerView.easy.layout(
    Top(0),
    Left(0),
    Right(0),
    Height(60)
  )
}

func didTapButton(sender: UIButton?) {
  headerView.easy.layout(Top(100))
}

就是这样!我们已经更新了Top约束,无需关心保留引用或安装/卸载新的约束。

但是,有些情况下EasyPeasy无法防止冲突(至少目前是这样)。这是当多个约束无法满足时,也就是说,如果一个LeftRight约束被应用,同时也应用了一个Width约束(它们都有相同的优先级)。但是EasyPeasy足够聪明,可以防止冲突,比如说当你用CenterX属性替换LeftRight属性时。

清除约束

EasyPeasy提供了一种扩展UIView的方法,可以清除使用框架安装在一个UIView上的所有约束。此方法为func easy.clear()

view.easy.clear()

动画约束

使用 EasyPeasy 动画约束非常简单直观,只需在一个动画块中将一个或多个 Attributes 应用到您的视图中,然后就可以出发了,无需担心约束冲突。示例

UIView.animateWithDuration(0.3) {
  view.easy.layout(Top(10))
  view.layoutIfNeeded()
}

示例项目

别忘了克隆存储库并运行 iOS 和 OS X 示例项目,以查看 EasyPeasy 的实际应用。

Demo iOS Demo macOS

注意:演示应用程序中的消息不是真实的,那些 Twitter 账户的出现,仅仅是对一些优秀开发者的致敬:)

EasyPeasy 演示场

或者,您可以克隆 Playground 项目来使用 EasyPeasy,项目地址为 这里

Playground

自动生成的文档

EasyPeasy 是一个文档良好的框架,因此所有已记录的类和方法都可以在 Cocoadocs 中找到。

作者

Carlos Vidal - @nakiostudio

许可证

EasyPeasy 根据MIT许可证提供。有关更多信息,请参阅 LICENSE 文件。