diff --git a/README.md b/README.md index 2f43f17..f1a83ad 100644 --- a/README.md +++ b/README.md @@ -59,7 +59,7 @@ platform :ios, '10.0' use_frameworks! target '' do - pod 'SnapKit', '~> 3.1.2' + pod 'SnapKit', '~> 3.2.0' end ``` @@ -83,7 +83,7 @@ $ brew install carthage To integrate SnapKit into your Xcode project using Carthage, specify it in your `Cartfile`: ```ogdl -github "SnapKit/SnapKit" ~> 3.1.2 +github "SnapKit/SnapKit" ~> 3.2.0 ``` Run `carthage update` to build the framework and drag the built `SnapKit.framework` into your Xcode project. diff --git a/SnapKit.podspec b/SnapKit.podspec index 7a6a06b..f392991 100644 --- a/SnapKit.podspec +++ b/SnapKit.podspec @@ -1,12 +1,12 @@ Pod::Spec.new do |s| s.name = 'SnapKit' - s.version = '3.1.2' + s.version = '3.2.0' s.license = 'MIT' s.summary = 'Harness the power of auto layout with a simplified, chainable, and compile time safe syntax.' s.homepage = 'https://github.com/SnapKit/SnapKit' s.authors = { 'Robert Payne' => 'robertpayne@me.com' } s.social_media_url = 'http://twitter.com/robertjpayne' - s.source = { :git => 'https://github.com/SnapKit/SnapKit.git', :tag => '3.1.2' } + s.source = { :git => 'https://github.com/SnapKit/SnapKit.git', :tag => '3.2.0' } s.ios.deployment_target = '8.0' s.osx.deployment_target = '10.11' diff --git a/SnapKit.xcodeproj/project.pbxproj b/SnapKit.xcodeproj/project.pbxproj index 77fc0dd..7221d08 100644 --- a/SnapKit.xcodeproj/project.pbxproj +++ b/SnapKit.xcodeproj/project.pbxproj @@ -33,6 +33,7 @@ EE235FC31C5785DC00C08960 /* ConstraintLayoutSupportDSL.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE235FBF1C5785DC00C08960 /* ConstraintLayoutSupportDSL.swift */; }; EE235FC81C5785E200C08960 /* ConstraintView+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE235FC61C5785E200C08960 /* ConstraintView+Extensions.swift */; }; EE4910991B19A40200A54F1F /* SnapKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EEBCC9D819CC627D0083B827 /* SnapKit.framework */; }; + EE6087751E4F133E0029CF84 /* ConstraintPriority.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE6087741E4F133E0029CF84 /* ConstraintPriority.swift */; }; EE6898CB1DA7B3A100D47F33 /* LayoutConstraintItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE6898CA1DA7B3A100D47F33 /* LayoutConstraintItem.swift */; }; EECDB3741AC0C9B6006BBC11 /* SnapKit.h in Headers */ = {isa = PBXBuildFile; fileRef = EECDB3661AC0C95C006BBC11 /* SnapKit.h */; settings = {ATTRIBUTES = (Public, ); }; }; EECDB3931AC0CB52006BBC11 /* Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EECDB36A1AC0C95C006BBC11 /* Tests.swift */; }; @@ -71,6 +72,7 @@ EE235FBE1C5785DC00C08960 /* ConstraintViewDSL.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConstraintViewDSL.swift; sourceTree = ""; }; EE235FBF1C5785DC00C08960 /* ConstraintLayoutSupportDSL.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConstraintLayoutSupportDSL.swift; sourceTree = ""; }; EE235FC61C5785E200C08960 /* ConstraintView+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ConstraintView+Extensions.swift"; sourceTree = ""; }; + EE6087741E4F133E0029CF84 /* ConstraintPriority.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConstraintPriority.swift; sourceTree = ""; }; EE6898CA1DA7B3A100D47F33 /* LayoutConstraintItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LayoutConstraintItem.swift; sourceTree = ""; }; EE94F6081AC0F10A008767FF /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; EE94F60A1AC0F10F008767FF /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.10.sdk/System/Library/Frameworks/AppKit.framework; sourceTree = DEVELOPER_DIR; }; @@ -187,6 +189,7 @@ EE235F6B1C5785C600C08960 /* ConstraintItem.swift */, EE235F6C1C5785C600C08960 /* LayoutConstraint.swift */, EE6898CA1DA7B3A100D47F33 /* LayoutConstraintItem.swift */, + EE6087741E4F133E0029CF84 /* ConstraintPriority.swift */, ); name = Models; sourceTree = ""; @@ -365,6 +368,7 @@ EE235FB51C5785D400C08960 /* ConstraintMakerEditable.swift in Sources */, EEF68FBC1D78653000980C26 /* ConstraintLayoutGuide.swift in Sources */, EE235FAC1C5785D400C08960 /* ConstraintMaker.swift in Sources */, + EE6087751E4F133E0029CF84 /* ConstraintPriority.swift in Sources */, EE235F941C5785CE00C08960 /* ConstraintRelatableTarget.swift in Sources */, EEF68FA61D784A5300980C26 /* ConstraintDSL.swift in Sources */, EE235FBB1C5785D400C08960 /* ConstraintMakerExtendable.swift in Sources */, @@ -506,6 +510,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "io.snapkit.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_BUNDLE_PACKAGE_TYPE = FMWK; PRODUCT_NAME = SnapKit; + SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; VERSIONING_SYSTEM = "apple-generic"; @@ -525,6 +530,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "io.snapkit.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_BUNDLE_PACKAGE_TYPE = FMWK; PRODUCT_NAME = SnapKit; + SKIP_INSTALL = YES; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; VERSIONING_SYSTEM = "apple-generic"; }; diff --git a/Source/Constraint.swift b/Source/Constraint.swift index 5c8c483..a4ec7f1 100644 --- a/Source/Constraint.swift +++ b/Source/Constraint.swift @@ -28,10 +28,10 @@ #endif public final class Constraint { - + internal let sourceLocation: (String, UInt) internal let label: String? - + private let from: ConstraintItem private let to: ConstraintItem private let relation: ConstraintRelation @@ -46,10 +46,19 @@ public final class Constraint { self.updateConstantAndPriorityIfNeeded() } } - private var layoutConstraints: [LayoutConstraint] + public var layoutConstraints: [LayoutConstraint] + + public var isActive: Bool { + for layoutConstraint in self.layoutConstraints { + if layoutConstraint.isActive { + return true + } + } + return false + } // MARK: Initialization - + internal init(from: ConstraintItem, to: ConstraintItem, relation: ConstraintRelation, @@ -67,17 +76,17 @@ public final class Constraint { self.constant = constant self.priority = priority self.layoutConstraints = [] - + // get attributes let layoutFromAttributes = self.from.attributes.layoutAttributes let layoutToAttributes = self.to.attributes.layoutAttributes - + // get layout from let layoutFrom = self.from.layoutConstraintItem! - + // get relation let layoutRelation = self.relation.layoutRelation - + for layoutFromAttribute in layoutFromAttributes { // get layout to attribute let layoutToAttribute: NSLayoutAttribute @@ -130,18 +139,18 @@ public final class Constraint { layoutToAttribute = layoutFromAttribute } #endif - + // get layout constant let layoutConstant: CGFloat = self.constant.constraintConstantTargetValueFor(layoutAttribute: layoutToAttribute) - + // get layout to var layoutTo: AnyObject? = self.to.target - + // use superview if possible if layoutTo == nil && layoutToAttribute != .width && layoutToAttribute != .height { layoutTo = layoutFrom.superview } - + // create layout constraint let layoutConstraint = LayoutConstraint( item: layoutFrom, @@ -152,119 +161,113 @@ public final class Constraint { multiplier: self.multiplier.constraintMultiplierTargetValue, constant: layoutConstant ) - + // set label layoutConstraint.label = self.label - + // set priority layoutConstraint.priority = self.priority.constraintPriorityTargetValue - + // set constraint layoutConstraint.constraint = self - + // append self.layoutConstraints.append(layoutConstraint) } } - + // MARK: Public - + @available(*, deprecated:3.0, message:"Use activate().") public func install() { self.activate() } - + @available(*, deprecated:3.0, message:"Use deactivate().") public func uninstall() { self.deactivate() } - + public func activate() { self.activateIfNeeded() } - + public func deactivate() { self.deactivateIfNeeded() } - + @discardableResult public func update(offset: ConstraintOffsetTarget) -> Constraint { self.constant = offset.constraintOffsetTargetValue return self } - + @discardableResult public func update(inset: ConstraintInsetTarget) -> Constraint { self.constant = inset.constraintInsetTargetValue return self } - + @discardableResult public func update(priority: ConstraintPriorityTarget) -> Constraint { self.priority = priority.constraintPriorityTargetValue return self } - + @available(*, deprecated:3.0, message:"Use update(offset: ConstraintOffsetTarget) instead.") public func updateOffset(amount: ConstraintOffsetTarget) -> Void { self.update(offset: amount) } - + @available(*, deprecated:3.0, message:"Use update(inset: ConstraintInsetTarget) instead.") public func updateInsets(amount: ConstraintInsetTarget) -> Void { self.update(inset: amount) } - + @available(*, deprecated:3.0, message:"Use update(priority: ConstraintPriorityTarget) instead.") public func updatePriority(amount: ConstraintPriorityTarget) -> Void { self.update(priority: amount) } - + @available(*, obsoleted:3.0, message:"Use update(priority: ConstraintPriorityTarget) instead.") public func updatePriorityRequired() -> Void {} - + @available(*, obsoleted:3.0, message:"Use update(priority: ConstraintPriorityTarget) instead.") public func updatePriorityHigh() -> Void { fatalError("Must be implemented by Concrete subclass.") } - + @available(*, obsoleted:3.0, message:"Use update(priority: ConstraintPriorityTarget) instead.") public func updatePriorityMedium() -> Void { fatalError("Must be implemented by Concrete subclass.") } - + @available(*, obsoleted:3.0, message:"Use update(priority: ConstraintPriorityTarget) instead.") public func updatePriorityLow() -> Void { fatalError("Must be implemented by Concrete subclass.") } - + // MARK: Internal - + internal func updateConstantAndPriorityIfNeeded() { for layoutConstraint in self.layoutConstraints { let attribute = (layoutConstraint.secondAttribute == .notAnAttribute) ? layoutConstraint.firstAttribute : layoutConstraint.secondAttribute layoutConstraint.constant = self.constant.constraintConstantTargetValueFor(layoutAttribute: attribute) - - #if os(iOS) || os(tvOS) - let requiredPriority: UILayoutPriority = UILayoutPriorityRequired - #else - let requiredPriority: Float = 1000.0 - #endif - - + + let requiredPriority = ConstraintPriority.required.value if (layoutConstraint.priority < requiredPriority), (self.priority.constraintPriorityTargetValue != requiredPriority) { layoutConstraint.priority = self.priority.constraintPriorityTargetValue } } } - + internal func activateIfNeeded(updatingExisting: Bool = false) { guard let item = self.from.layoutConstraintItem else { print("WARNING: SnapKit failed to get from item from constraint. Activate will be a no-op.") return } let layoutConstraints = self.layoutConstraints - + if updatingExisting { var existingLayoutConstraints: [LayoutConstraint] = [] for constraint in item.constraints { existingLayoutConstraints += constraint.layoutConstraints } - + for layoutConstraint in layoutConstraints { let existingLayoutConstraint = existingLayoutConstraints.first { $0 == layoutConstraint } guard let updateLayoutConstraint = existingLayoutConstraint else { fatalError("Updated constraint could not find existing matching constraint to update: \(layoutConstraint)") } - + let updateLayoutAttribute = (updateLayoutConstraint.secondAttribute == .notAnAttribute) ? updateLayoutConstraint.firstAttribute : updateLayoutConstraint.secondAttribute updateLayoutConstraint.constant = self.constant.constraintConstantTargetValueFor(layoutAttribute: updateLayoutAttribute) } @@ -273,7 +276,7 @@ public final class Constraint { item.add(constraints: [self]) } } - + internal func deactivateIfNeeded() { guard let item = self.from.layoutConstraintItem else { print("WARNING: SnapKit failed to get from item from constraint. Deactivate will be a no-op.") diff --git a/Source/ConstraintMakerExtendable.swift b/Source/ConstraintMakerExtendable.swift index 658c833..6a755b5 100644 --- a/Source/ConstraintMakerExtendable.swift +++ b/Source/ConstraintMakerExtendable.swift @@ -109,6 +109,12 @@ public class ConstraintMakerExtendable: ConstraintMakerRelatable { return self } + @available(iOS 8.0, *) + public var topMargin: ConstraintMakerExtendable { + self.description.attributes += .topMargin + return self + } + @available(iOS 8.0, *) public var bottomMargin: ConstraintMakerExtendable { self.description.attributes += .bottomMargin diff --git a/Source/ConstraintMakerPriortizable.swift b/Source/ConstraintMakerPriortizable.swift index 82a6de6..ef79448 100644 --- a/Source/ConstraintMakerPriortizable.swift +++ b/Source/ConstraintMakerPriortizable.swift @@ -30,38 +30,39 @@ public class ConstraintMakerPriortizable: ConstraintMakerFinalizable { + @discardableResult + public func priority(_ amount: ConstraintPriority) -> ConstraintMakerFinalizable { + self.description.priority = amount.value + return self + } + @discardableResult public func priority(_ amount: ConstraintPriorityTarget) -> ConstraintMakerFinalizable { self.description.priority = amount return self } - @available(*, deprecated:3.0, message:"Use priority(_ amount: ConstraintPriorityTarget) instead.") + @available(*, deprecated:3.0, message:"Use priority(.required) instead.") @discardableResult public func priorityRequired() -> ConstraintMakerFinalizable { - return self.priority(1000) + return self.priority(.required) } - @available(*, deprecated:3.0, message:"Use priority(_ amount: ConstraintPriorityTarget) instead.") + @available(*, deprecated:3.0, message:"Use priority(.high) instead.") @discardableResult public func priorityHigh() -> ConstraintMakerFinalizable { - return self.priority(750) + return self.priority(.high) } - @available(*, deprecated:3.0, message:"Use priority(_ amount: ConstraintPriorityTarget) instead.") + @available(*, deprecated:3.0, message:"Use priority(.medium) instead.") @discardableResult public func priorityMedium() -> ConstraintMakerFinalizable { - #if os(iOS) || os(tvOS) - return self.priority(500) - #else - return self.priority(501) - #endif + return self.priority(.medium) } - @available(*, deprecated:3.0, message:"Use priority(_ amount: ConstraintPriorityTarget) instead.") + @available(*, deprecated:3.0, message:"Use priority(.low) instead.") @discardableResult public func priorityLow() -> ConstraintMakerFinalizable { - return self.priority(250) + return self.priority(.low) } - } diff --git a/Source/ConstraintPriority.swift b/Source/ConstraintPriority.swift new file mode 100644 index 0000000..7299fa9 --- /dev/null +++ b/Source/ConstraintPriority.swift @@ -0,0 +1,68 @@ +// +// SnapKit +// +// Copyright (c) 2011-Present SnapKit Team - https://github.com/SnapKit +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#if os(iOS) || os(tvOS) + import UIKit +#else + import AppKit +#endif + + +public struct ConstraintPriority : ExpressibleByFloatLiteral, Equatable { + public typealias FloatLiteralType = Float + + public let value: Float + + public init(floatLiteral value: Float) { + self.value = value + } + + public init(_ value: Float) { + self.value = value + } + + public static var required: ConstraintPriority { + return 1000.0 + } + + public static var high: ConstraintPriority { + return 750.0 + } + + public static var medium: ConstraintPriority { + #if os(OSX) + return 501.0 + #else + return 500.0 + #endif + + } + + public static var low: ConstraintPriority { + return 250.0 + } + + public static func ==(lhs: ConstraintPriority, rhs: ConstraintPriority) -> Bool { + return lhs.value == rhs.value + } +} diff --git a/Source/ConstraintViewDSL.swift b/Source/ConstraintViewDSL.swift index 0242c4a..8b38d32 100644 --- a/Source/ConstraintViewDSL.swift +++ b/Source/ConstraintViewDSL.swift @@ -74,7 +74,7 @@ public struct ConstraintViewDSL: ConstraintAttributesDSL { return self.view.contentCompressionResistancePriority(for: .horizontal) } set { - self.view.setContentHuggingPriority(newValue, for: .horizontal) + self.view.setContentCompressionResistancePriority(newValue, for: .horizontal) } } diff --git a/Tests/Tests.swift b/Tests/Tests.swift index 564be53..ed6df40 100644 --- a/Tests/Tests.swift +++ b/Tests/Tests.swift @@ -517,4 +517,27 @@ class SnapKitTests: XCTestCase { self.container.snp.setLabel("Hello World") } + func testPriorityShortcuts() { + let view = View() + self.container.addSubview(view) + + view.snp.remakeConstraints { make in + make.left.equalTo(1000.0).priority(.required) + } + XCTAssertEqual(self.container.snp_constraints.count, 1, "Should have 1 constraint") + XCTAssertEqual(self.container.snp_constraints.first?.priority, ConstraintPriority.required.value) + + view.snp.remakeConstraints { make in + make.left.equalTo(1000.0).priority(.low) + } + XCTAssertEqual(self.container.snp_constraints.count, 1, "Should have 1 constraint") + XCTAssertEqual(self.container.snp_constraints.first?.priority, ConstraintPriority.low.value) + + view.snp.remakeConstraints { make in + make.left.equalTo(1000.0).priority(ConstraintPriority.low.value + 1) + } + XCTAssertEqual(self.container.snp_constraints.count, 1, "Should have 1 constraint") + XCTAssertEqual(self.container.snp_constraints.first?.priority, ConstraintPriority.low.value + 1) + } + }