├── Cartfile
├── Cartfile.resolved
├── Gemfile
├── .swiftpm
└── xcode
│ └── package.xcworkspace
│ └── contents.xcworkspacedata
├── Layoutable.xcodeproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
├── xcshareddata
│ └── xcschemes
│ │ ├── LayoutableTests.xcscheme
│ │ └── Layoutable.xcscheme
└── project.pbxproj
├── Package.resolved
├── Layoutable
├── Layoutable.h
├── Info.plist
└── Sources
│ ├── LayoutEngine.swift
│ ├── LayoutProperty.swift
│ ├── LayoutValues.swift
│ ├── LayoutManager.swift
│ ├── LayoutConstraint.swift
│ ├── LayoutAnchor.swift
│ ├── CompositAnchor.swift
│ └── Layoutable.swift
├── .travis.yml
├── Package.swift
├── LayoutableTests
├── Info.plist
├── TestNode.swift
├── LayoutTest.swift
└── PerformanceTest.swift
├── LICENSE
├── SwiftLayoutable.podspec
├── .gitignore
└── README.md
/Cartfile:
--------------------------------------------------------------------------------
1 | github "https://github.com/nangege/Cassowary" "master"
2 |
--------------------------------------------------------------------------------
/Cartfile.resolved:
--------------------------------------------------------------------------------
1 | github "nangege/Cassowary" "fdc596978d9daff5b4da6640aa3666b0519cdc7f"
2 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | source "https://rubygems.org"
4 |
5 | gem "fastlane"
6 | gem "jazzy"
7 | gem "cocoapods", "~>1.6.beta"
8 |
--------------------------------------------------------------------------------
/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Layoutable.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Layoutable.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Package.resolved:
--------------------------------------------------------------------------------
1 | {
2 | "object": {
3 | "pins": [
4 | {
5 | "package": "Cassowary",
6 | "repositoryURL": "git@github.com:nangege/Cassowary.git",
7 | "state": {
8 | "branch": "master",
9 | "revision": "c5c93c28853a56bc484fa59696e9d0ee80ec9d0e",
10 | "version": null
11 | }
12 | }
13 | ]
14 | },
15 | "version": 1
16 | }
17 |
--------------------------------------------------------------------------------
/Layoutable/Layoutable.h:
--------------------------------------------------------------------------------
1 | //
2 | // Layoutable.h
3 | // Layoutable
4 | //
5 | // Created by nangezao on 2018/8/31.
6 | // Copyright © 2018 Tang Nan. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | //! Project version number for Layoutable.
12 | FOUNDATION_EXPORT double LayoutableVersionNumber;
13 |
14 | //! Project version string for Layoutable.
15 | FOUNDATION_EXPORT const unsigned char LayoutableVersionString[];
16 |
17 | // In this header, you should import all the public headers of your framework using statements like #import
18 |
19 |
20 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: swift
2 | os: osx
3 | osx_image: xcode10
4 |
5 | env:
6 | matrix:
7 | - TEST_TYPE=iOS
8 |
9 | -before_install:
10 | - |
11 | gem install xcpretty -N --no-ri --no-rdoc
12 | brew update
13 | brew outdated carthage || brew upgrade carthage
14 | script:
15 | - |
16 | if [ "$TEST_TYPE" = iOS ]; then
17 | set -o pipefail
18 | carthage update
19 | xcodebuild clean test -project Layoutable.xcodeproj -scheme Layoutable -destination 'platform=iOS Simulator,name=iPhone 7,OS=12.0' -enableCodeCoverage YES | bundle exec xcpretty
20 | fi
21 | after_success:
22 | - sleep 5
23 | - if [ "$TEST_TYPE" = iOS ] ; then
24 | bash <(curl -s https://codecov.io/bash)
25 | fi
26 |
--------------------------------------------------------------------------------
/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version:5.5
2 |
3 | import PackageDescription
4 |
5 | let package = Package(
6 | name: "Layoutable",
7 | products: [
8 | .library(
9 | name: "Layoutable",
10 | targets: ["Layoutable"]
11 | )
12 | ],
13 | dependencies: [.package(url: "git@github.com:nangege/Cassowary.git", branch:"master")],
14 | targets: [
15 | .target(
16 | name: "Layoutable",
17 | dependencies: ["Cassowary"],
18 | path: "Layoutable/Sources"
19 | ),
20 | .testTarget(
21 | name: "LayoutableTests",
22 | dependencies: ["Layoutable"],
23 | path: "LayoutableTests"
24 | )
25 | ]
26 | )
27 |
--------------------------------------------------------------------------------
/LayoutableTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 |
22 |
23 |
--------------------------------------------------------------------------------
/Layoutable/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | $(CURRENT_PROJECT_VERSION)
21 |
22 |
23 |
--------------------------------------------------------------------------------
/LayoutableTests/TestNode.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TestNode.swift
3 | // LayoutableTests
4 | //
5 | // Created by nangezao on 2018/9/4.
6 | // Copyright © 2018 Tang Nan. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | @testable import Layoutable
11 |
12 | class TestNode: Layoutable{
13 |
14 | public init() {}
15 |
16 | lazy var layoutManager = LayoutManager(self)
17 |
18 | var layoutSize = CGSize(width: InvalidIntrinsicMetric, height: InvalidIntrinsicMetric)
19 |
20 | weak var superItem: Layoutable? = nil
21 |
22 | var subItems = [Layoutable]()
23 |
24 | func addSubnode(_ node: TestNode){
25 | subItems.append(node)
26 | node.superItem = self
27 | }
28 |
29 | func layoutSubItems() {}
30 |
31 | func updateConstraint() {}
32 |
33 | var layoutRect: CGRect = .zero
34 |
35 | var itemIntrinsicContentSize: CGSize{
36 | return layoutSize
37 | }
38 |
39 | func contentSizeFor(maxWidth: CGFloat) -> CGSize {
40 | return InvaidIntrinsicSize
41 | }
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 nangege
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/LayoutableTests/LayoutTest.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LayoutTest.swift
3 | // LayoutableTests
4 | //
5 | // Created by nangezao on 2018/10/10.
6 | // Copyright © 2018 Tang Nan. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | @testable import Layoutable
11 |
12 | class LayoutTest: XCTestCase {
13 |
14 | override func setUp() {
15 | // Put setup code here. This method is called before the invocation of each test method in the class.
16 | }
17 |
18 | override func tearDown() {
19 | // Put teardown code here. This method is called after the invocation of each test method in the class.
20 | }
21 |
22 |
23 | func testExample() {
24 | let node = TestNode()
25 | let node1 = TestNode()
26 | let node2 = TestNode()
27 |
28 | node.addSubnode(node1)
29 | node.addSubnode(node2)
30 |
31 | node1.size == (30,30)
32 | node2.size == (40,40)
33 |
34 | [node,node1].equal(.centerY,.left)
35 |
36 | [node2,node].equal(.top,.bottom,.centerY,.right)
37 |
38 | [node1,node2].space(10, axis: .horizontal)
39 |
40 | node.layoutIfEnabled()
41 |
42 | XCTAssertEqual(node1.layoutRect, CGRect(x: 0, y: 5, width: 30, height: 30))
43 | XCTAssertEqual(node2.layoutRect, CGRect(x: 40, y: 0, width: 40, height: 40))
44 | XCTAssertEqual(node.layoutRect, CGRect(x: 0, y: 0, width: 80, height: 40))
45 | }
46 |
47 | }
48 |
--------------------------------------------------------------------------------
/SwiftLayoutable.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |s|
2 |
3 | s.name = "SwiftLayoutable"
4 | s.version = "0.1-beta"
5 | s.summary = "Swift reimplement of Autolayout"
6 |
7 | s.description = <<-DESC
8 | Layoutable is a swift reimplement of apple's Autolayout.
9 | * It uses the same Cassowary algorithm as it's core and provides a set of api similar to Autolayout.
10 | * The difference is that Layouable is more flexable and easy to use.Layoutable don't rely on UIView, it can be used in any object that conform to Layoutable protocol such as CALaye or self defined object.
11 | * It can be used in background thread which is the core benefit of Layoutable. Layoutable also provides high level api and syntax sugar to make it easy to use.
12 | DESC
13 |
14 | s.homepage = "https://github.com/nangege/Layoutable"
15 |
16 | s.license = { :type => "MIT", :file => "LICENSE" }
17 |
18 | s.authors = { "nangege" => "1543640002@qq.com" }
19 |
20 | s.swift_version = "4.2"
21 |
22 | s.ios.deployment_target = "8.0"
23 | s.module_name = "Layoutable"
24 |
25 |
26 | s.source = { :git => "https://github.com/nangege/Layoutable.git", :tag => '0.1-beta' }
27 | s.source_files = ["Layoutable/Sources/*.swift", "Layoutable/Layoutable.h"]
28 | s.public_header_files = ["Layoutable/Layoutable.h"]
29 |
30 |
31 | s.requires_arc = true
32 |
33 | s.dependency 'SwiftCassowary', '~> 0.1-beta'
34 |
35 | end
36 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | #
3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
4 |
5 | ## Build generated
6 | build/
7 | DerivedData/
8 |
9 | ## Various settings
10 | *.pbxuser
11 | !default.pbxuser
12 | *.mode1v3
13 | !default.mode1v3
14 | *.mode2v3
15 | !default.mode2v3
16 | *.perspectivev3
17 | !default.perspectivev3
18 | xcuserdata/
19 |
20 | ## Other
21 | *.moved-aside
22 | *.xccheckout
23 | *.xcscmblueprint
24 |
25 | ## Obj-C/Swift specific
26 | *.hmap
27 | *.ipa
28 | *.dSYM.zip
29 | *.dSYM
30 |
31 | ## Playgrounds
32 | timeline.xctimeline
33 | playground.xcworkspace
34 |
35 | # Swift Package Manager
36 | #
37 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
38 | # Packages/
39 | # Package.pins
40 | # Package.resolved
41 | .build/
42 |
43 | # CocoaPods
44 | #
45 | # We recommend against adding the Pods directory to your .gitignore. However
46 | # you should judge for yourself, the pros and cons are mentioned at:
47 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
48 | #
49 | # Pods/
50 |
51 | # Carthage
52 | #
53 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
54 | Carthage/Checkouts
55 |
56 | Carthage/Build
57 |
58 | # fastlane
59 | #
60 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
61 | # screenshots whenever they are needed.
62 | # For more information about the recommended setup visit:
63 | # https://docs.fastlane.tools/best-practices/source-control/#source-control
64 |
65 | fastlane/report.xml
66 | fastlane/Preview.html
67 | fastlane/screenshots/**/*.png
68 | fastlane/test_output
69 |
70 | *.DS_Store
71 |
--------------------------------------------------------------------------------
/LayoutableTests/PerformanceTest.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LayoutableTests.swift
3 | // LayoutableTests
4 | //
5 | // Created by nangezao on 2018/8/31.
6 | // Copyright © 2018 Tang Nan. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | @testable import Layoutable
11 |
12 | class LayoutableTests: XCTestCase {
13 |
14 | func testNestPerformance() {
15 | let testNumber = 100
16 | self.measure {
17 | let node = TestNode()
18 | var nodes = [TestNode]()
19 | node.size == (320.0,640.0)
20 | for index in 0..= 0
57 | newNode.right <= node.right
58 |
59 | newNode.top >= 20
60 | newNode.bottom <= node.bottom - 20
61 |
62 | newNode.left == leftNode.left + CGFloat(arc4random()%20)
63 | node.top == rightNode.top + CGFloat(arc4random()%20) ~ .strong
64 |
65 | nodes.append(newNode)
66 | }
67 | node.layoutIfEnabled()
68 | }
69 | }
70 |
71 | }
72 |
--------------------------------------------------------------------------------
/Layoutable.xcodeproj/xcshareddata/xcschemes/LayoutableTests.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
14 |
15 |
17 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
39 |
40 |
41 |
42 |
48 |
49 |
51 |
52 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/Layoutable/Sources/LayoutEngine.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LayoutEngine.swift
3 | // Cassowary
4 | //
5 | // Created by Tang,Nan(MAD) on 2018/3/19.
6 | // Copyright © 2018年 nange. All rights reserved.
7 | //
8 | /// Permission is hereby granted, free of charge, to any person obtaining a copy
9 | /// of this software and associated documentation files (the "Software"), to deal
10 | /// in the Software without restriction, including without limitation the rights
11 | /// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 | /// copies of the Software, and to permit persons to whom the Software is
13 | /// furnished to do so, subject to the following conditions:
14 | ///
15 | /// The above copyright notice and this permission notice shall be included in
16 | /// all copies or substantial portions of the Software.
17 | ///
18 | /// Notwithstanding the foregoing, you may not use, copy, modify, merge, publish,
19 | /// distribute, sublicense, create a derivative work, and/or sell copies of the
20 | /// Software in any work that is designed, intended, or marketed for pedagogical or
21 | /// instructional purposes related to programming, coding, application development,
22 | /// or information technology. Permission for such use, copying, modification,
23 | /// merger, publication, distribution, sublicensing, creation of derivative works,
24 | /// or sale is expressly withheld.
25 | ///
26 | /// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
27 | /// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
28 | /// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
29 | /// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
30 | /// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
31 | /// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
32 | /// THE SOFTWARE.
33 |
34 | import Cassowary
35 | import Foundation
36 |
37 | final class LayoutEngine{
38 |
39 | static let solverPool = NSMapTable.weakToStrongObjects()
40 | static func solveFor(_ node: Layoutable) -> SimplexSolver{
41 | if let solver = solverPool.object(forKey: node){
42 | return solver
43 | }else{
44 | let solver = SimplexSolver()
45 | solver.autoSolve = false
46 | solverPool.setObject(solver, forKey: node)
47 | return solver
48 | }
49 | }
50 |
51 | }
52 |
--------------------------------------------------------------------------------
/Layoutable/Sources/LayoutProperty.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Layout.swift
3 | //
4 | // Created by nangezao on 2017/7/19.
5 | // Copyright © 2017年 nange. All rights reserved.
6 | //
7 | /// Permission is hereby granted, free of charge, to any person obtaining a copy
8 | /// of this software and associated documentation files (the "Software"), to deal
9 | /// in the Software without restriction, including without limitation the rights
10 | /// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | /// copies of the Software, and to permit persons to whom the Software is
12 | /// furnished to do so, subject to the following conditions:
13 | ///
14 | /// The above copyright notice and this permission notice shall be included in
15 | /// all copies or substantial portions of the Software.
16 | ///
17 | /// Notwithstanding the foregoing, you may not use, copy, modify, merge, publish,
18 | /// distribute, sublicense, create a derivative work, and/or sell copies of the
19 | /// Software in any work that is designed, intended, or marketed for pedagogical or
20 | /// instructional purposes related to programming, coding, application development,
21 | /// or information technology. Permission for such use, copying, modification,
22 | /// merger, publication, distribution, sublicensing, creation of derivative works,
23 | /// or sale is expressly withheld.
24 | ///
25 | /// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
26 | /// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
27 | /// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
28 | /// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
29 | /// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
30 | /// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
31 | /// THE SOFTWARE.
32 |
33 | import Cassowary
34 | import CoreGraphics
35 |
36 | final class LayoutProperty{
37 |
38 | let x = Variable()
39 | let y = Variable()
40 | let width = Variable.restricted()
41 | let height = Variable.restricted()
42 |
43 | weak var solver: SimplexSolver?
44 |
45 | var frame: CGRect{
46 | guard let solver = solver else{
47 | return .zero
48 | }
49 | let minX = solver.valueFor(x)
50 | let minY = solver.valueFor(y)
51 | let w = solver.valueFor(width)
52 | let h = solver.valueFor(height)
53 | return CGRect(x: minX ?? 0, y: minY ?? 0, width: w ?? 0, height: h ?? 0)
54 | }
55 |
56 | func expressionFor(attribue: LayoutAttribute) -> Expression{
57 | switch attribue {
58 | case .left:
59 | return Expression(x)
60 | case .top:
61 | return Expression(y)
62 | case .right:
63 | return x + width
64 | case .bottom:
65 | return y + height
66 | case .width:
67 | return Expression(width)
68 | case .height:
69 | return Expression(height)
70 | case .centerX:
71 | return width/2 + x
72 | case .centerY:
73 | return height/2 + y
74 | }
75 | }
76 | }
77 |
78 |
--------------------------------------------------------------------------------
/Layoutable/Sources/LayoutValues.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LayoutCache.swift
3 | // Cassowary
4 | //
5 | // Created by nangezao on 2017/12/7.
6 | // Copyright © 2017年 nange. All rights reserved.
7 | //
8 | /// Permission is hereby granted, free of charge, to any person obtaining a copy
9 | /// of this software and associated documentation files (the "Software"), to deal
10 | /// in the Software without restriction, including without limitation the rights
11 | /// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 | /// copies of the Software, and to permit persons to whom the Software is
13 | /// furnished to do so, subject to the following conditions:
14 | ///
15 | /// The above copyright notice and this permission notice shall be included in
16 | /// all copies or substantial portions of the Software.
17 | ///
18 | /// Notwithstanding the foregoing, you may not use, copy, modify, merge, publish,
19 | /// distribute, sublicense, create a derivative work, and/or sell copies of the
20 | /// Software in any work that is designed, intended, or marketed for pedagogical or
21 | /// instructional purposes related to programming, coding, application development,
22 | /// or information technology. Permission for such use, copying, modification,
23 | /// merger, publication, distribution, sublicensing, creation of derivative works,
24 | /// or sale is expressly withheld.
25 | ///
26 | /// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
27 | /// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
28 | /// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
29 | /// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
30 | /// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
31 | /// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
32 | /// THE SOFTWARE.
33 |
34 | import CoreFoundation
35 |
36 | // use tuple as data struct to make it easy to write
37 | // like node.size = (30,30) compare to UIKit node.size = CGSize(width: height: 30)
38 | public typealias Value = CGFloat
39 | public typealias Size = (width: Value, height: Value)
40 | public typealias Point = (x: Value, y: Value)
41 | public typealias Offset = (x: Value, y: Value)
42 | public typealias Insets = (top: Value,left: Value, bottom: Value,right: Value)
43 | public typealias XSideInsets = (left: Value, right: Value)
44 | public typealias YSideInsets = (top: Value, bottom: Value)
45 | public typealias EdgeInsets = (top: Value, left: Value, bottom: Value, right: Value)
46 |
47 | //public typealias Rect = (origin: Point, size: Size)
48 | //
49 | public let InvalidIntrinsicMetric: CGFloat = -1
50 | //
51 | public let InvaidIntrinsicSize = CGSize(width: InvalidIntrinsicMetric,
52 | height: InvalidIntrinsicMetric)
53 |
54 | public let EdgeInsetsZero: EdgeInsets = (0,0,0,0)
55 |
56 | //public let RectZero: Rect = ((0,0),(0,0))
57 |
58 | public let OffsetZero: Offset = (0,0)
59 |
60 | public let SizeZero: Size = (0,0)
61 |
62 | public struct LayoutValues{
63 | public var frame = CGRect.zero
64 | public var subLayout = [LayoutValues]()
65 | }
66 |
67 |
--------------------------------------------------------------------------------
/Layoutable.xcodeproj/xcshareddata/xcschemes/Layoutable.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
33 |
39 |
40 |
41 |
42 |
43 |
49 |
50 |
51 |
52 |
53 |
54 |
64 |
65 |
71 |
72 |
73 |
74 |
75 |
76 |
82 |
83 |
89 |
90 |
91 |
92 |
94 |
95 |
98 |
99 |
100 |
--------------------------------------------------------------------------------
/Layoutable/Sources/LayoutManager.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LayoutManager.swift
3 | // Layoutable
4 | //
5 | // Created by nangezao on 2018/9/21.
6 | // Copyright © 2018 Tang Nan. All rights reserved.
7 | //
8 | //
9 | /// Permission is hereby granted, free of charge, to any person obtaining a copy
10 | /// of this software and associated documentation files (the "Software"), to deal
11 | /// in the Software without restriction, including without limitation the rights
12 | /// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | /// copies of the Software, and to permit persons to whom the Software is
14 | /// furnished to do so, subject to the following conditions:
15 | ///
16 | /// The above copyright notice and this permission notice shall be included in
17 | /// all copies or substantial portions of the Software.
18 | ///
19 | /// Notwithstanding the foregoing, you may not use, copy, modify, merge, publish,
20 | /// distribute, sublicense, create a derivative work, and/or sell copies of the
21 | /// Software in any work that is designed, intended, or marketed for pedagogical or
22 | /// instructional purposes related to programming, coding, application development,
23 | /// or information technology. Permission for such use, copying, modification,
24 | /// merger, publication, distribution, sublicensing, creation of derivative works,
25 | /// or sale is expressly withheld.
26 | ///
27 | /// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
28 | /// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
29 | /// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
30 | /// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
31 | /// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
32 | /// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
33 | /// THE SOFTWARE.
34 |
35 | import Cassowary
36 | import CoreGraphics
37 |
38 |
39 | /// LayoutManager hold and handle all the properties needed for layout
40 | /// like SimplexSolver, LayoutProperty ...
41 | /// so that the class conform to LayoutItem does not need to provide those properties
42 | final public class LayoutManager{
43 |
44 | public init(_ item: Layoutable){
45 | self.item = item
46 | }
47 |
48 | weak var item: Layoutable?
49 |
50 | weak var solver: SimplexSolver?
51 |
52 | var variable = LayoutProperty()
53 |
54 | // This property is used to adjust position after layout pass
55 | // It is useful for simplefy layout for irregular layout
56 | public var offset = OffsetZero
57 |
58 | var fixedWidth = false
59 |
60 | /// just like translateAutoSizingMaskIntoConstraints in autolayout
61 | /// if true, current frame of this item will be added to layout engine
62 | var translateRectIntoConstraints = true
63 |
64 | var enabled = true
65 |
66 | /// to indicator whether contentSize of this item is changed
67 | /// if true, contentSize constraints will be updated
68 | var layoutNeedsUpdate = false
69 |
70 | /// used to track all constraints that secondAnchor.item is self
71 | var pinedConstraints = Set()
72 |
73 | /// constraints for this item that had been added to solver
74 | var installedConstraints = Set()
75 |
76 | /// constraints for this item that need to be added to solver
77 | var newlyAddedConstraints = Set()
78 |
79 | /// size constraints for contentSize
80 | let contentSizeConstraints = ContentSizeConstraints()
81 |
82 | // size constraints used for frame translated constraint
83 | let sizeConstraints = SizeConstraints()
84 |
85 | /// position constraints used for frame translated constraint
86 | let positionConstraints = PositionConstraints()
87 |
88 | /// whether this item should constrainted by rect translated constraints
89 | var isRectConstrainted: Bool{
90 | return translateRectIntoConstraints && !pinedConstraints.isEmpty
91 | }
92 |
93 | var isConstraintValidRect: Bool{
94 | return solver != nil && !translateRectIntoConstraints
95 | }
96 |
97 | var sizeNeedsUpdate: Bool{
98 | return layoutNeedsUpdate && !translateRectIntoConstraints
99 | }
100 |
101 | func addConstraintsTo(_ solver: SimplexSolver){
102 |
103 | // maybe need optiomize
104 | // find a better way to manager constraint cycle
105 | // when to add ,when to remove
106 | self.solver = solver
107 | variable.solver = solver
108 | installedConstraints.forEach{ $0.addToSolver(solver)}
109 | updateConstraint()
110 | }
111 |
112 | /// add new constraints to current solver
113 | func updateConstraint(){
114 | if let solver = self.solver{
115 | newlyAddedConstraints.forEach {
116 | $0.addToSolver(solver)
117 | installedConstraints.insert($0)
118 | }
119 | newlyAddedConstraints.removeAll()
120 | }
121 | }
122 |
123 | func addConstraint(_ constraint: LayoutConstraint){
124 | newlyAddedConstraints.insert(constraint)
125 | }
126 |
127 | func removeConstraint(_ constraint: LayoutConstraint){
128 | newlyAddedConstraints.remove(constraint)
129 | installedConstraints.remove(constraint)
130 | }
131 |
132 | func updateSize(_ size: CGSize){
133 | guard let item = item else { return }
134 | contentSizeConstraints.updateSize(size, node: item)
135 | }
136 |
137 | func updateRect(_ rect: CGRect){
138 | guard let item = item else { return }
139 | sizeConstraints.updateSize(rect.size, node: item)
140 | positionConstraints.updateOrigin(rect.origin, node: item)
141 | }
142 |
143 | /// final caculated rect for this item
144 | var layoutRect: CGRect{
145 | return variable.frame
146 | }
147 | }
148 |
149 | final class ContentSizeConstraints{
150 |
151 | class Axis{
152 | var huggingPriorty = LayoutPriority.medium{
153 | didSet{
154 | if let hugging = hugging{
155 | hugging.priority = huggingPriorty
156 | }
157 | }
158 | }
159 |
160 | var compressionPriorty = LayoutPriority.strong{
161 | didSet{
162 | if let compression = compression{
163 | compression.priority = compressionPriorty
164 | }
165 | }
166 | }
167 |
168 | var hugging: LayoutConstraint?
169 | var compression: LayoutConstraint?
170 | }
171 |
172 | var xAxis = Axis()
173 | var yAxis = Axis()
174 |
175 | /// update content size Constraint
176 | func updateSize(_ size: CGSize,node: Layoutable){
177 |
178 | if size.width != InvalidIntrinsicMetric{
179 | if let width = xAxis.hugging,
180 | let widthCompression = xAxis.compression{
181 |
182 | widthCompression.constant = size.width
183 | width.constant = size.width
184 |
185 | }else{
186 | xAxis.compression = node.width >= size.width ~ xAxis.compressionPriorty
187 | xAxis.hugging = node.width <= size.width ~ xAxis.huggingPriorty
188 | }
189 | }
190 |
191 | if size.height != InvalidIntrinsicMetric{
192 | if let height = yAxis.hugging,
193 | let heightCompression = yAxis.compression{
194 |
195 | heightCompression.constant = size.height
196 | height.constant = size.height
197 |
198 | }else{
199 | yAxis.compression = node.height >= size.height ~ yAxis.compressionPriorty
200 | yAxis.hugging = node.height <= size.height ~ yAxis.huggingPriorty
201 | }
202 | }
203 | }
204 | }
205 |
206 | final class SizeConstraints{
207 |
208 | var width: LayoutConstraint?
209 | var height: LayoutConstraint?
210 |
211 | /// update content size Constraint
212 | func updateSize(_ size: CGSize,node: Layoutable, priority: LayoutPriority = .strong){
213 |
214 | if size.width != InvalidIntrinsicMetric{
215 | if let width = width{
216 | width.constant = size.width
217 | }else{
218 | width = node.width == size.width ~ priority
219 | }
220 | }
221 |
222 | if size.height != InvalidIntrinsicMetric{
223 | if let height = height{
224 | height.constant = size.height
225 | }else{
226 | height = node.height == size.height ~ priority
227 | }
228 | }
229 | }
230 | }
231 |
232 | final class PositionConstraints{
233 |
234 | /// used for frame translated constraint
235 | var minX: LayoutConstraint?
236 | var minY: LayoutConstraint?
237 |
238 | /// update content size Constraint
239 | func updateOrigin(_ point: CGPoint,node: Layoutable, priority: LayoutPriority = .required){
240 |
241 | if let minX = minX{
242 | minX.constant = point.x
243 | }else{
244 | minX = node.left == point.x ~ priority
245 | }
246 |
247 | if let minY = minY{
248 | minY.constant = point.y
249 | }else{
250 | minY = node.top == point.y ~ priority
251 | }
252 | }
253 | }
254 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## Layoutable
2 | [](http://cocoapods.org/pods/SwiftLayoutable)
3 | [](https://github.com/Carthage/Carthage)
4 | []()
5 | []()
6 |
7 |
8 |
9 |
10 | Layoutable is a swift reimplement of apple's Autolayout. It uses the same [Cassowary ](https://constraints.cs.washington.edu/cassowary/) algorithm as it's core and provides a set of api similar to Autolayout. The difference is that Layouable is more flexable and easy to use.Layoutable don't rely on UIView, it can be used in any object that conform to Layoutable protocol such as CALaye or self defined object.It can be used in background thread which is the core benefit of Layoutable. Layoutable also provides high level api and syntax sugar to make it easy to use.
11 |
12 | ## Requirements
13 | - iOS 8.0+
14 | - Swift 4.2
15 | - Xcode 10.0 or higher
16 |
17 | ## Installation
18 |
19 | ### CocoaPods
20 |
21 | [CocoaPods](http://cocoapods.org/) is a dependency manager for Cocoa projects. Install it with the following command:
22 |
23 | `$ gem install cocoapods`
24 |
25 | To integrate Layoutable into your Xcode project using CocoaPods, specify it to a target in your Podfile:
26 |
27 | ```
28 | source 'https://github.com/CocoaPods/Specs.git'
29 | platform :ios, '8.0'
30 | use_frameworks!
31 |
32 | target 'MyApp' do
33 | # your other pod
34 | # ...
35 | pod 'SwiftLayoutable'
36 | end
37 | ```
38 | Then, run the following command:
39 |
40 | `$ pod install`
41 |
42 | open the `{Project}.xcworkspace` instead of the `{Project}.xcodeproj` after you installed anything from CocoaPods.
43 |
44 | For more information about how to use CocoaPods, [see this tutorial](http://www.raywenderlich.com/64546/introduction-to-cocoapods-2).
45 |
46 |
47 | [Layoutable](https://github.com/nangege/Layoutable) rely on [Cassowary](https://github.com/nangege/Cassowary), you need to add both of them to your projetc.
48 |
49 |
50 | ### Carthage
51 |
52 |
53 | [Carthage](https://github.com/Carthage/Carthage) is a decentralized dependency manager for Cocoa application. To install the carthage tool, you can use [Homebrew](http://brew.sh).
54 |
55 | ```bash
56 | $ brew update
57 | $ brew install carthage
58 | ```
59 |
60 | To integrate Panda into your Xcode project using Carthage, specify it in your `Cartfile`:
61 |
62 | ```bash
63 | github "https://github.com/nangege/Layoutable" "master"
64 | ```
65 |
66 | Then, run the following command to build the Panda framework:
67 |
68 | ```bash
69 | $ carthage update
70 | ```
71 |
72 | At last, you need to set up your Xcode project manually to add the Panda,Layoutable and Cassowary framework.
73 |
74 | On your application targets’ “General” settings tab, in the “Linked Frameworks and Libraries” section, drag and drop each framework you want to use from the Carthage/Build folder on disk.
75 |
76 | On your application targets’ “Build Phases” settings tab, click the “+” icon and choose “New Run Script Phase”. Create a Run Script with the following content:
77 |
78 | ```bash
79 | /usr/local/bin/carthage copy-frameworks
80 | ```
81 |
82 | and add the paths to the frameworks you want to use under “Input Files”:
83 |
84 | ```bash
85 | $(SRCROOT)/Carthage/Build/iOS/Layoutable.framework
86 | $(SRCROOT)/Carthage/Build/iOS/Cassowary.framework
87 | ```
88 |
89 | For more information about how to use Carthage, please see its [project page](https://github.com/Carthage/Carthage).
90 |
91 | ### Manually
92 |
93 | `git clone git@github.com:nangege/Layoutable.git` ,
94 | `git clone git@github.com:nangege/Cassowary.git`
95 |
96 |
97 | then drag `Layoutable.xcodeproj` and `Cassowary.xcodeproj` file to your projrct
98 |
99 | On your application targets’ “General” settings tab, in the “Linked Frameworks and Libraries” section,add `Layoutable.framework` and `Cassowary.framework`.
100 |
101 |
102 | ## Usage
103 |
104 | 1. define your own Layout Object
105 |
106 | ```swift
107 | import Layoutable
108 |
109 | class TestNode: Layoutable{
110 |
111 | public init() {}
112 |
113 | lazy var layoutManager = LayoutManager(self)
114 |
115 | var layoutSize = CGSize(width: InvalidIntrinsicMetric, height: InvalidIntrinsicMetric)
116 |
117 | weak var superItem: Layoutable? = nil
118 |
119 | var subItems = [Layoutable]()
120 |
121 | func addSubnode(_ node: TestNode){
122 | subItems.append(node)
123 | node.superItem = self
124 | }
125 |
126 | func layoutSubItems() {}
127 |
128 | func updateConstraint() {}
129 |
130 | var layoutRect: CGRect = .zero
131 |
132 | var itemIntrinsicContentSize: CGSize{
133 | return layoutSize
134 | }
135 |
136 | func contentSizeFor(maxWidth: CGFloat) -> CGSize {
137 | return InvaidIntrinsicSize
138 | }
139 |
140 | }
141 |
142 | ```
143 |
144 | 2. use Layout object to Layout
145 |
146 | ```swift
147 | import Layoutable
148 |
149 | // Layout node1 and node2 horizontalally in node,space 10 and equal center in Vertical
150 |
151 | let node = TestNode()
152 | let node1 = TestNode()
153 | let node2 = TestNode()
154 |
155 | node.addSubnode(node1)
156 | node.addSubnode(node2)
157 |
158 | node1.size == (30,30)
159 | node2.size == (40,40)
160 |
161 | [node,node1].equal(.centerY,.left)
162 | [node2,node].equal(.top,.bottom,.centerY,.right)
163 | [node1,node2].space(10, axis: .horizontal)
164 |
165 | node.layoutIfEnabled()
166 |
167 | print(node.frame) // (0.0, 0.0, 80.0, 40.0)
168 | print(node1.frame) // (0.0, 5.0, 30.0, 30.0)
169 | print(node2.frame) // (40.0, 0.0, 40.0, 40.0)
170 |
171 | ```
172 |
173 | ### Operation
174 |
175 | 1. basic attributes
176 |
177 | Like Autolayout, Layoutable support both Equal, lessThanOrEqual and greatThanOrEqualTo
178 |
179 | ```swift
180 | node1.left.equalTo(node2.left)
181 | node1.top.greatThanOrEqualTo(node2.top)
182 | node1.bottom.lessThanOrEqualTo(node2.bottom)
183 | ```
184 | or
185 |
186 | ```swift
187 | node1.left == node2.left // can bve write as node1.left == node2
188 | node1.top >= node2.top // can bve write as node1.top >= node2
189 | node1.bottom <= node2.bottom // can bve write as node1.bottom <= node2
190 |
191 | ```
192 | 2. composit attribute
193 |
194 | beside basic attribute such as left,right, Layoutable also provide some Composit attribute like size ,xSide,ySide,edge
195 |
196 | ```swift
197 | node1.xSide.equalTo(node2,insets:(10,10))
198 | node1.edge(node2,insets:(5,5,5,5))
199 | node.topLeft.equalTo(node2, offset: (10,5))
200 |
201 | ```
202 | or
203 |
204 | ```swift
205 | node1.xSide == node2.xSide + (10,10)
206 | //node1.xSide == node2.xSide.insets(10)
207 | //node1.xSide == node2.xSide.insets((10,10))
208 |
209 | node1.edge == node2.insets((5,5,5,5))
210 | // node1.edge == node2 + (5,5,5,5)
211 |
212 | node.topLeft == node2.topLeft.offset((10,5))
213 |
214 | ```
215 |
216 | 3. update Priority
217 |
218 | ```swift
219 | node1.width == 100 ~.strong
220 | node1.height == 200 ~ 760.0
221 | ```
222 | 4. update constant
223 |
224 | ```swift
225 | let c = node.left == node2.left + 10
226 | c.constant = 100
227 |
228 | ```
229 |
230 | ## Supported attributes
231 |
232 |
233 | Layoutable | NSLayoutAttribute
234 | ------------------------- | --------------------------
235 | Layoutable.left | NSLayoutAttributeLeft
236 | Layoutable.right | NSLayoutAttributeRight
237 | Layoutable.top | NSLayoutAttributeTop
238 | Layoutable.bottom | NSLayoutAttributeBottom
239 | Layoutable.width | NSLayoutAttributeWidth
240 | Layoutable.height | NSLayoutAttributeHeight
241 | Layoutable.centerX | NSLayoutAttributeCenterX
242 | Layoutable.centerY | NSLayoutAttributeCenterY
243 | Layoutable.size | width and height
244 | Layoutable.center | centerX and centerY
245 | Layoutable.xSide | left and right
246 | Layoutable.ySide | top and bottom
247 | Layoutable.edge | top,left,bottom,right
248 | Layoutable.topLeft | top and left
249 | Layoutable.topRight | top and right
250 | Layoutable.bottomLeft | bottom and left
251 | Layoutable.bottomRight | bottom and right
252 |
253 |
254 | ## Lisence
255 |
256 | The MIT License (MIT)
257 |
258 |
259 |
260 |
261 |
--------------------------------------------------------------------------------
/Layoutable/Sources/LayoutConstraint.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LayoutConstraint.swift
3 | // Cassowary
4 | //
5 | // Created by Tang,Nan(MAD) on 2018/3/20.
6 | // Copyright © 2018年 nange. All rights reserved.
7 | //
8 | /// Permission is hereby granted, free of charge, to any person obtaining a copy
9 | /// of this software and associated documentation files (the "Software"), to deal
10 | /// in the Software without restriction, including without limitation the rights
11 | /// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 | /// copies of the Software, and to permit persons to whom the Software is
13 | /// furnished to do so, subject to the following conditions:
14 | ///
15 | /// The above copyright notice and this permission notice shall be included in
16 | /// all copies or substantial portions of the Software.
17 | ///
18 | /// Notwithstanding the foregoing, you may not use, copy, modify, merge, publish,
19 | /// distribute, sublicense, create a derivative work, and/or sell copies of the
20 | /// Software in any work that is designed, intended, or marketed for pedagogical or
21 | /// instructional purposes related to programming, coding, application development,
22 | /// or information technology. Permission for such use, copying, modification,
23 | /// merger, publication, distribution, sublicensing, creation of derivative works,
24 | /// or sale is expressly withheld.
25 | ///
26 | /// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
27 | /// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
28 | /// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
29 | /// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
30 | /// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
31 | /// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
32 | /// THE SOFTWARE.
33 |
34 | import Cassowary
35 | import CoreFoundation
36 |
37 | public enum LayoutRelation {
38 | case equal
39 | case lessThanOrEqual
40 | case greatThanOrEqual
41 | }
42 |
43 | extension LayoutRelation: CustomDebugStringConvertible{
44 | public var debugDescription: String {
45 | switch self {
46 | case .equal: return "=="
47 | case .lessThanOrEqual: return "<="
48 | case .greatThanOrEqual: return ">="
49 | }
50 | }
51 | }
52 |
53 | public struct LayoutPriority: RawRepresentable,ExpressibleByFloatLiteral{
54 | public init(rawValue: Double) {
55 | self.rawValue = rawValue
56 | }
57 |
58 | public var rawValue: Double
59 |
60 | public init(floatLiteral value: Double) {
61 | rawValue = value
62 | }
63 |
64 | public typealias RawValue = Double
65 |
66 | public typealias FloatLiteralType = Double
67 |
68 | public static let required: LayoutPriority = 1000.0
69 | public static let strong: LayoutPriority = 750.0
70 | public static let medium: LayoutPriority = 250.0
71 | public static let weak: LayoutPriority = 10.0
72 | }
73 |
74 | public enum LayoutAttribute{
75 |
76 | case left
77 |
78 | case right
79 |
80 | case top
81 |
82 | case bottom
83 |
84 | case width
85 |
86 | case height
87 |
88 | case centerX
89 |
90 | case centerY
91 | }
92 |
93 | extension LayoutAttribute: CustomDebugStringConvertible{
94 | public var debugDescription: String {
95 | switch self {
96 | case .left: return "left"
97 | case .right: return "right"
98 | case .top: return "top"
99 | case .bottom: return "bottom"
100 | case .width: return "width"
101 | case .height: return "height"
102 | case .centerX: return "centerX"
103 | case .centerY: return "centerY"
104 | }
105 | }
106 | }
107 |
108 | open class LayoutConstraint{
109 |
110 | public init(firstAnchor: AnchorType,secondAnchor: AnchorType? = nil, relation: LayoutRelation = .equal, multiplier: Value = 1,constant: Value = 0){
111 | self.firstAnchor = firstAnchor
112 | self.secondAnchor = secondAnchor
113 | self.relation = relation
114 | self.constant = constant
115 | self.multiplier = multiplier
116 |
117 | // maybe this code should not be here, need to be fix
118 | firstAnchor.item.layoutManager.translateRectIntoConstraints = false
119 | firstAnchor.item.addConstraint(self)
120 | secondAnchor?.item.layoutManager.pinedConstraints.insert(self)
121 | }
122 |
123 | public let firstAnchor: AnchorType
124 |
125 | public let secondAnchor: AnchorType?
126 |
127 | public let relation: LayoutRelation
128 |
129 | public let multiplier: Value
130 |
131 | open var constant: Value = 0{
132 | didSet{
133 | if let solver = solver , constant != oldValue{
134 | solver.updateConstant(for: constraint, to: Double(constant))
135 | }
136 | }
137 | }
138 |
139 | open var priority: LayoutPriority = .required{
140 | didSet{
141 | if let solver = solver{
142 | try? solver.updateStrength(for: constraint, to: Strength(rawValue: priority.rawValue))
143 | }
144 | }
145 | }
146 |
147 | open var isActive: Bool = false{
148 | didSet{
149 | if oldValue != isActive{
150 | if isActive{
151 | firstAnchor.item.addConstraint(self)
152 | secondAnchor?.item.layoutManager.pinedConstraints.insert(self)
153 | }else{
154 | firstAnchor.item.removeConstraint(self)
155 | secondAnchor?.item.layoutManager.pinedConstraints.remove(self)
156 | try? solver?.remove(constraint: constraint)
157 | }
158 | }
159 | }
160 | }
161 |
162 | fileprivate weak var solver: SimplexSolver? = nil
163 |
164 | // translate LayoutConstraint to Constraint
165 | lazy var constraint: Constraint = {
166 |
167 | var constraint: Constraint
168 | let superItem = firstAnchor.item.commonSuperItem(with: secondAnchor?.item)
169 |
170 | var lhsExpr = firstAnchor.expression(in: superItem)
171 | let rhsExpr = Expression(constant: Double(constant))
172 |
173 | if let secondAnchor = secondAnchor{
174 | rhsExpr += secondAnchor.expression(in: superItem)*Double(multiplier)
175 | }else{
176 | lhsExpr = firstAnchor.expression()
177 | }
178 |
179 | switch relation {
180 | case .equal:
181 | constraint = lhsExpr == rhsExpr
182 | case .greatThanOrEqual:
183 | constraint = lhsExpr >= rhsExpr
184 | case .lessThanOrEqual:
185 | constraint = lhsExpr <= rhsExpr
186 | }
187 |
188 | constraint.strength = Strength(rawValue: priority.rawValue)
189 | constraint.owner = self
190 | return constraint
191 | }()
192 |
193 | }
194 |
195 | extension LayoutConstraint{
196 |
197 | func addToSolver(_ solver: SimplexSolver){
198 | if self.solver === solver{
199 | return
200 | }
201 | self.solver = solver
202 | do{
203 | try solver.add(constraint: constraint)
204 | }catch ConstraintError.requiredFailureWithExplanation(let constraint){
205 | let tips = """
206 | Unable to simultaneously satisfy constraints.
207 | Probably at least one of the constraints in the following list is one you don't want.
208 | Try this:
209 | (1) look at each constraint and try to figure out which you don't expect;
210 | (2) find the code that added the unwanted constraint or constraints and fix it.
211 | """
212 | print(tips)
213 |
214 | for (index,constraint) in constraint.enumerated(){
215 | if let owner = constraint.owner{
216 | print(" \(index + 1). \(String(describing: owner)) " )
217 | }
218 | }
219 |
220 | print("""
221 | Will attempt to recover by breaking constraint
222 | \(self)
223 | \n
224 | """)
225 | }catch{
226 | print(error)
227 | }
228 | }
229 |
230 | public func remove(){
231 | _ = try? solver?.remove(constraint: constraint)
232 | secondAnchor?.item.layoutManager.pinedConstraints.remove(self)
233 | solver = nil
234 | }
235 |
236 | class func active(_ constraints: [LayoutConstraint]){
237 | constraints.forEach{ $0.isActive = true}
238 | }
239 |
240 | @discardableResult
241 | func priority(_ priority: LayoutPriority) -> LayoutConstraint{
242 | self.priority = priority
243 | return self
244 | }
245 | }
246 |
247 | extension LayoutConstraint: Hashable{
248 | public static func ==(lhs: LayoutConstraint, rhs: LayoutConstraint) -> Bool {
249 | return lhs === rhs
250 | }
251 |
252 | public func hash(into hasher: inout Hasher) {
253 | hasher.combine(ObjectIdentifier(self))
254 | }
255 | }
256 |
257 | infix operator ~:TernaryPrecedence
258 |
259 | // syntax surge for setPriority
260 | // item1.left = item2.right + 10 ~ .strong
261 | @discardableResult
262 | public func ~(lhs: LayoutConstraint, rhs: LayoutPriority) -> LayoutConstraint{
263 | lhs.priority = rhs
264 | return lhs
265 | }
266 |
267 | extension LayoutConstraint: CustomStringConvertible{
268 | public var description: String {
269 | let lhsdesc = firstAnchor.debugDescription
270 | var desc = lhsdesc
271 | if let rhsAnchor = self.secondAnchor{
272 | let rhsdesc = rhsAnchor.debugDescription
273 | desc = "\(lhsdesc) \(relation) \(rhsdesc)*\(multiplier) + \(constant)"
274 | }else{
275 | desc = "\(lhsdesc) \(relation) \(constant)"
276 | }
277 | return desc
278 | }
279 | }
280 |
--------------------------------------------------------------------------------
/Layoutable/Sources/LayoutAnchor.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LayoutAnchor.swift
3 | //
4 | // Created by nangezao on 2017/7/19.
5 | // Copyright © 2017年 nange. All rights reserved.
6 | //
7 | /// Permission is hereby granted, free of charge, to any person obtaining a copy
8 | /// of this software and associated documentation files (the "Software"), to deal
9 | /// in the Software without restriction, including without limitation the rights
10 | /// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | /// copies of the Software, and to permit persons to whom the Software is
12 | /// furnished to do so, subject to the following conditions:
13 | ///
14 | /// The above copyright notice and this permission notice shall be included in
15 | /// all copies or substantial portions of the Software.
16 | ///
17 | /// Notwithstanding the foregoing, you may not use, copy, modify, merge, publish,
18 | /// distribute, sublicense, create a derivative work, and/or sell copies of the
19 | /// Software in any work that is designed, intended, or marketed for pedagogical or
20 | /// instructional purposes related to programming, coding, application development,
21 | /// or information technology. Permission for such use, copying, modification,
22 | /// merger, publication, distribution, sublicensing, creation of derivative works,
23 | /// or sale is expressly withheld.
24 | ///
25 | /// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
26 | /// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
27 | /// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
28 | /// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
29 | /// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
30 | /// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
31 | /// THE SOFTWARE.
32 |
33 | import Cassowary
34 | import CoreFoundation
35 |
36 | public protocol AnchorType: AnyObject, CustomDebugStringConvertible{
37 |
38 | var item: Layoutable!{ get set}
39 |
40 | var attribute: LayoutAttribute{ get }
41 | }
42 |
43 | extension AnchorType{
44 | func expression() -> Expression{
45 | return item.layoutManager.variable.expressionFor(attribue: attribute)
46 | }
47 |
48 | func expression(in node: Layoutable?) -> Expression{
49 | let expr = expression()
50 | guard let node = node else {
51 | return expr
52 | }
53 |
54 | expr.earse(node.layoutManager.variable.x)
55 | expr.earse(node.layoutManager.variable.y)
56 |
57 | if node === item {
58 | return expr
59 | }
60 |
61 | assert(item.superItem != nil)
62 | guard var superItem = item.superItem else { return expr }
63 | let xExpr = Expression()
64 | let yExpr = Expression()
65 | while !(superItem === node) {
66 | xExpr += superItem.layoutManager.variable.x
67 | yExpr += superItem.layoutManager.variable.y
68 | assert(superItem.superItem != nil)
69 | superItem = superItem.superItem!
70 | }
71 | let coffeeX = expr.coefficient(for: item.layoutManager.variable.x)
72 | let coffeeY = expr.coefficient(for: item.layoutManager.variable.y)
73 | expr += coffeeX * xExpr
74 | expr += coffeeY * yExpr
75 | return expr
76 | }
77 | }
78 |
79 | extension AnchorType{
80 |
81 | @discardableResult
82 | public func constraint(to anchor: Self? = nil,relation: LayoutRelation, constant: Value = 0) -> LayoutConstraint{
83 | return LayoutConstraint(firstAnchor: self , secondAnchor: anchor, relation: relation, constant:constant)
84 | }
85 |
86 | // These methods return a constraint of the form thisAnchor = otherAnchor + constant.
87 | @discardableResult
88 | public func equalTo(_ anchor: Self? = nil, constant: Value = 0) -> LayoutConstraint{
89 | return constraint(to: anchor, relation: .equal, constant: constant)
90 | }
91 |
92 | @discardableResult
93 | public func greaterThanOrEqualTo(_ anchor: Self? = nil, constant: Value = 0) -> LayoutConstraint{
94 | return constraint(to: anchor, relation: .greatThanOrEqual, constant: constant)
95 | }
96 |
97 | @discardableResult
98 | public func lessThanOrEqualTo(_ anchor: Self? = nil, constant: Value = 0) -> LayoutConstraint{
99 | return constraint(to: anchor, relation: .lessThanOrEqual, constant: constant)
100 | }
101 |
102 | @discardableResult
103 | static public func == (lhs: Self, rhs: Self) -> LayoutConstraint{
104 | return lhs.equalTo(rhs)
105 | }
106 |
107 | @discardableResult
108 | static public func <= (lhs: Self, rhs: Self) -> LayoutConstraint{
109 | return lhs.lessThanOrEqualTo(rhs)
110 | }
111 |
112 | @discardableResult
113 | static public func >= (lhs: Self,rhs: Self) -> LayoutConstraint{
114 | return lhs.greaterThanOrEqualTo(rhs)
115 | }
116 |
117 | @discardableResult
118 | static public func == (lhs: Self, rhs: Value) -> LayoutConstraint{
119 | return lhs.equalTo(constant: rhs)
120 | }
121 |
122 | @discardableResult
123 | static public func <= (lhs: Self, rhs: Value) -> LayoutConstraint{
124 | return lhs.lessThanOrEqualTo(constant: rhs)
125 | }
126 |
127 | @discardableResult
128 | static public func >= (lhs: Self,rhs: Value) -> LayoutConstraint{
129 | return lhs.greaterThanOrEqualTo(constant: rhs)
130 | }
131 | }
132 |
133 | public class Anchor: AnchorType{
134 |
135 | public weak var item: Layoutable!
136 |
137 | public let attribute: LayoutAttribute
138 |
139 | public init(item: Layoutable, attribute: LayoutAttribute){
140 | self.item = item
141 | self.attribute = attribute
142 | }
143 |
144 | public var debugDescription: String{
145 | let identifier = ObjectIdentifier(item).pure
146 | return "\(String(describing: item!))(\(identifier)).\(attribute)"
147 | }
148 | }
149 |
150 | /// Axis-specific subclasses for location anchors: top/bottom, left/right, etc.
151 | final public class XAxisAnchor : Anchor {}
152 |
153 | final public class YAxisAnchor : Anchor {}
154 |
155 | /// This layout anchor subclass is used for sizes (width & height).
156 | final public class DimensionAnchor : Anchor {
157 |
158 | /// These methods return a constraint of the form
159 | /// thisAnchor = otherAnchor * multiplier + constant.
160 | @discardableResult
161 | final public func equalTo(_ anchor: DimensionAnchor? = nil, multiplier m: Value = 1, constant: Value = 0) -> LayoutConstraint{
162 | return LayoutConstraint(firstAnchor: self, secondAnchor: anchor, relation: .equal, multiplier: m, constant: constant)
163 | }
164 |
165 | @discardableResult
166 | final public func greaterThanOrEqualTo(_ anchor: DimensionAnchor? = nil, multiplier m: Value = 1, constant: Value = 0 ) -> LayoutConstraint{
167 | return LayoutConstraint(firstAnchor:self, secondAnchor: anchor, relation:.greatThanOrEqual ,multiplier:m, constant:constant )
168 | }
169 |
170 | @discardableResult
171 | final public func lessThanOrEqualTo(_ anchor: DimensionAnchor? = nil, multiplier m: Value = 1, constant: Value = 0) -> LayoutConstraint{
172 | return LayoutConstraint(firstAnchor:self, secondAnchor: anchor,relation:.lessThanOrEqual ,multiplier:m, constant:constant )
173 | }
174 | }
175 |
176 | final public class LayoutExpression{
177 | var anchor: AnchorType
178 | var value: Value
179 | var multiplier: Value = 1
180 |
181 | init(anchor: AnchorType, multiplier: Value = 1, offset: Value = 0) {
182 | self.anchor = anchor
183 | self.multiplier = multiplier
184 | self.value = offset
185 | }
186 | }
187 |
188 | @discardableResult
189 | public func + (lhs: AnchorType, rhs: Value) -> LayoutExpression{
190 | return LayoutExpression(anchor: lhs, offset: rhs)
191 | }
192 |
193 | @discardableResult
194 | public func - (lhs: AnchorType, rhs: Value) -> LayoutExpression{
195 | return lhs + (-rhs)
196 | }
197 |
198 | @discardableResult
199 | public func == (lhs: AnchorType, rhs: LayoutExpression) -> LayoutConstraint{
200 | return lhs.equalTo(rhs.anchor, constant: rhs.value)
201 | }
202 |
203 | @discardableResult
204 | public func >= (lhs: AnchorType, rhs: LayoutExpression) -> LayoutConstraint{
205 | return lhs.greaterThanOrEqualTo(rhs.anchor, constant: rhs.value)
206 | }
207 |
208 | @discardableResult
209 | public func <= (lhs: AnchorType, rhs: LayoutExpression) -> LayoutConstraint{
210 | return lhs.lessThanOrEqualTo(rhs.anchor, constant: rhs.value)
211 | }
212 |
213 | /// LayoutDimession
214 | @discardableResult
215 | public func + (lhs: LayoutExpression, rhs: Value) -> LayoutExpression{
216 | lhs.value += rhs
217 | return lhs
218 | }
219 |
220 | @discardableResult
221 | public func - (lhs: LayoutExpression, rhs: Value) -> LayoutExpression{
222 | return lhs + (-rhs)
223 | }
224 |
225 | @discardableResult
226 | public func * (lhs: DimensionAnchor, rhs: Value) -> LayoutExpression{
227 | return LayoutExpression(anchor: lhs, multiplier: rhs)
228 | }
229 |
230 | @discardableResult
231 | public func / (lhs: DimensionAnchor, rhs: Value) -> LayoutExpression{
232 | return lhs * (1/rhs)
233 | }
234 |
235 | @discardableResult
236 | public func == (lhs: DimensionAnchor, rhs: LayoutExpression) -> LayoutConstraint{
237 | return lhs.equalTo(rhs.anchor, multiplier: rhs.multiplier, constant: rhs.value)
238 | }
239 |
240 | @discardableResult
241 | public func >= (lhs: DimensionAnchor, rhs: LayoutExpression) -> LayoutConstraint{
242 | return lhs.greaterThanOrEqualTo(rhs.anchor, multiplier: rhs.multiplier, constant: rhs.value)
243 | }
244 |
245 | @discardableResult
246 | public func <= (lhs: DimensionAnchor, rhs: LayoutExpression) -> LayoutConstraint{
247 | return lhs.lessThanOrEqualTo( rhs.anchor, multiplier: rhs.multiplier, constant: rhs.value)
248 | }
249 |
250 | extension ObjectIdentifier{
251 | var pure: String{
252 | return String(debugDescription.split(separator: "(")[1].split(separator: ")")[0])
253 | }
254 | }
255 |
--------------------------------------------------------------------------------
/Layoutable/Sources/CompositAnchor.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CompositAnchor.swift
3 | // Layout
4 | //
5 | // Created by nangezao on 2018/8/24.
6 | // Copyright © 2018 Tang Nan. All rights reserved.
7 | //
8 | /// Permission is hereby granted, free of charge, to any person obtaining a copy
9 | /// of this software and associated documentation files (the "Software"), to deal
10 | /// in the Software without restriction, including without limitation the rights
11 | /// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 | /// copies of the Software, and to permit persons to whom the Software is
13 | /// furnished to do so, subject to the following conditions:
14 | ///
15 | /// The above copyright notice and this permission notice shall be included in
16 | /// all copies or substantial portions of the Software.
17 | ///
18 | /// Notwithstanding the foregoing, you may not use, copy, modify, merge, publish,
19 | /// distribute, sublicense, create a derivative work, and/or sell copies of the
20 | /// Software in any work that is designed, intended, or marketed for pedagogical or
21 | /// instructional purposes related to programming, coding, application development,
22 | /// or information technology. Permission for such use, copying, modification,
23 | /// merger, publication, distribution, sublicensing, creation of derivative works,
24 | /// or sale is expressly withheld.
25 | ///
26 | /// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
27 | /// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
28 | /// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
29 | /// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
30 | /// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
31 | /// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
32 | /// THE SOFTWARE.
33 |
34 | import CoreFoundation
35 |
36 | public enum LayoutAxis {
37 | case horizontal
38 | case vertical
39 | }
40 |
41 | public class CompositAnchor {
42 | let item: Layoutable
43 |
44 | public init(item: Layoutable) {
45 | self.item = item
46 | }
47 | }
48 |
49 | final public class SizeAnchor: CompositAnchor {
50 | @discardableResult
51 | public func equalTo(_ size: Size) -> [LayoutConstraint]{
52 | return [
53 | item.width == size.width,
54 | item.height == size.height
55 | ]
56 | }
57 |
58 | @discardableResult
59 | public func equalTo(_ layoutItem: Layoutable, offset: Offset = ( 0, 0)) -> [LayoutConstraint]{
60 | return [
61 | item.width == layoutItem.width + offset.x,
62 | item.height == layoutItem.height + offset.y
63 | ]
64 | }
65 |
66 | @discardableResult
67 | public static func == (lhs: SizeAnchor, rhs: Size) -> [LayoutConstraint]{
68 | return lhs.equalTo(rhs)
69 | }
70 | }
71 |
72 | typealias PositionAttributes = (x: LayoutAttribute, y: LayoutAttribute)
73 |
74 | final public class PositionAnchor: CompositAnchor{
75 |
76 | let attributes: PositionAttributes
77 |
78 | init(item: Layoutable, attributes: PositionAttributes) {
79 | self.attributes = attributes
80 | super.init(item: item)
81 | }
82 |
83 | public func offset(_ offset: Offset) -> CompositExpression{
84 | return self + offset
85 | }
86 |
87 | @discardableResult
88 | public func equalTo(_ anchor: PositionAnchor, offset: Offset = (0,0)) -> [LayoutConstraint]{
89 |
90 | let x = XAxisAnchor(item: item,
91 | attribute: attributes.x) ==
92 | XAxisAnchor(item: anchor.item,
93 | attribute: anchor.attributes.x) + offset.x
94 |
95 | let y = YAxisAnchor(item: item,
96 | attribute: attributes.y) ==
97 | YAxisAnchor(item: anchor.item,
98 | attribute: anchor.attributes.y) + offset.y
99 |
100 | return [x,y]
101 | }
102 |
103 | @discardableResult
104 | public func equalTo(_ layoutItem: Layoutable, offset: Offset = (0,0)) -> [LayoutConstraint]{
105 | return equalTo(PositionAnchor(item: layoutItem, attributes: attributes),
106 | offset: offset)
107 | }
108 |
109 | @discardableResult
110 | static public func == (lhs: PositionAnchor, rhs: Layoutable) -> [LayoutConstraint]{
111 | return lhs.equalTo(rhs)
112 | }
113 |
114 | @discardableResult
115 | static public func == (lhs: PositionAnchor, rhs: PositionAnchor) -> [LayoutConstraint]{
116 | return lhs.equalTo(rhs)
117 | }
118 | }
119 |
120 | final public class XSideAnchor: CompositAnchor{
121 |
122 | @discardableResult
123 | public func equalTo(_ layoutItem: Layoutable, insets: XSideInsets = (0,0)) -> [LayoutConstraint]{
124 | return [
125 | item.left == layoutItem.left + insets.left,
126 | item.right == layoutItem.right - insets.right
127 | ]
128 | }
129 |
130 | @discardableResult
131 | public func equalTo(_ anchor: XSideAnchor, insets: XSideInsets = (0,0)) -> [LayoutConstraint]{
132 | return equalTo(anchor.item, insets: insets)
133 | }
134 |
135 | @discardableResult
136 | public func equalTo(_ layoutItem: Layoutable, insets: Value) -> [LayoutConstraint]{
137 | return equalTo(layoutItem, insets: (insets, insets))
138 | }
139 |
140 | public func insets(_ inset: XSideInsets) -> CompositExpression{
141 | return self + inset
142 | }
143 |
144 | public func insets(_ inset: Value) -> CompositExpression{
145 | return self + inset
146 | }
147 |
148 | @discardableResult
149 | static public func == (lhs: XSideAnchor, rhs: Layoutable) -> [LayoutConstraint]{
150 | return lhs.equalTo(rhs)
151 | }
152 |
153 | }
154 |
155 | final public class YSideAnchor: CompositAnchor{
156 | @discardableResult
157 | public func equalTo(_ layoutItem: Layoutable, insets: YSideInsets = (0, 0)) -> [LayoutConstraint]{
158 | return [
159 | item.top == layoutItem.top + insets.top,
160 | item.bottom == layoutItem.bottom - insets.bottom
161 | ]
162 | }
163 |
164 | @discardableResult
165 | public func equalTo(_ anchor: YSideAnchor, insets: YSideInsets = (0, 0)) -> [LayoutConstraint]{
166 | return equalTo(anchor.item, insets: insets)
167 | }
168 |
169 | @discardableResult
170 | public func equalTo(_ layoutItem: Layoutable, insets: Value) -> [LayoutConstraint]{
171 | return equalTo(layoutItem, insets: (insets, insets))
172 | }
173 |
174 | public func insets(_ inset: YSideInsets) -> CompositExpression{
175 | return self + inset
176 | }
177 |
178 | public func insets(_ inset: Value) -> CompositExpression{
179 | return self + inset
180 | }
181 |
182 | @discardableResult
183 | static public func == (lhs: YSideAnchor, rhs: Layoutable) -> [LayoutConstraint]{
184 | return lhs.equalTo(rhs)
185 | }
186 | }
187 |
188 | final public class EdgeAnchor: CompositAnchor{
189 |
190 | public func insets(_ insets: EdgeInsets) -> CompositExpression{
191 | return self + insets
192 | }
193 |
194 | @discardableResult
195 | public func equalTo(_ layoutItem: Layoutable, insets: EdgeInsets = EdgeInsetsZero) -> [LayoutConstraint]{
196 | return [
197 | item.top == layoutItem.top + insets.top,
198 | item.left == layoutItem.left + insets.left,
199 | item.bottom == layoutItem.bottom - insets.bottom,
200 | item.right == layoutItem.right - insets.right
201 | ]
202 | }
203 |
204 | @discardableResult
205 | static public func == (lhs: EdgeAnchor, rhs: Layoutable) -> [LayoutConstraint]{
206 | return lhs.equalTo(rhs)
207 | }
208 | }
209 |
210 | final public class CompositExpression{
211 | var anchor: AnchorType
212 | var value: valueType
213 |
214 | init(anchor: AnchorType, value: valueType) {
215 | self.anchor = anchor
216 | self.value = value
217 | }
218 | }
219 |
220 | @discardableResult
221 | public func + (lhs: Layoutable, rhs: ValueType) -> CompositExpression {
222 | return CompositExpression(anchor: lhs, value: rhs)
223 | }
224 |
225 | @discardableResult
226 | public func + (lhs: AnchorType, rhs: ValueType) -> CompositExpression {
227 | return CompositExpression(anchor: lhs, value: rhs)
228 | }
229 |
230 | @discardableResult
231 | public func == (lhs: SizeAnchor, rhs: CompositExpression) -> [LayoutConstraint]{
233 | return lhs.equalTo(rhs.anchor, offset: rhs.value)
234 | }
235 |
236 | @discardableResult
237 | public func == (lhs: PositionAnchor, rhs: CompositExpression) -> [LayoutConstraint]{
238 | return lhs.equalTo(rhs.anchor, offset: rhs.value)
239 | }
240 |
241 | @discardableResult
242 | public func == (lhs: PositionAnchor, rhs: CompositExpression) -> [LayoutConstraint]{
243 | return lhs.equalTo(rhs.anchor, offset: rhs.value)
244 | }
245 |
246 | @discardableResult
247 | public func == (lhs: XSideAnchor, rhs: CompositExpression) -> [LayoutConstraint]{
248 | return lhs.equalTo(rhs.anchor, insets: rhs.value)
249 | }
250 |
251 | @discardableResult
252 | public func == (lhs: XSideAnchor, rhs: CompositExpression) -> [LayoutConstraint]{
253 | return lhs.equalTo(rhs.anchor, insets: rhs.value)
254 | }
255 |
256 | @discardableResult
257 | public func == (lhs: XSideAnchor, rhs: CompositExpression) -> [LayoutConstraint]{
258 | return lhs.equalTo(rhs.anchor.item, insets: rhs.value)
259 | }
260 |
261 | @discardableResult
262 | public func == (lhs: XSideAnchor, rhs: CompositExpression) -> [LayoutConstraint]{
263 | return lhs.equalTo(rhs.anchor, insets: rhs.value)
264 | }
265 |
266 | @discardableResult
267 | public func == (lhs: YSideAnchor, rhs: CompositExpression) -> [LayoutConstraint]{
268 | return lhs.equalTo(rhs.anchor, insets: rhs.value)
269 | }
270 |
271 | @discardableResult
272 | public func == (lhs: YSideAnchor, rhs: CompositExpression) -> [LayoutConstraint]{
273 | return lhs.equalTo(rhs.anchor, insets: rhs.value)
274 | }
275 |
276 | @discardableResult
277 | public func == (lhs: YSideAnchor, rhs: CompositExpression) -> [LayoutConstraint]{
278 | return lhs.equalTo(rhs.anchor.item, insets: rhs.value)
279 | }
280 |
281 | @discardableResult
282 | public func == (lhs: YSideAnchor, rhs: CompositExpression) -> [LayoutConstraint]{
283 | return lhs.equalTo(rhs.anchor, insets: rhs.value)
284 | }
285 |
286 | @discardableResult
287 | public func == (lhs: EdgeAnchor, rhs: CompositExpression) -> [LayoutConstraint]{
288 | return lhs.equalTo(rhs.anchor, insets: rhs.value)
289 | }
290 |
291 | @discardableResult
292 | public func == (lhs: EdgeAnchor, rhs: CompositExpression) -> [LayoutConstraint]{
293 | return lhs.equalTo(rhs.anchor.item, insets: rhs.value)
294 | }
295 |
296 | public extension Array where Element: Layoutable{
297 |
298 | typealias LayoutAction = (_ pre: Layoutable, _ current: Layoutable) -> (LayoutConstraint)
299 |
300 | @discardableResult
301 | func traverse(action: LayoutAction) -> [LayoutConstraint]{
302 | var constraints = [LayoutConstraint]()
303 |
304 | guard var preItem = self.first else { return constraints }
305 | for item in self{
306 | if item !== preItem{
307 | constraints.append(action(preItem, item))
308 | }
309 | preItem = item
310 | }
311 | return constraints
312 | }
313 |
314 | @discardableResult
315 | func space(_ space: Value,axis: LayoutAxis = .vertical) -> [LayoutConstraint]{
316 | return traverse { (preItem, currentItem) -> (LayoutConstraint) in
317 | switch axis{
318 | case .horizontal: return currentItem.left == preItem.right + space
319 | case .vertical: return currentItem.top == preItem.bottom + space
320 | }
321 | }
322 | }
323 |
324 | @discardableResult
325 | func equal(_ attributes: LayoutAttribute...) -> [LayoutConstraint]{
326 | var constraints = [LayoutConstraint]()
327 | for attribute in attributes{
328 | constraints.append(contentsOf:traverse { (preItem, current) -> (LayoutConstraint) in
329 | let firstAnchor = Anchor(item: current, attribute: attribute)
330 | let secondAnchor = Anchor(item: preItem, attribute: attribute)
331 | return firstAnchor.equalTo(secondAnchor)
332 | } )
333 | }
334 | return constraints
335 | }
336 |
337 | }
338 |
339 |
340 |
--------------------------------------------------------------------------------
/Layoutable/Sources/Layoutable.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ConstraintAble.swift
3 | // Cassowary
4 | //
5 | // Created by Tang,Nan(MAD) on 2018/4/2.
6 | // Copyright © 2018年 nange. All rights reserved.
7 | //
8 | /// Permission is hereby granted, free of charge, to any person obtaining a copy
9 | /// of this software and associated documentation files (the "Software"), to deal
10 | /// in the Software without restriction, including without limitation the rights
11 | /// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 | /// copies of the Software, and to permit persons to whom the Software is
13 | /// furnished to do so, subject to the following conditions:
14 | ///
15 | /// The above copyright notice and this permission notice shall be included in
16 | /// all copies or substantial portions of the Software.
17 | ///
18 | /// Notwithstanding the foregoing, you may not use, copy, modify, merge, publish,
19 | /// distribute, sublicense, create a derivative work, and/or sell copies of the
20 | /// Software in any work that is designed, intended, or marketed for pedagogical or
21 | /// instructional purposes related to programming, coding, application development,
22 | /// or information technology. Permission for such use, copying, modification,
23 | /// merger, publication, distribution, sublicensing, creation of derivative works,
24 | /// or sale is expressly withheld.
25 | ///
26 | /// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
27 | /// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
28 | /// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
29 | /// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
30 | /// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
31 | /// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
32 | /// THE SOFTWARE.
33 |
34 | import Cassowary
35 | import CoreFoundation
36 |
37 | /// abstraction for constraint layout Item
38 | /// any object conform to Layoutable can use constraint to caculate frame
39 | public protocol Layoutable: AnyObject {
40 |
41 | var layoutManager: LayoutManager{ get }
42 |
43 | var superItem: Layoutable? { get }
44 | var subItems: [Layoutable]{ get }
45 | var layoutRect: CGRect { get set}
46 |
47 | /// like layoutSubviews in UIView
48 | /// this method will be called after layout pass
49 | /// frame of this item is determined
50 | func layoutSubItems()
51 |
52 | /// override point for add new constraint
53 | /// do not use this if not needed
54 | func updateConstraint()
55 |
56 | /// contentSize of node like,just like intrinsicContentSize in UIKit
57 | var itemIntrinsicContentSize: CGSize { get }
58 |
59 | /// contentSize of node, unlike intrinsicContentSize, this time width of node is determined
60 | /// this method is used to adjust height of this item
61 | /// such as text item, if numberOfLines is 0, we need maxWidth to determine number of lines and text height
62 | /// - Parameter maxWidth: maxWidth of this node
63 | /// - Returns: size of content
64 | func contentSizeFor(maxWidth: Value) -> CGSize
65 | }
66 |
67 | // public function
68 | extension Layoutable{
69 |
70 | public var allConstraints: [LayoutConstraint]{
71 | return Array(layoutManager.installedConstraints) + Array(layoutManager.pinedConstraints)
72 | }
73 |
74 | public var fixedWidth: Bool{
75 | set{ layoutManager.fixedWidth = newValue }
76 | get{ return layoutManager.fixedWidth }
77 | }
78 |
79 | public var layoutNeedsUpdate: Bool{
80 | set{ layoutManager.layoutNeedsUpdate = newValue}
81 | get{ return layoutManager.layoutNeedsUpdate }
82 | }
83 |
84 | /// disable cassowary Layout Enginer
85 | /// - Parameter disable: if set to true, all cassowary related code will return immediately
86 | /// it is useful when you want to use cached frame to Layout node, rather than caculate again
87 | public func disableLayout(_ disable: Bool = true){
88 | layoutManager.enabled = !disable
89 | subItems.forEach{ $0.disableLayout(disable)}
90 | }
91 |
92 | /// just like layutIfNeeded in UIView
93 | /// call this method will caculate and update frame immediately
94 | /// be careful, don't call this if layout hierarchy is not ready
95 | public func layoutIfEnabled(){
96 |
97 | if !layoutManager.enabled{ return }
98 |
99 | let item = ancestorItem
100 |
101 | /// for newly added Layoutable object
102 | /// if item is added after a layout pass,we need to add constraints to solver
103 | if let solver = item.layoutManager.solver{
104 | item.updateSolverIfNeeded(solver)
105 | }else{
106 | let solver = LayoutEngine.solveFor(item)
107 | solver.autoSolve = false
108 | item.addConstraintsTo(solver)
109 | try? solver.solve()
110 | solver.autoSolve = true
111 | }
112 | item.layoutFirstPass()
113 | item.layoutSecondPass()
114 | updateLayout()
115 | }
116 |
117 | /// layout info of the current node hierarchy
118 | /// provide for case of layout cache
119 | public var layoutValues: LayoutValues{
120 | var cache = LayoutValues()
121 | if layoutManager.isConstraintValidRect{
122 | cache.frame = layoutManager.layoutRect
123 | }else{
124 | cache.frame = layoutRect
125 | }
126 | cache.subLayout = subItems.map{ $0.layoutValues }
127 | return cache
128 | }
129 |
130 | /// layout node hierarchy with frame
131 | ///
132 | /// - Parameter layout: layout hierarchy from this root node
133 | /// - make sure node hierarchy is exactly the same when you get this layoutValues
134 | public func apply(_ layout: LayoutValues){
135 | layoutRect = layout.frame
136 | for (index, node) in subItems.enumerated(){
137 | node.apply(layout.subLayout[index])
138 | }
139 | }
140 |
141 |
142 | /// used for cleaning constraints for removed item
143 | // this function will remove all constraints for this item and it's subitems from current solver
144 | /// and break all constraints with item's supernode
145 | /// - Parameter item: item from which to break
146 | public func recursivelyReset(from item: Layoutable){
147 | layoutManager.solver = nil
148 | while let constraint = layoutManager.installedConstraints.popFirst() {
149 | constraint.remove()
150 | if let secondItem = constraint.secondAnchor?.item{
151 | if secondItem.ancestorItem === item{
152 | addConstraint(constraint)
153 | secondItem.layoutManager.pinedConstraints.insert(constraint)
154 | }
155 | }else{
156 | addConstraint(constraint)
157 | }
158 | }
159 |
160 | let pinnedToBeRemoved = layoutManager.pinedConstraints.filter{ $0.firstAnchor.item.ancestorItem !== item }
161 | pinnedToBeRemoved.forEach{ $0.remove() }
162 | subItems.forEach{ $0.recursivelyReset(from: item)}
163 | }
164 |
165 | public func setContentHuggingPriorty(for axis: LayoutAxis, priorty: LayoutPriority){
166 | switch axis {
167 | case .horizontal:
168 | layoutManager.contentSizeConstraints.xAxis.huggingPriorty = priorty
169 | case .vertical:
170 | layoutManager.contentSizeConstraints.yAxis.huggingPriorty = priorty
171 | }
172 | }
173 |
174 | public func contentHuggingPriorty(for axis: LayoutAxis) -> LayoutPriority{
175 | switch axis {
176 | case .horizontal:
177 | return layoutManager.contentSizeConstraints.xAxis.huggingPriorty
178 | case .vertical:
179 | return layoutManager.contentSizeConstraints.yAxis.huggingPriorty
180 | }
181 | }
182 |
183 | public func setContentCompressionPriorty(for axis: LayoutAxis, priorty: LayoutPriority){
184 | switch axis {
185 | case .horizontal:
186 | layoutManager.contentSizeConstraints.xAxis.compressionPriorty = priorty
187 | case .vertical:
188 | layoutManager.contentSizeConstraints.yAxis.compressionPriorty = priorty
189 | }
190 | }
191 |
192 | public func contentCompressionPriorty(for axis: LayoutAxis) -> LayoutPriority{
193 | switch axis {
194 | case .horizontal:
195 | return layoutManager.contentSizeConstraints.xAxis.compressionPriorty
196 | case .vertical:
197 | return layoutManager.contentSizeConstraints.yAxis.compressionPriorty
198 | }
199 | }
200 | }
201 |
202 | // MARK: - public property
203 | extension Layoutable{
204 | public var left: XAxisAnchor{
205 | return XAxisAnchor(item: self, attribute: .left)
206 | }
207 |
208 | public var right: XAxisAnchor{
209 | return XAxisAnchor(item: self, attribute: .right)
210 | }
211 |
212 | public var top: YAxisAnchor{
213 | return YAxisAnchor(item: self, attribute: .top)
214 | }
215 |
216 | public var bottom: YAxisAnchor{
217 | return YAxisAnchor(item: self, attribute: .bottom)
218 | }
219 |
220 | public var width: DimensionAnchor{
221 | return DimensionAnchor(item: self, attribute:.width)
222 | }
223 |
224 | public var height: DimensionAnchor{
225 | return DimensionAnchor(item: self, attribute:.height)
226 | }
227 |
228 | public var centerX: XAxisAnchor{
229 | return XAxisAnchor(item: self, attribute: .centerX)
230 | }
231 |
232 | public var centerY: YAxisAnchor{
233 | return YAxisAnchor(item: self, attribute: .centerY)
234 | }
235 |
236 | public var size: SizeAnchor{
237 | return SizeAnchor(item: self)
238 | }
239 |
240 | public var center: PositionAnchor{
241 | return PositionAnchor(item: self, attributes: (.centerX,.centerY))
242 | }
243 |
244 | public var topLeft: PositionAnchor{
245 | return PositionAnchor(item: self, attributes: (.top,.left))
246 | }
247 |
248 | public var topRight: PositionAnchor{
249 | return PositionAnchor(item: self, attributes: (.top,.right))
250 | }
251 |
252 | public var bottomLeft: PositionAnchor{
253 | return PositionAnchor(item: self, attributes: (.bottom,.left))
254 | }
255 |
256 | public var bottomRight: PositionAnchor{
257 | return PositionAnchor(item: self, attributes: (.bottom,.right))
258 | }
259 |
260 | /// left and right
261 | public var xSide: XSideAnchor{
262 | return XSideAnchor(item: self)
263 | }
264 |
265 | /// top and bottom
266 | public var ySide: YSideAnchor{
267 | return YSideAnchor(item: self)
268 | }
269 |
270 | /// top, left, right, bottom
271 | public var edge: EdgeAnchor{
272 | return EdgeAnchor(item: self)
273 | }
274 |
275 | }
276 |
277 |
278 | // MARK: - internal function
279 | extension Layoutable{
280 |
281 | func addConstraint(_ constraint: LayoutConstraint){
282 | layoutManager.addConstraint(constraint)
283 | }
284 |
285 | func removeConstraint(_ constraint: LayoutConstraint){
286 | layoutManager.removeConstraint(constraint)
287 | }
288 |
289 | func removeConstraints(_ constraints: [LayoutConstraint]){
290 | constraints.forEach {
291 | self.removeConstraint($0)
292 | }
293 | }
294 |
295 | /// find common super item with another LayoutItem
296 | /// - Parameter item: item to find common superNode with
297 | /// - Returns: first super node for self and node
298 | func commonSuperItem(with item: Layoutable?) -> Layoutable?{
299 |
300 | guard let item = item else{
301 | return self
302 | }
303 |
304 | var depth1 = depth
305 | var depth2 = item.depth
306 |
307 | var superItem1: Layoutable = self
308 | var superItem2 = item
309 |
310 | while depth1 > depth2 {
311 | superItem1 = superItem1.superItem!
312 | depth1 -= 1
313 | }
314 |
315 | while depth2 > depth1 {
316 | superItem2 = superItem2.superItem!
317 | depth2 -= 1
318 | }
319 |
320 | while !(superItem1 === superItem2) {
321 | if superItem1.superItem == nil{
322 | return nil
323 | }
324 | superItem1 = superItem1.superItem!
325 | superItem2 = superItem2.superItem!
326 | }
327 |
328 | return superItem1
329 | }
330 |
331 | }
332 |
333 | // MARK: - private function
334 | extension Layoutable{
335 |
336 | private var depth: Int{
337 | if let item = superItem{
338 | return item.depth + 1
339 | }else{
340 | return 1
341 | }
342 | }
343 |
344 | private func updateLayout(){
345 | // need to be optimized
346 | if layoutManager.isConstraintValidRect{
347 | layoutRect = layoutManager.layoutRect
348 | }
349 | subItems.forEach{ $0.updateLayout() }
350 | }
351 |
352 | private func addConstraintsTo(_ solver: SimplexSolver){
353 | layoutManager.addConstraintsTo(solver)
354 | subItems.forEach { $0.addConstraintsTo(solver) }
355 | }
356 |
357 | private var ancestorItem: Layoutable{
358 | if let superItem = superItem{
359 | return superItem.ancestorItem
360 | }
361 | return self
362 | }
363 |
364 | private func updateAllConstraint(){
365 | updateConstraint()
366 | layoutManager.updateConstraint()
367 | }
368 |
369 | private func updateSolverIfNeeded(_ solver: SimplexSolver){
370 | if layoutManager.solver !== solver{
371 | addConstraintsTo(solver)
372 | return
373 | }
374 | subItems.forEach{ $0.updateSolverIfNeeded(solver)}
375 | }
376 |
377 | private func layoutFirstPass(){
378 | if layoutNeedsUpdate{
379 | if !layoutManager.translateRectIntoConstraints{
380 | var size = CGSize(width: InvalidIntrinsicMetric, height: 0)
381 | if !layoutManager.fixedWidth{
382 | size = itemIntrinsicContentSize
383 | }
384 | layoutManager.updateSize(size)
385 | }
386 | }else if layoutManager.isRectConstrainted{
387 | layoutManager.updateRect(layoutRect)
388 | /// a little weird here, when update size or origin,some constraints will be add to this item
389 | /// this item's translateRectIntoConstraints will be set to false
390 | /// correct it here. need a better way.
391 | layoutManager.translateRectIntoConstraints = true
392 | }
393 | updateAllConstraint()
394 | subItems.forEach { $0.layoutFirstPass() }
395 | }
396 |
397 | /// second layout pass is used to adjust contentSize height
398 | /// such as TextNode,at this time ,width for textNode is determined
399 | /// so we can know how manay lines this text should have
400 | private func layoutSecondPass(){
401 | if layoutManager.sizeNeedsUpdate && !layoutManager.translateRectIntoConstraints{
402 | var size = contentSizeFor(maxWidth: layoutManager.layoutRect.size.width)
403 | if layoutManager.fixedWidth{
404 | size = CGSize(width: InvalidIntrinsicMetric, height: size.height)
405 | }
406 |
407 | if size != InvaidIntrinsicSize{
408 | layoutManager.updateSize(size)
409 | }
410 | }
411 |
412 | subItems.forEach{ $0.layoutSecondPass()}
413 | layoutNeedsUpdate = false
414 | }
415 |
416 | }
417 |
--------------------------------------------------------------------------------
/Layoutable.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 50;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 3806BA17214F628E00D6981F /* Cassowary.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 380AD772214F603300C94CDD /* Cassowary.framework */; };
11 | 3806BA1A214F635100D6981F /* Cassowary.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 380AD772214F603300C94CDD /* Cassowary.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
12 | 38335FE22154681D0084DC0C /* LayoutManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38335FE12154681D0084DC0C /* LayoutManager.swift */; };
13 | 3858B93B213E76D200CE161E /* TestNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3858B93A213E76D200CE161E /* TestNode.swift */; };
14 | 38C105CA2139919B00993530 /* Layoutable.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 38C105C02139919B00993530 /* Layoutable.framework */; };
15 | 38C105CF2139919B00993530 /* PerformanceTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38C105CE2139919B00993530 /* PerformanceTest.swift */; };
16 | 38C105D12139919B00993530 /* Layoutable.h in Headers */ = {isa = PBXBuildFile; fileRef = 38C105C32139919B00993530 /* Layoutable.h */; settings = {ATTRIBUTES = (Public, ); }; };
17 | 38C105FA2139920800993530 /* CompositAnchor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38C105F32139920800993530 /* CompositAnchor.swift */; };
18 | 38C105FB2139920800993530 /* LayoutEngine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38C105F42139920800993530 /* LayoutEngine.swift */; };
19 | 38C105FC2139920800993530 /* LayoutProperty.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38C105F52139920800993530 /* LayoutProperty.swift */; };
20 | 38C105FD2139920800993530 /* LayoutAnchor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38C105F62139920800993530 /* LayoutAnchor.swift */; };
21 | 38C105FE2139920800993530 /* LayoutValues.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38C105F72139920800993530 /* LayoutValues.swift */; };
22 | 38C105FF2139920800993530 /* Layoutable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38C105F82139920800993530 /* Layoutable.swift */; };
23 | 38C106002139920800993530 /* LayoutConstraint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38C105F92139920800993530 /* LayoutConstraint.swift */; };
24 | 38C1068A213999B500993530 /* Cassowary.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 38C10689213999B500993530 /* Cassowary.framework */; settings = {ATTRIBUTES = (Required, ); }; };
25 | 38D0C35E216E02FD009CD78F /* LayoutTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38D0C35D216E02FD009CD78F /* LayoutTest.swift */; };
26 | /* End PBXBuildFile section */
27 |
28 | /* Begin PBXContainerItemProxy section */
29 | 38C105CB2139919B00993530 /* PBXContainerItemProxy */ = {
30 | isa = PBXContainerItemProxy;
31 | containerPortal = 38C105B72139919B00993530 /* Project object */;
32 | proxyType = 1;
33 | remoteGlobalIDString = 38C105BF2139919B00993530;
34 | remoteInfo = LayoutItemAble;
35 | };
36 | /* End PBXContainerItemProxy section */
37 |
38 | /* Begin PBXCopyFilesBuildPhase section */
39 | 3806BA19214F631D00D6981F /* CopyFiles */ = {
40 | isa = PBXCopyFilesBuildPhase;
41 | buildActionMask = 2147483647;
42 | dstPath = "";
43 | dstSubfolderSpec = 10;
44 | files = (
45 | 3806BA1A214F635100D6981F /* Cassowary.framework in CopyFiles */,
46 | );
47 | runOnlyForDeploymentPostprocessing = 0;
48 | };
49 | /* End PBXCopyFilesBuildPhase section */
50 |
51 | /* Begin PBXFileReference section */
52 | 380AD772214F603300C94CDD /* Cassowary.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cassowary.framework; path = Carthage/Build/iOS/Cassowary.framework; sourceTree = ""; };
53 | 38335FE12154681D0084DC0C /* LayoutManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LayoutManager.swift; sourceTree = ""; };
54 | 3858B93A213E76D200CE161E /* TestNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestNode.swift; sourceTree = ""; };
55 | 38C105C02139919B00993530 /* Layoutable.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Layoutable.framework; sourceTree = BUILT_PRODUCTS_DIR; };
56 | 38C105C32139919B00993530 /* Layoutable.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Layoutable.h; sourceTree = ""; };
57 | 38C105C42139919B00993530 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
58 | 38C105C92139919B00993530 /* LayoutableTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = LayoutableTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
59 | 38C105CE2139919B00993530 /* PerformanceTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PerformanceTest.swift; sourceTree = ""; };
60 | 38C105D02139919B00993530 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
61 | 38C105F32139920800993530 /* CompositAnchor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CompositAnchor.swift; sourceTree = ""; };
62 | 38C105F42139920800993530 /* LayoutEngine.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LayoutEngine.swift; sourceTree = ""; };
63 | 38C105F52139920800993530 /* LayoutProperty.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LayoutProperty.swift; sourceTree = ""; };
64 | 38C105F62139920800993530 /* LayoutAnchor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LayoutAnchor.swift; sourceTree = ""; };
65 | 38C105F72139920800993530 /* LayoutValues.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LayoutValues.swift; sourceTree = ""; };
66 | 38C105F82139920800993530 /* Layoutable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Layoutable.swift; sourceTree = ""; };
67 | 38C105F92139920800993530 /* LayoutConstraint.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LayoutConstraint.swift; sourceTree = ""; };
68 | 38C10689213999B500993530 /* Cassowary.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Cassowary.framework; sourceTree = BUILT_PRODUCTS_DIR; };
69 | 38D0C35D216E02FD009CD78F /* LayoutTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LayoutTest.swift; sourceTree = ""; };
70 | /* End PBXFileReference section */
71 |
72 | /* Begin PBXFrameworksBuildPhase section */
73 | 38C105BD2139919B00993530 /* Frameworks */ = {
74 | isa = PBXFrameworksBuildPhase;
75 | buildActionMask = 2147483647;
76 | files = (
77 | 38C1068A213999B500993530 /* Cassowary.framework in Frameworks */,
78 | );
79 | runOnlyForDeploymentPostprocessing = 0;
80 | };
81 | 38C105C62139919B00993530 /* Frameworks */ = {
82 | isa = PBXFrameworksBuildPhase;
83 | buildActionMask = 2147483647;
84 | files = (
85 | 3806BA17214F628E00D6981F /* Cassowary.framework in Frameworks */,
86 | 38C105CA2139919B00993530 /* Layoutable.framework in Frameworks */,
87 | );
88 | runOnlyForDeploymentPostprocessing = 0;
89 | };
90 | /* End PBXFrameworksBuildPhase section */
91 |
92 | /* Begin PBXGroup section */
93 | 38C105B62139919B00993530 = {
94 | isa = PBXGroup;
95 | children = (
96 | 38C105C22139919B00993530 /* Layoutable */,
97 | 38C105CD2139919B00993530 /* LayoutableTests */,
98 | 38C105C12139919B00993530 /* Products */,
99 | 38C10688213999B500993530 /* Frameworks */,
100 | );
101 | sourceTree = "";
102 | };
103 | 38C105C12139919B00993530 /* Products */ = {
104 | isa = PBXGroup;
105 | children = (
106 | 38C105C02139919B00993530 /* Layoutable.framework */,
107 | 38C105C92139919B00993530 /* LayoutableTests.xctest */,
108 | );
109 | name = Products;
110 | sourceTree = "";
111 | };
112 | 38C105C22139919B00993530 /* Layoutable */ = {
113 | isa = PBXGroup;
114 | children = (
115 | 38C105F22139920800993530 /* Sources */,
116 | 38C105C32139919B00993530 /* Layoutable.h */,
117 | 38C105C42139919B00993530 /* Info.plist */,
118 | );
119 | path = Layoutable;
120 | sourceTree = "";
121 | };
122 | 38C105CD2139919B00993530 /* LayoutableTests */ = {
123 | isa = PBXGroup;
124 | children = (
125 | 38C105CE2139919B00993530 /* PerformanceTest.swift */,
126 | 38D0C35D216E02FD009CD78F /* LayoutTest.swift */,
127 | 3858B93A213E76D200CE161E /* TestNode.swift */,
128 | 38C105D02139919B00993530 /* Info.plist */,
129 | );
130 | path = LayoutableTests;
131 | sourceTree = "";
132 | };
133 | 38C105F22139920800993530 /* Sources */ = {
134 | isa = PBXGroup;
135 | children = (
136 | 38C105F42139920800993530 /* LayoutEngine.swift */,
137 | 38C105F52139920800993530 /* LayoutProperty.swift */,
138 | 38C105F62139920800993530 /* LayoutAnchor.swift */,
139 | 38C105F72139920800993530 /* LayoutValues.swift */,
140 | 38C105F82139920800993530 /* Layoutable.swift */,
141 | 38335FE12154681D0084DC0C /* LayoutManager.swift */,
142 | 38C105F92139920800993530 /* LayoutConstraint.swift */,
143 | 38C105F32139920800993530 /* CompositAnchor.swift */,
144 | );
145 | path = Sources;
146 | sourceTree = "";
147 | };
148 | 38C10688213999B500993530 /* Frameworks */ = {
149 | isa = PBXGroup;
150 | children = (
151 | 380AD772214F603300C94CDD /* Cassowary.framework */,
152 | 38C10689213999B500993530 /* Cassowary.framework */,
153 | );
154 | name = Frameworks;
155 | sourceTree = "";
156 | };
157 | /* End PBXGroup section */
158 |
159 | /* Begin PBXHeadersBuildPhase section */
160 | 38C105BB2139919B00993530 /* Headers */ = {
161 | isa = PBXHeadersBuildPhase;
162 | buildActionMask = 2147483647;
163 | files = (
164 | 38C105D12139919B00993530 /* Layoutable.h in Headers */,
165 | );
166 | runOnlyForDeploymentPostprocessing = 0;
167 | };
168 | /* End PBXHeadersBuildPhase section */
169 |
170 | /* Begin PBXNativeTarget section */
171 | 38C105BF2139919B00993530 /* Layoutable */ = {
172 | isa = PBXNativeTarget;
173 | buildConfigurationList = 38C105D42139919B00993530 /* Build configuration list for PBXNativeTarget "Layoutable" */;
174 | buildPhases = (
175 | 38C105BB2139919B00993530 /* Headers */,
176 | 38C105BC2139919B00993530 /* Sources */,
177 | 38C105BD2139919B00993530 /* Frameworks */,
178 | 38C105BE2139919B00993530 /* Resources */,
179 | );
180 | buildRules = (
181 | );
182 | dependencies = (
183 | );
184 | name = Layoutable;
185 | productName = LayoutItemAble;
186 | productReference = 38C105C02139919B00993530 /* Layoutable.framework */;
187 | productType = "com.apple.product-type.framework";
188 | };
189 | 38C105C82139919B00993530 /* LayoutableTests */ = {
190 | isa = PBXNativeTarget;
191 | buildConfigurationList = 38C105D72139919B00993530 /* Build configuration list for PBXNativeTarget "LayoutableTests" */;
192 | buildPhases = (
193 | 38C105C52139919B00993530 /* Sources */,
194 | 38C105C62139919B00993530 /* Frameworks */,
195 | 38C105C72139919B00993530 /* Resources */,
196 | 3806BA19214F631D00D6981F /* CopyFiles */,
197 | );
198 | buildRules = (
199 | );
200 | dependencies = (
201 | 38C105CC2139919B00993530 /* PBXTargetDependency */,
202 | );
203 | name = LayoutableTests;
204 | productName = LayoutItemAbleTests;
205 | productReference = 38C105C92139919B00993530 /* LayoutableTests.xctest */;
206 | productType = "com.apple.product-type.bundle.unit-test";
207 | };
208 | /* End PBXNativeTarget section */
209 |
210 | /* Begin PBXProject section */
211 | 38C105B72139919B00993530 /* Project object */ = {
212 | isa = PBXProject;
213 | attributes = {
214 | LastSwiftUpdateCheck = 1000;
215 | LastUpgradeCheck = 1000;
216 | ORGANIZATIONNAME = "Tang Nan";
217 | TargetAttributes = {
218 | 38C105BF2139919B00993530 = {
219 | CreatedOnToolsVersion = 10.0;
220 | };
221 | 38C105C82139919B00993530 = {
222 | CreatedOnToolsVersion = 10.0;
223 | };
224 | };
225 | };
226 | buildConfigurationList = 38C105BA2139919B00993530 /* Build configuration list for PBXProject "Layoutable" */;
227 | compatibilityVersion = "Xcode 9.3";
228 | developmentRegion = en;
229 | hasScannedForEncodings = 0;
230 | knownRegions = (
231 | en,
232 | );
233 | mainGroup = 38C105B62139919B00993530;
234 | productRefGroup = 38C105C12139919B00993530 /* Products */;
235 | projectDirPath = "";
236 | projectRoot = "";
237 | targets = (
238 | 38C105BF2139919B00993530 /* Layoutable */,
239 | 38C105C82139919B00993530 /* LayoutableTests */,
240 | );
241 | };
242 | /* End PBXProject section */
243 |
244 | /* Begin PBXResourcesBuildPhase section */
245 | 38C105BE2139919B00993530 /* Resources */ = {
246 | isa = PBXResourcesBuildPhase;
247 | buildActionMask = 2147483647;
248 | files = (
249 | );
250 | runOnlyForDeploymentPostprocessing = 0;
251 | };
252 | 38C105C72139919B00993530 /* Resources */ = {
253 | isa = PBXResourcesBuildPhase;
254 | buildActionMask = 2147483647;
255 | files = (
256 | );
257 | runOnlyForDeploymentPostprocessing = 0;
258 | };
259 | /* End PBXResourcesBuildPhase section */
260 |
261 | /* Begin PBXSourcesBuildPhase section */
262 | 38C105BC2139919B00993530 /* Sources */ = {
263 | isa = PBXSourcesBuildPhase;
264 | buildActionMask = 2147483647;
265 | files = (
266 | 38C105FE2139920800993530 /* LayoutValues.swift in Sources */,
267 | 38C105FA2139920800993530 /* CompositAnchor.swift in Sources */,
268 | 38C105FC2139920800993530 /* LayoutProperty.swift in Sources */,
269 | 38C105FD2139920800993530 /* LayoutAnchor.swift in Sources */,
270 | 38C105FF2139920800993530 /* Layoutable.swift in Sources */,
271 | 38C106002139920800993530 /* LayoutConstraint.swift in Sources */,
272 | 38C105FB2139920800993530 /* LayoutEngine.swift in Sources */,
273 | 38335FE22154681D0084DC0C /* LayoutManager.swift in Sources */,
274 | );
275 | runOnlyForDeploymentPostprocessing = 0;
276 | };
277 | 38C105C52139919B00993530 /* Sources */ = {
278 | isa = PBXSourcesBuildPhase;
279 | buildActionMask = 2147483647;
280 | files = (
281 | 38D0C35E216E02FD009CD78F /* LayoutTest.swift in Sources */,
282 | 38C105CF2139919B00993530 /* PerformanceTest.swift in Sources */,
283 | 3858B93B213E76D200CE161E /* TestNode.swift in Sources */,
284 | );
285 | runOnlyForDeploymentPostprocessing = 0;
286 | };
287 | /* End PBXSourcesBuildPhase section */
288 |
289 | /* Begin PBXTargetDependency section */
290 | 38C105CC2139919B00993530 /* PBXTargetDependency */ = {
291 | isa = PBXTargetDependency;
292 | target = 38C105BF2139919B00993530 /* Layoutable */;
293 | targetProxy = 38C105CB2139919B00993530 /* PBXContainerItemProxy */;
294 | };
295 | /* End PBXTargetDependency section */
296 |
297 | /* Begin XCBuildConfiguration section */
298 | 38C105D22139919B00993530 /* Debug */ = {
299 | isa = XCBuildConfiguration;
300 | buildSettings = {
301 | ALWAYS_SEARCH_USER_PATHS = NO;
302 | CLANG_ANALYZER_NONNULL = YES;
303 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
304 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
305 | CLANG_CXX_LIBRARY = "libc++";
306 | CLANG_ENABLE_MODULES = YES;
307 | CLANG_ENABLE_OBJC_ARC = YES;
308 | CLANG_ENABLE_OBJC_WEAK = YES;
309 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
310 | CLANG_WARN_BOOL_CONVERSION = YES;
311 | CLANG_WARN_COMMA = YES;
312 | CLANG_WARN_CONSTANT_CONVERSION = YES;
313 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
314 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
315 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
316 | CLANG_WARN_EMPTY_BODY = YES;
317 | CLANG_WARN_ENUM_CONVERSION = YES;
318 | CLANG_WARN_INFINITE_RECURSION = YES;
319 | CLANG_WARN_INT_CONVERSION = YES;
320 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
321 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
322 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
323 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
324 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
325 | CLANG_WARN_STRICT_PROTOTYPES = YES;
326 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
327 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
328 | CLANG_WARN_UNREACHABLE_CODE = YES;
329 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
330 | CODE_SIGN_IDENTITY = "iPhone Developer";
331 | COPY_PHASE_STRIP = NO;
332 | CURRENT_PROJECT_VERSION = 1;
333 | DEBUG_INFORMATION_FORMAT = dwarf;
334 | ENABLE_STRICT_OBJC_MSGSEND = YES;
335 | ENABLE_TESTABILITY = YES;
336 | GCC_C_LANGUAGE_STANDARD = gnu11;
337 | GCC_DYNAMIC_NO_PIC = NO;
338 | GCC_NO_COMMON_BLOCKS = YES;
339 | GCC_OPTIMIZATION_LEVEL = 0;
340 | GCC_PREPROCESSOR_DEFINITIONS = (
341 | "DEBUG=1",
342 | "$(inherited)",
343 | );
344 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
345 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
346 | GCC_WARN_UNDECLARED_SELECTOR = YES;
347 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
348 | GCC_WARN_UNUSED_FUNCTION = YES;
349 | GCC_WARN_UNUSED_VARIABLE = YES;
350 | IPHONEOS_DEPLOYMENT_TARGET = 12.0;
351 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
352 | MTL_FAST_MATH = YES;
353 | ONLY_ACTIVE_ARCH = YES;
354 | SDKROOT = iphoneos;
355 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
356 | SWIFT_COMPILATION_MODE = wholemodule;
357 | SWIFT_OPTIMIZATION_LEVEL = "-O";
358 | VERSIONING_SYSTEM = "apple-generic";
359 | VERSION_INFO_PREFIX = "";
360 | };
361 | name = Debug;
362 | };
363 | 38C105D32139919B00993530 /* Release */ = {
364 | isa = XCBuildConfiguration;
365 | buildSettings = {
366 | ALWAYS_SEARCH_USER_PATHS = NO;
367 | CLANG_ANALYZER_NONNULL = YES;
368 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
369 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
370 | CLANG_CXX_LIBRARY = "libc++";
371 | CLANG_ENABLE_MODULES = YES;
372 | CLANG_ENABLE_OBJC_ARC = YES;
373 | CLANG_ENABLE_OBJC_WEAK = YES;
374 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
375 | CLANG_WARN_BOOL_CONVERSION = YES;
376 | CLANG_WARN_COMMA = YES;
377 | CLANG_WARN_CONSTANT_CONVERSION = YES;
378 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
379 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
380 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
381 | CLANG_WARN_EMPTY_BODY = YES;
382 | CLANG_WARN_ENUM_CONVERSION = YES;
383 | CLANG_WARN_INFINITE_RECURSION = YES;
384 | CLANG_WARN_INT_CONVERSION = YES;
385 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
386 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
387 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
388 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
389 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
390 | CLANG_WARN_STRICT_PROTOTYPES = YES;
391 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
392 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
393 | CLANG_WARN_UNREACHABLE_CODE = YES;
394 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
395 | CODE_SIGN_IDENTITY = "iPhone Developer";
396 | COPY_PHASE_STRIP = NO;
397 | CURRENT_PROJECT_VERSION = 1;
398 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
399 | ENABLE_NS_ASSERTIONS = NO;
400 | ENABLE_STRICT_OBJC_MSGSEND = YES;
401 | GCC_C_LANGUAGE_STANDARD = gnu11;
402 | GCC_NO_COMMON_BLOCKS = YES;
403 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
404 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
405 | GCC_WARN_UNDECLARED_SELECTOR = YES;
406 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
407 | GCC_WARN_UNUSED_FUNCTION = YES;
408 | GCC_WARN_UNUSED_VARIABLE = YES;
409 | IPHONEOS_DEPLOYMENT_TARGET = 12.0;
410 | MTL_ENABLE_DEBUG_INFO = NO;
411 | MTL_FAST_MATH = YES;
412 | SDKROOT = iphoneos;
413 | SWIFT_COMPILATION_MODE = wholemodule;
414 | SWIFT_OPTIMIZATION_LEVEL = "-O";
415 | VALIDATE_PRODUCT = YES;
416 | VERSIONING_SYSTEM = "apple-generic";
417 | VERSION_INFO_PREFIX = "";
418 | };
419 | name = Release;
420 | };
421 | 38C105D52139919B00993530 /* Debug */ = {
422 | isa = XCBuildConfiguration;
423 | buildSettings = {
424 | CODE_SIGN_IDENTITY = "";
425 | CODE_SIGN_STYLE = Automatic;
426 | DEBUG_INFORMATION_FORMAT = dwarf;
427 | DEFINES_MODULE = YES;
428 | DEVELOPMENT_TEAM = DPJ43EEV6L;
429 | DYLIB_COMPATIBILITY_VERSION = 1;
430 | DYLIB_CURRENT_VERSION = 1;
431 | DYLIB_INSTALL_NAME_BASE = "@rpath";
432 | FRAMEWORK_SEARCH_PATHS = (
433 | "$(inherited)",
434 | "$(PROJECT_DIR)/Carthage/Build/iOS",
435 | );
436 | INFOPLIST_FILE = Layoutable/Info.plist;
437 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
438 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
439 | LD_RUNPATH_SEARCH_PATHS = (
440 | "$(inherited)",
441 | "@executable_path/Frameworks",
442 | "@loader_path/Frameworks",
443 | );
444 | PRODUCT_BUNDLE_IDENTIFIER = com.nange.Layoutable;
445 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
446 | SKIP_INSTALL = YES;
447 | SWIFT_VERSION = 4.2;
448 | TARGETED_DEVICE_FAMILY = "1,2";
449 | };
450 | name = Debug;
451 | };
452 | 38C105D62139919B00993530 /* Release */ = {
453 | isa = XCBuildConfiguration;
454 | buildSettings = {
455 | CODE_SIGN_IDENTITY = "";
456 | CODE_SIGN_STYLE = Automatic;
457 | DEFINES_MODULE = YES;
458 | DEVELOPMENT_TEAM = DPJ43EEV6L;
459 | DYLIB_COMPATIBILITY_VERSION = 1;
460 | DYLIB_CURRENT_VERSION = 1;
461 | DYLIB_INSTALL_NAME_BASE = "@rpath";
462 | FRAMEWORK_SEARCH_PATHS = (
463 | "$(inherited)",
464 | "$(PROJECT_DIR)/Carthage/Build/iOS",
465 | );
466 | INFOPLIST_FILE = Layoutable/Info.plist;
467 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
468 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
469 | LD_RUNPATH_SEARCH_PATHS = (
470 | "$(inherited)",
471 | "@executable_path/Frameworks",
472 | "@loader_path/Frameworks",
473 | );
474 | PRODUCT_BUNDLE_IDENTIFIER = com.nange.Layoutable;
475 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
476 | SKIP_INSTALL = YES;
477 | SWIFT_VERSION = 4.2;
478 | TARGETED_DEVICE_FAMILY = "1,2";
479 | };
480 | name = Release;
481 | };
482 | 38C105D82139919B00993530 /* Debug */ = {
483 | isa = XCBuildConfiguration;
484 | buildSettings = {
485 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
486 | CODE_SIGN_STYLE = Automatic;
487 | DEVELOPMENT_TEAM = DPJ43EEV6L;
488 | FRAMEWORK_SEARCH_PATHS = (
489 | "$(inherited)",
490 | "$(PROJECT_DIR)/Carthage/Build/iOS",
491 | );
492 | INFOPLIST_FILE = LayoutableTests/Info.plist;
493 | LD_RUNPATH_SEARCH_PATHS = (
494 | "$(inherited)",
495 | "@executable_path/Frameworks",
496 | "@loader_path/Frameworks",
497 | );
498 | PRODUCT_BUNDLE_IDENTIFIER = com.nange.LayoutableTests;
499 | PRODUCT_NAME = "$(TARGET_NAME)";
500 | SWIFT_COMPILATION_MODE = wholemodule;
501 | SWIFT_OPTIMIZATION_LEVEL = "-O";
502 | SWIFT_VERSION = 5.0;
503 | TARGETED_DEVICE_FAMILY = "1,2";
504 | };
505 | name = Debug;
506 | };
507 | 38C105D92139919B00993530 /* Release */ = {
508 | isa = XCBuildConfiguration;
509 | buildSettings = {
510 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
511 | CODE_SIGN_STYLE = Automatic;
512 | DEVELOPMENT_TEAM = DPJ43EEV6L;
513 | FRAMEWORK_SEARCH_PATHS = (
514 | "$(inherited)",
515 | "$(PROJECT_DIR)/Carthage/Build/iOS",
516 | );
517 | INFOPLIST_FILE = LayoutableTests/Info.plist;
518 | LD_RUNPATH_SEARCH_PATHS = (
519 | "$(inherited)",
520 | "@executable_path/Frameworks",
521 | "@loader_path/Frameworks",
522 | );
523 | PRODUCT_BUNDLE_IDENTIFIER = com.nange.LayoutableTests;
524 | PRODUCT_NAME = "$(TARGET_NAME)";
525 | SWIFT_COMPILATION_MODE = wholemodule;
526 | SWIFT_OPTIMIZATION_LEVEL = "-O";
527 | SWIFT_VERSION = 5.0;
528 | TARGETED_DEVICE_FAMILY = "1,2";
529 | };
530 | name = Release;
531 | };
532 | /* End XCBuildConfiguration section */
533 |
534 | /* Begin XCConfigurationList section */
535 | 38C105BA2139919B00993530 /* Build configuration list for PBXProject "Layoutable" */ = {
536 | isa = XCConfigurationList;
537 | buildConfigurations = (
538 | 38C105D22139919B00993530 /* Debug */,
539 | 38C105D32139919B00993530 /* Release */,
540 | );
541 | defaultConfigurationIsVisible = 0;
542 | defaultConfigurationName = Release;
543 | };
544 | 38C105D42139919B00993530 /* Build configuration list for PBXNativeTarget "Layoutable" */ = {
545 | isa = XCConfigurationList;
546 | buildConfigurations = (
547 | 38C105D52139919B00993530 /* Debug */,
548 | 38C105D62139919B00993530 /* Release */,
549 | );
550 | defaultConfigurationIsVisible = 0;
551 | defaultConfigurationName = Release;
552 | };
553 | 38C105D72139919B00993530 /* Build configuration list for PBXNativeTarget "LayoutableTests" */ = {
554 | isa = XCConfigurationList;
555 | buildConfigurations = (
556 | 38C105D82139919B00993530 /* Debug */,
557 | 38C105D92139919B00993530 /* Release */,
558 | );
559 | defaultConfigurationIsVisible = 0;
560 | defaultConfigurationName = Release;
561 | };
562 | /* End XCConfigurationList section */
563 | };
564 | rootObject = 38C105B72139919B00993530 /* Project object */;
565 | }
566 |
--------------------------------------------------------------------------------