Git Product home page Git Product logo

easyanchor's Introduction

EasyAnchor

❤️ Support my apps https://indiegoodies.com/ ❤️

❤️❤️😇😍🤘❤️❤️

Version Carthage Compatible License Platform Swift

Table of contents

Story

I like to build view in code, so Auto Layout is my definite choice. The syntax has improved over the years, but I always want to do it with minimum effort. More repetitive code makes you tend to do copy paste and produce more bugs.

Read more How to make Auto Layout more convenient in iOS

Auto Layout APIs history

How new APIs were introduced over the years, so you know to set your deployment target

  • NSLayoutConstraint since iOS 6, macOS 10.7
  • isActive since iOS 8, macOS 10.10
  • NSLayoutAnchor, UI|NSLayoutGuide since iOS 9, macOS 10.11

Do you need another Auto Layout framework?

All the Auto Layout frameworks you see are just convenient ways to build NSLayoutConstraint, in fact these are what you normally need

  • Call addSubview so that view is in hierarchy
  • Set translatesAutoresizingMaskIntoConstraints = false
  • Set isActive = true to enable constraints

Most of the time, NSLayoutAnchor is what you need. But if you need more, EasyAnchor can be your choice.

Examples

Tetris

Well, you can use Auto Layout to make Tetris. Auto Layout plays well with affine transform too. See code




activate(
  lineBlock.anchor.left.bottom
)

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

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

Piano

This is how to make a piano using apply and fixed spacing. See code

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

More

More example can be found in Example

Features

  • Fluent builder syntax
  • Easy to customize with protocol based
  • Support iOS, macOS
  • Support LayoutGuide
  • Update and reset constraints
  • Find existing constraints
  • Debug constraints
  • Visualize constraints

Basic with Anchor

Access Anchor

Prefer composition over extension, this is how you access anchor. Support View, LayoutGuide, LayoutSupport

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

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

topLayoutGuide.anchor.bottom
bottomLayoutGuide.anchor.top

Activate constraints

Use activate which accepts variadic parameters. This is important, no matter how you create constraints, they don't have any effect until you activate it ❗❗❗❗❗

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

Attributes

Supports all attributes you can think of

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

Relation

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

Configuration

This is how to apply constant, multiplier, priority, identifier

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

Reference

Get references to constraints to modify it later on. In the ref closure, we get access to all the created constraints

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

Convenient attributes

Use convenient attributes which combine multiple inner attributes

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

Convenient methods

Insets

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

Padding

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

Size

Size to another anchor

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

Size to a constant

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

You can't just use constant because EasyAnchor will infer to the superview

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

Ratio

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

Alternatively, you can just use 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

Inference

You know what you mostly want to do. So does EasyAnchor 🎉. It does its best to infer so don't have to write "obvious" code

Most of the time, you want to constraint to the superview

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

Most of the time, you want to constraint to the same attributes

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

Find existing constraints

You don't need to declare variables to store constraints, you can just retrieve them back

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

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

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

Animation

Animation is simple. You just change your constraint 's isActive or constant properties, then layoutIfNeeded in an animation block. You can use UIView.animate or UIViewPropertyAnimator

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

Update constraints with Group

activate is just a convenient way to produce group, then set isActive on the Group. If you just want to group a set of constraints, then set isActive later on, use function group

In this example, we have 4 groups, then take turn to toggle which group gets activated

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

Extensible with ConstraintProducer

Group is a set of NSLayoutConstraint, which are produced by ConstraintProducer

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

For now, there is Anchor and Builder which conforms to ConstraintProducer, you can extend EasyAnchor easily by conform to ConstraintProducer. For example

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

Build quickly with Builder

Well, Anchor is for making constraints between 2 views. If you want to make constraints for multiple views at once, you can use multiple Anchor. There are some tasks that you do often, let Builder help you. These method below use Builder under the hood

EasyAnchor has a set of builders to help you avoid repetitive tasks and build UIs quickly 😎

Apply

Apply the same anchor to other views

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

Paging

Build a paging scrollView horizontally

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

Fixed spacing

Add fixed spacing. The views will resize

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

Dynamic spacing

Add dynamic spacing using LayoutGuide. The spacing will resize

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

Debug Auto Layout

Support multiple screen sizes

Installation

EasyAnchor is available through CocoaPods. To install it, simply add the following line to your Podfile:

pod 'EasyAnchor'

EasyAnchor is also available through Carthage. To install just write into your Cartfile:

github "onmyway133/EasyAnchor"

EasyAnchor can also be installed manually. Just download and drop Sources folders in your project.

Author

Khoa Pham, [email protected]

Contributing

We would love you to contribute to EasyAnchor, check the CONTRIBUTING file for more info.

License

EasyAnchor is available under the MIT license. See the LICENSE file for more info.

easyanchor's People

Contributors

onmyway133 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

easyanchor's Issues

Framework is not compiling

There is an error "Argument labels '(rawValue:)' do not match any available overloads" on 17 line in Anchor+Constraints.swift file.
It works if i modify file manually, but it is not the case, hope you will fix it.
It is quite sad that there is no contributors here.

I have one Question..

I am a big fan of programmatically UI, and so far I am doing everything manually using apple's own API, but it takes a lot of time and I was looking around and found several options. I find your EasyAnchor really clean and short.

So, My Question is simple, how long you are playing to support this project? I would love to use EasyAnchor on my future project.

Make better algorithm to find constraints

When using the find method, EasyAnchor should lookup for constraints that are equal to the properties inside Anchor.

For example, I have set the anchor edges; width equal to height. To find the top constraint, I count use something like that anchor.top.find. In a scenario that there are a lot of constraints related to top, I could specify more attributes to find it like anchor.top.priority(750).constant(10)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.