EasyAnchor 3.0.0

EasyAnchor 3.0.0

Khoa Pham维护。



EasyAnchor

Version Carthage Compatible License Platform Swift

目录

故事

我喜欢在代码中构建视图,所以 Auto Layout 是我的首选。语法多年来已经有了改进,但我总是希望用最少的努力来完成它。更多的重复代码会让你倾向于复制粘贴,从而产生更多的错误。

如何使 iOS 中的 Auto Layout 更方便

Auto Layout API 历史

多年来如何引入新的 API,以便您知道设置您的部署目标

  • NSLayoutConstraint 自 iOS 6,macOS 10.7
  • isActive 自 iOS 8,macOS 10.10
  • NSLayoutAnchorUI|NSLayoutGuide 自 iOS 9,macOS 10.11

你需要另一个Auto Layout框架吗?

你所看到的所有Auto Layout框架,其实只是构建NSLayoutConstraint的一种方便方法,实际上这些都是你通常需要的东西。

  • 调用addSubview使得视图进入层级结构。
  • 设置translatesAutoresizingMaskIntoConstraints = false
  • isActive = true设置为启用约束。

大多数时候,你需要的是NSLayoutAnchor。但如果需要更多,EasyAnchor可以成为你的选择。

示例

俄罗斯方块

当然,你可以使用Auto Layout来制作俄罗斯方块。Auto Layout与仿射变换相配合也很好。查看代码




activate(
  lineBlock.anchor.left.bottom
)

// later
activate(
  firstSquareBlock.anchor.left.equal.to(lineBlock.anchor.right),
  firstSquareBlock.anchor.bottom
)

// later
activate(
  secondSquareBlock.anchor.right.bottom
)

钢琴

这是如何使用apply固定间距制作钢琴的方法。查看代码

activate(
  c.anchor.left,
  b.anchor.right,
  c.anchor.top.bottom,
  c.anchor.top.bottom.width.apply(to: [d, e, f, g, a, b]),
  c.anchor.fixedSpacingHorizontally(togetherWith: [d, e, f, g, a, b], spacing: 0)
)

activate(
  cd.anchor.top,
  cd.anchor.size.equal.to(c.anchor.size).multiplier(2/3),
  cd.anchor.top.size.apply(to: [de, fg, ga, ab]),
  cd.anchor.centerX.equal.to(c.anchor.right),
  de.anchor.centerX.equal.to(d.anchor.right),
  fg.anchor.centerX.equal.to(f.anchor.right),
  ga.anchor.centerX.equal.to(g.anchor.right),
  ab.anchor.centerX.equal.to(a.anchor.right)
)

更多

更多示例可以在示例中找到。

特点

  • 流畅的构建器语法
  • 基于协议的易于定制
  • 支持iOS, macOS
  • 支持 LayoutGuide
  • 更新和重置约束
  • 找到现有的约束
  • 调试约束
  • 可视化约束

带锚的基本

访问锚

优先使用组合而非扩展,这是如何访问 anchor。支持 ViewLayoutGuideLayoutSupport

let view = UIView()
view.anchor.left.right

let guide = UILayoutGuide()
guide.anchor.width.height

topLayoutGuide.anchor.bottom
bottomLayoutGuide.anchor.top

激活约束

使用 activate 函数,它接受可变数量的参数。这很重要,无论你如何创建约束,它们在未 activate 之前都不会有任何作用 ❗❗❗❗❗

activate(
  a.anchor.top.left,
  b.anchor.top.right,
  c.anchor.bottom.left,
  d.anchor.bottom.right
)

属性

支持所有能想到的属性

anchor.top.left.bottom.right
  .leading.trailing
  .topMargin.bottomMargin.leftMargin.rightMargin
  .centerX.centerY
  .centerXWithinMargins.centerXWithinMargins
  .lastBaseline.firstBaseline
  .width.height

关系

a.anchor.top.equal.to(b.bottom)
a.anchor.width.greaterThanOrEqual.to(b.anchor.height)
a.anchor.width.lessThanOrEqual.to(b.anchor)

配置

这是如何应用 constantmultiplierpriorityidentifier

a.anchor.top.equal.to(b.anchor.bottom)
  .constant(10).multiplier(1.5).priority(999)
  .id("verticalSpacingBetweenA-B")

参考文献

获取约束引用,以便日后修改。在ref闭包中,我们可以访问所有创建的约束。

var constraint: NSLayoutConstraint?
activate(
  view.anchor.center.constant(10).ref({ constraint = $0.first })
)

便捷属性

使用组合多个内部属性的便捷属性。

a.anchor.center  // centerX, centerY
a.anchor.size  // width, height
a.anchor.edges  // top, right, bottom, left

便捷方法

填充

a.anchor.edges.insets(EdgeInsets(top: 1, left: 2, bottom: 3, right: 4))  // top+1, left+2, bottom+3, right+4
a.anchor.edges.insets(5)  // top+5, left+5, bottom-5, right-5

内边距

a.anchor.paddingHorizontally(20) // leading+20, trailing-20
b.anchor.paddingVertically(20)	// top+20, bottom-20

大小

大小到另一个锚点

a.anchor.height.equal.to(b.anchor.width)
c.anchor.size.equal.to(d.anchor)

大小到常量

a.anchor.height.equal.to(20)  // height==20
b.anchor.size.equal.to(20)  // width==20, height==20

不能直接使用constant,因为EasyAnchor会推断到代理视图。

c.anchor.width.constant(20)  // width==superview.width+20

比例

a.anchor.height.equal.to(a.anchor.width)  // height==width

或者,您可以直接使用ratio

a.anchor.width.constant(10)
a.anchor.height.ratio(2) // height==width*2
a.anchor.height.constant(10)
a.anchor.width.ratio(2) // width==height*2

推断

您知道您基本上想要做什么。EasyAnchor也知道。🎉。它会尽可能进行推断,因此您无需编写“明显”的代码

大多数情况下,您希望限制到superview

a.anchor.top.left // a.top == a.superview.top, a.left == a.superview.left

大多数情况下,您希望限制到相同的属性

a.anchor.top.bottom.width.equal.to(b.anchor) // a.top == b.top, a.bottom == b.bottom, a.width == b.width

查找现有约束

您不需要声明变量来存储constraints,可以直接检索

a.anchor.find(.height)?.constant = 100

// later
b.anchor.find(.height)?.constant = 100

// later
c.anchor.find(.height)?.constant = 100

动画

动画很简单。您只需更改constraintisActiveconstant属性,然后在动画块中调用layoutIfNeeded。您可以使用UIView.animateUIViewPropertyAnimator

// Change constraint
a.anchor.find(.height)?.constant = 100
loginButtonHeightConstraint.isActive = false

let animator = UIViewPropertyAnimator(duration: 1, dampingRatio: 0.7)
animator.addAnimations { [weak self] in
  self?.view.layoutIfNeeded()
}

animator.startAnimation(afterDelay: 1)

使用Group更新约束

使用activate只是一个方便的方式来创建group,然后设置GroupisActive属性。如果您只想将一组约束分组,然后在以后设置isActive,请使用group函数

在本例中,我们有4个group,然后轮流切换哪个group被激活

func toggle(_ group: Group) {
  [g1, g2, g3, g4].forEach { g in
    guard let g = g else {
      return
    }

    g.isActive = (g == group)
  }
}

g1 = group(a.anchor.top.left)
g2 = group(a.anchor.top.right)
g3 = group(a.anchor.bottom.right)
g4 = group(a.anchor.bottom.left)

g1.isActive = true

animator = Animator(view: self, animations: [
  {
    self.toggle(self.g2)
  },
  {
    self.toggle(self.g3)
  },
  {
    self.toggle(self.g4)
  },
  {
    self.toggle(self.g1)
  }
  ])

animator.start()

可扩展与ConstraintProducer

Group是一组由ConstraintProducer生成的

public protocol ConstraintProducer {
  func constraints() -> [NSLayoutConstraint]
}

目前,有AnchorBuilder符合ConstraintProducer的要求,你可以通过满足ConstraintProducer来轻松扩展EasyAnchor。例如

// This accepts a list of views, and build constraints
class MyAwesomeLayout: ConstraintProducer {
  init(views: [UIView]) {
    // Your code goes here
  }

  func constraints() -> [NSLayoutConstraint] {
    // Your code goes here
    return []
  }
}

let awesomeLayout = MyAwesomeLayout(views: [view1, view2])
activate(awesomeLayout)

使用Builder快速构建

好的,Anchor是用来在两个视图之间创建约束。如果你想要一次为多个视图创建约束,你可以使用多个Anchor。有一些经常要做的工作,让Builder来帮助你。下面的方法在内部使用了Builder

EasyAnchor提供了一系列的Builder来帮助您避免重复性任务,并快速构建用户界面😎

应用

将相同的锚应用到其他视图

a.anchor.left.height.apply(to: [b, c]),

分页

水平构建分页的scrollView

addSubview(scrollView)
[a, b, c, d].forEach {
  scrollView.addSubview($0)
}

activate(
  scrollView.anchor.edges.insets(8),
  a.anchor.pagingHorizontally(togetherWith: [b, c, d], in: scrollView)
)

固定间距

添加固定间距。视图将调整大小

activate(
  container.anchor.edges.insets(8),
  a.anchor.left.top.bottom,
  c.anchor.right,
  a.anchor.top.bottom.width.apply(to: [b, c]),
  a.anchor.fixedSpacingHorizontally(togetherWith: [b, c], spacing: 50)
)

动态间距

使用LayoutGuide添加动态间距。间距将调整大小

activate(
  container.anchor.edges.insets(8),
  a.anchor.size.equal.to(30),
  b.anchor.size.equal.to(30),
  c.anchor.size.equal.to(30),
  a.anchor.left.centerY,
  a.anchor.centerY.apply(to: [b, c]),
  c.anchor.right,
  a.anchor.dynamicSpacingHorizontally(togetherWith: [b, c])
)

调试自动布局

支持多种屏幕尺寸

安装

EasyAnchor 可通过 CocoaPods 文件安装。要安装,只需将以下行添加到 Podfile 中

pod 'EasyAnchor'

EasyAnchor 同样可通过 Carthage 安装。只需在 Cartfile 中写入

github "onmyway133/EasyAnchor"

EasyAnchor 也可以手动安装。只需下载并将 Sources 文件夹拖到您的项目文件中。

作者

Pham Khao, [email protected]

贡献

我们非常欢迎您为 EasyAnchor 贡献,有关更多信息,请查看 贡献指南 文件。

许可证

EasyAnchor 在 MIT 许可下可用。有关更多信息,请参阅 许可证文件