Skip to content

Commit

Permalink
Merge pull request #34 from nakiostudio/group-deactivations
Browse files Browse the repository at this point in the history
Group deactivation of NSLayoutConstraints
  • Loading branch information
nakiostudio committed Aug 14, 2016
2 parents 4abf8c0 + 18c6759 commit 9668b7b
Show file tree
Hide file tree
Showing 7 changed files with 326 additions and 70 deletions.
1 change: 0 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ branches:
- develop
before_install:
- bundle install
- instruments -s
script:
- set -o pipefail
- fastlane travis
Expand Down
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## v.1.1.1

* Grouped deactivation of `NSLayoutConstraints`. Before, the deactivation of `NSLayoutConstraints` was taking place upon finding
a conflict, whereas now a single `NSLayoutConstraint.deactivateConstraints(_:)` takes place per `<-`, `easy_reload` or `easy_clear`
operation.

## v.1.1.0

* Now it's possible to combine `multipliers` with `Equal`, `GreaterThatOrEqual`
Expand Down
2 changes: 1 addition & 1 deletion EasyPeasy.podspec
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "EasyPeasy"
s.version = "1.1.0"
s.version = "1.1.1"
s.summary = "EasyPeasy is a Swift framework that eases the creation of
Autolayout constraints programmatically"
s.description = <<-DESC
Expand Down
52 changes: 35 additions & 17 deletions EasyPeasy/Item.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@ import AppKit

internal var easy_attributesReference: Int = 0

/**
Typealias of a tuple grouping an array of `NSLayoutConstraints`
to activate and another array of `NSLayoutConstraints` to
deactivate
*/
internal typealias ActivationGroup = ([NSLayoutConstraint], [NSLayoutConstraint])

/**
Protocol enclosing the objects a constraint will apply to
*/
Expand All @@ -38,23 +45,32 @@ public extension Item {
closures will be evaluated again
*/
public func easy_reload() {
var layoutConstraints: [NSLayoutConstraint] = []
var activateConstraints: [NSLayoutConstraint] = []
var deactivateConstraints: [NSLayoutConstraint] = []
for node in self.nodes.values {
layoutConstraints.appendContentsOf(node.reload())
let activationGroup = node.reload()
activateConstraints.appendContentsOf(activationGroup.0)
deactivateConstraints.appendContentsOf(activationGroup.1)
}
// Activate the resulting `NSLayoutConstraints`
NSLayoutConstraint.activateConstraints(layoutConstraints)

// Activate/deactivate the resulting `NSLayoutConstraints`
NSLayoutConstraint.deactivateConstraints(deactivateConstraints)
NSLayoutConstraint.activateConstraints(activateConstraints)
}

/**
Clears all the constraints applied with EasyPeasy to the
current `UIView`
*/
public func easy_clear() {
var deactivateConstraints: [NSLayoutConstraint] = []
for node in self.nodes.values {
node.clear()
deactivateConstraints.appendContentsOf(node.clear())
}
self.nodes = [:]

// Deactivate the resulting `NSLayoutConstraints`
NSLayoutConstraint.deactivateConstraints(deactivateConstraints)
}

}
Expand Down Expand Up @@ -91,26 +107,30 @@ internal extension Item {
*/
internal func apply(attributes attributes: [Attribute]) -> [NSLayoutConstraint] {
var layoutConstraints: [NSLayoutConstraint] = []
var activateConstraints: [NSLayoutConstraint] = []
var deactivateConstraints: [NSLayoutConstraint] = []

for attribute in attributes {
//
if let compoundAttribute = attribute as? CompoundAttribute {
layoutConstraints.appendContentsOf(self.apply(attributes: compoundAttribute.attributes))
continue
}

//
let createdConstraints = self.apply(attribute: attribute)
layoutConstraints.appendContentsOf(createdConstraints)
if let activationGroup = self.apply(attribute: attribute) {
layoutConstraints.appendContentsOf(activationGroup.0)
activateConstraints.appendContentsOf(activationGroup.0)
deactivateConstraints.appendContentsOf(activationGroup.1)
}
}

// Activate the `NSLayoutConstraints` returned by the different `Nodes`
NSLayoutConstraint.activateConstraints(layoutConstraints)
// Activate/deactivate the `NSLayoutConstraints` returned by the different `Nodes`
NSLayoutConstraint.deactivateConstraints(deactivateConstraints)
NSLayoutConstraint.activateConstraints(activateConstraints)

return layoutConstraints
}

internal func apply(attribute attribute: Attribute) -> [NSLayoutConstraint] {
internal func apply(attribute attribute: Attribute) -> ActivationGroup? {
// Creates the `NSLayoutConstraint` of the `Attribute` holding
// a reference to it from the `Attribute` objects
attribute.createConstraints(for: self)
Expand All @@ -119,14 +139,12 @@ internal extension Item {
// in case it doesn't exist
let node = self.nodes[attribute.signature] ?? Node()

// Add the `Attribute` to the node and appends the `NSLayoutConstraints`
// that have to be activated
let createdConstraints = node.add(attribute: attribute)

// Set node
self.nodes[attribute.signature] = node

return createdConstraints
// Add the `Attribute` to the node and return the `NSLayoutConstraints`
// to be activated/deactivated
return node.add(attribute: attribute)
}

}
92 changes: 56 additions & 36 deletions EasyPeasy/Node.swift
Original file line number Diff line number Diff line change
Expand Up @@ -62,57 +62,66 @@ internal class Node {
`Node` and its associated `NSLayoutConstraint` returned in
order to be activated by the `Item` owning the `Node`.
- parameter attribute: `Attribute` to be added to the `Node`
- returns: `NSLayoutConstraints` to be activated by the
`Item` owning the current `Node`
- returns an `ActivationGroup` gathering `NSLayoutConstraints`
to be activated/deactivated by the `Item` owning the current
`Node`
*/
func add(attribute attribute: Attribute) -> [NSLayoutConstraint] {
func add(attribute attribute: Attribute) -> ActivationGroup? {
guard attribute.shouldInstall() else {
self.inactiveAttributes.append(attribute)
return []
return nil
}

var deactivate: [NSLayoutConstraint]?

// Checks whether the `Attribute` is conflicting with any of
// the existing `Subnodes`. If so deactivates the conflicting
// `Subnodes`
let nodeAttribute = attribute.createAttribute.subnode
switch nodeAttribute {
case .Left:
if self.left === attribute { return [] }
self.deactivate(attributes: [self.left, self.center].flatMap { $0 })
if self.left === attribute { return nil }
deactivate = self.deactivate(attributes: [self.left, self.center].flatMap { $0 })
self.left = attribute
case .Right:
if self.right === attribute { return [] }
self.deactivate(attributes: [self.right, self.center].flatMap { $0 })
if self.right === attribute { return nil }
deactivate = self.deactivate(attributes: [self.right, self.center].flatMap { $0 })
self.right = attribute
case .Center:
if self.center === attribute { return [] }
self.deactivate(attributes: [self.center, self.left, self.right].flatMap { $0 })
if self.center === attribute { return nil }
deactivate = self.deactivate(attributes: [self.center, self.left, self.right].flatMap { $0 })
self.center = attribute
case .Dimension:
if self.dimension === attribute { return [] }
if self.dimension === attribute { return nil }
if let previousDimension = self.dimension {
self.deactivate(attributes: [previousDimension])
deactivate = self.deactivate(attributes: [previousDimension])
}
self.dimension = attribute
}

// Returns the associated `NSLayoutConstraint` to be activated
// by the `Item` owning the `Node`
if let layoutConstraint = attribute.layoutConstraint {
return [layoutConstraint]
return ([layoutConstraint], deactivate ?? [])
}

// Return constraints to deactivate
if let deactivateConstraints = deactivate {
return ([], deactivateConstraints)
}

return []
return nil
}

/**
Deactivates the `NSLayoutConstraints` for the `Attributes`
given. Also nullifies the `Subnodes` for those `Attributes`
Returns the `NSLayoutConstraints` to deactivate for the `Attributes`
given. Also nullifies the `Subnodes` holding those `Attributes`
- parameter attributes: `Attributes` to be deactivated
- returns an array of `NSLayoutConstraints` to be deactivated
*/
func deactivate(attributes attributes: [Attribute]) {
func deactivate(attributes attributes: [Attribute]) -> [NSLayoutConstraint] {
guard attributes.count > 0 else {
return
return []
}

var layoutConstraints: [NSLayoutConstraint] = []
Expand All @@ -131,24 +140,27 @@ internal class Node {
}
}

// Deactivate `NSLayoutContraints`
NSLayoutConstraint.deactivateConstraints(layoutConstraints)
// Return `NSLayoutContraints` to deactivate
return layoutConstraints
}

/**
Re-evaluates every `Condition` closure within the active and
inactive `Attributes`, in case an active `Attribute` has
become inactive deactivates it and the `NSLayoutConstraints` of
those that have changed to active are passed to the `Item` owner
of the `Node` to active them along with other `Nodes` active
`NSLayoutConstraints`
- returns the `NSLayoutConstraints` to be activated
Re-evaluates every `Condition` closure within the active and inactive
`Attributes`, in case an active `Attribute` has become inactive
returns its associated `NSLayoutConstraints` along with those that
have changed to active in order to be activated or deactivated by the
`Item` owning the `Node`
- returns an `ActivationGroup` gathering the `NSLayoutConstraints`
to be activated and deactivated
*/
func reload() -> [NSLayoutConstraint] {
func reload() -> ActivationGroup {
var activateConstraints: [NSLayoutConstraint] = []
var deactivateConstraints: [NSLayoutConstraint] = []

// Deactivate `Attributes` which condition changed to false
// Get the `Attributes` its condition changed to false in order to
// deactivate the associated `NSLayoutConstraint`
let deactivatedAttributes = self.activeAttributes.filter { $0.shouldInstall() == false }
self.deactivate(attributes: deactivatedAttributes)
deactivateConstraints.appendContentsOf(self.deactivate(attributes: deactivatedAttributes))

// Gather all the existing `Attributes` that need to be added
// again to the `Node`
Expand All @@ -161,18 +173,26 @@ internal class Node {

// Re-add `Attributes` to the `Node` in order to solve conflicts
// and re-evaluate `Conditions`
let layoutAttributes = activeAttributes.flatMap { self.add(attribute: $0) }
activeAttributes.forEach { attribute in
if let activationGroup = self.add(attribute: attribute) {
activateConstraints.appendContentsOf(activationGroup.0)
deactivateConstraints.appendContentsOf(activationGroup.1)
}
}

return layoutAttributes
return (activateConstraints, deactivateConstraints)
}

/**
Deactives all the active `Attributes` within the node and
clears all the persisted ones
Returns all the active `NSLayoutConstraints` within the node and
clears all the persisted `Attributes`
- returns an array of `NSLayoutConstraints` to deactivate
*/
func clear() {
self.deactivate(attributes: self.activeAttributes)
func clear() -> [NSLayoutConstraint] {
let deactivateConstraints = self.deactivate(attributes: self.activeAttributes)
self.inactiveAttributes = []

return deactivateConstraints
}

}
Expand Down

0 comments on commit 9668b7b

Please sign in to comment.