锚点 2.5.0

Anchors 2.5.0

测试已测试
Lang语言 SwiftSwift
许可证 NOASSERTION
发布最后发布2019年4月
SPM支持SPM

Khoa Pham维护。



Anchors 2.5.0

锚点

Version Carthage Compatible License Platform Swift

目录

故事

我喜欢在代码中构建视图,所以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起

你需要另一个自动布局框架吗?

你看到的所有自动布局框架都只是方便构建NSLayoutConstraint的方法,实际上这些都是你通常需要的。

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

大多数情况下,你需要的是NSLayoutAnchor。但是,如果你需要更多,锚点可以是你的选择。

示例

俄罗斯方块

你可以使用自动布局来构建俄罗斯方块。自动布局与仿射变换配合得很好。请参阅代码




activate(
  lineBlock.anchor.left.bottom
)

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

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

钢琴

这是使用applyfixed spacing制作钢琴的方法。请参阅代码

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是不行的,因为锚点会推断到superview

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

推断

你知道你主要想做什么。锚点也是如此🎉。它会尽力推断,所以不需要写“显而易见”的代码

大多数情况下,你想要将约束限制在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个分组,然后轮流切换哪个分组被激活

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

支持富约束生成器

Group 是由 ConstraintProducer 生成的 NSLayoutConstraint 集合

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

目前,有 AnchorBuilder 符合 ConstraintProducer,您可以通过符合 ConstraintProducer 来轻松扩展 锚点。例如

// 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 快速构建

锚点用于在 2 个视图之间创建 constraints。如果您想要一次为多个视图创建 constraints,可以使用多个锚点。有一些常见的任务,让 Builder 来帮您。以下方法在内部使用了 Builder

锚点提供了一套构建器来帮助您避免重复性任务,并快速构建 UI😎

应用

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

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

调试自动布局

支持多个屏幕尺寸

安装

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

pod 'Anchors'

Anchors 也可通过 Carthage 提供。安装只需在您的 Cartfile 中写入

github "onmyway133/Anchors"

Anchors 还可手动安装。只需要下载并将 Sources 文件夹拖放到您项目中。

作者

Khoa Pham,[email protected]

贡献

我们很乐意您为 Anchors 贡献,详细信息请查看 CONTRIBUTING 文件。

许可证

Anchors 在 MIT 许可证下可用。更多信息请参阅 LICENSE 文件。