├── .github └── workflows │ ├── CocoaPodsRelease.yml │ └── swift.yml ├── .gitignore ├── Demo_MondrianLayout ├── AppDelegate.swift ├── Assets.xcassets │ ├── AccentColor.colorset │ │ └── Contents.json │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── Contents.json ├── Base.lproj │ └── LaunchScreen.storyboard ├── Book.Background.swift ├── Book.Classic.swift ├── Book.HStackBlock.swift ├── Book.LayoutContainer.swift ├── Book.Manager.swift ├── Book.Mondrian.swift ├── Book.Overlay.swift ├── Book.RelativeBlock.swift ├── Book.SafeArea.swift ├── Book.Sizing.swift ├── Book.VGridBlock.swift ├── Book.VStackBlock.swift ├── Book.ViewController.swift ├── Book.ZStackBlock.swift ├── Book.swift ├── CGSize+.swift ├── ExampleView.swift ├── Extensions.swift ├── Info.plist ├── InstagramPostView.swift ├── RootContainerViewController.swift ├── SwiftUIComparison.swift └── UIColor+Mondrian.swift ├── Gemfile ├── Gemfile.lock ├── LICENSE ├── MondrianLayout.podspec ├── MondrianLayout.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── swiftpm │ │ └── Package.resolved └── xcshareddata │ └── xcschemes │ ├── Demo_MondrianLayout.xcscheme │ ├── MondrianLayout.xcscheme │ ├── Tests-Record.xcscheme │ └── Tests.xcscheme ├── MondrianLayout ├── Classic │ ├── LayoutDescriptor.swift │ └── MondrianArrayBuilder.swift ├── Descriptors │ ├── DimensionDescriptor.swift │ ├── Edge.swift │ ├── LayoutBuilderContext.swift │ ├── Optimization.swift │ ├── VHStackContentBuilder.swift │ ├── _LayoutBlockNode.swift │ └── _LayoutBlockType.swift ├── EntryPoint.swift ├── Extensions │ ├── NSLayoutConstraint+.swift │ ├── UILayoutGuide+Mondrian.swift │ └── UIView+Mondrian.swift ├── Internal │ ├── Utils.swift │ └── _LayoutElement.swift ├── LayoutBlocks │ ├── BackgroundBlock.swift │ ├── HStackBlock.swift │ ├── LayoutContainer.swift │ ├── LayoutGuideBlock.swift │ ├── OverlayBlock.swift │ ├── RelativeBlock.swift │ ├── StackingSpacer.swift │ ├── VGridBlock.swift │ ├── VStackBlock.swift │ ├── ViewBlock.swift │ └── ZStackBlock.swift ├── Manager │ └── LayoutManager.swift └── MondrianNamespace.swift ├── Package.swift ├── README.md ├── RevealServer.xcframework ├── Info.plist ├── Scripts │ └── integrate_revealserver.sh ├── _CodeSignature │ ├── CodeDirectory │ ├── CodeRequirements │ ├── CodeRequirements-1 │ ├── CodeResources │ └── CodeSignature ├── ios-arm64_armv7 │ └── RevealServer.framework │ │ ├── Headers │ │ └── RevealServer.h │ │ ├── Info.plist │ │ ├── Modules │ │ └── module.modulemap │ │ ├── RevealServer │ │ ├── Scripts │ │ └── integrate_revealserver.sh │ │ └── _CodeSignature │ │ └── CodeResources ├── ios-arm64_i386_x86_64-simulator │ └── RevealServer.framework │ │ ├── Headers │ │ └── RevealServer.h │ │ ├── Info.plist │ │ ├── Modules │ │ └── module.modulemap │ │ ├── RevealServer │ │ ├── Scripts │ │ └── integrate_revealserver.sh │ │ └── _CodeSignature │ │ └── CodeResources ├── ios-arm64_x86_64-maccatalyst │ └── RevealServer.framework │ │ ├── Headers │ │ ├── Modules │ │ ├── Resources │ │ ├── RevealServer │ │ └── Versions │ │ ├── A │ │ ├── Headers │ │ │ └── RevealServer.h │ │ ├── Modules │ │ │ └── module.modulemap │ │ ├── Resources │ │ │ ├── Info.plist │ │ │ └── Scripts │ │ │ │ └── integrate_revealserver.sh │ │ ├── RevealServer │ │ └── _CodeSignature │ │ │ └── CodeResources │ │ └── Current ├── ios-x86_64-maccatalyst │ └── RevealServer.framework │ │ ├── Headers │ │ ├── Modules │ │ ├── Resources │ │ ├── RevealServer │ │ └── Versions │ │ ├── A │ │ ├── Headers │ │ │ └── RevealServer.h │ │ ├── Modules │ │ │ └── module.modulemap │ │ ├── Resources │ │ │ ├── Info.plist │ │ │ └── Scripts │ │ │ │ └── integrate_revealserver.sh │ │ ├── RevealServer │ │ └── _CodeSignature │ │ │ └── CodeResources │ │ └── Current ├── tvos-arm64 │ └── RevealServer.framework │ │ ├── Headers │ │ └── RevealServer.h │ │ ├── Info.plist │ │ ├── Modules │ │ └── module.modulemap │ │ ├── RevealServer │ │ ├── Scripts │ │ └── integrate_revealserver.sh │ │ └── _CodeSignature │ │ └── CodeResources └── tvos-arm64_x86_64-simulator │ └── RevealServer.framework │ ├── Headers │ └── RevealServer.h │ ├── Info.plist │ ├── Modules │ └── module.modulemap │ ├── RevealServer │ ├── Scripts │ └── integrate_revealserver.sh │ └── _CodeSignature │ └── CodeResources ├── Tests ├── ClassicTests.swift ├── HStackTests.swift ├── Info.plist ├── LayoutDescriptorTests.swift ├── OverlayTests.swift ├── RelativeTests.swift ├── SizingTests.swift ├── SyntaxTests.swift ├── Tests.swift ├── VGridTests.swift ├── VStackTests.swift ├── ZStackTests.swift └── __Snapshots__ │ ├── ClassicTests │ └── test_multiplier_constraints.1.png │ ├── HStackTests │ ├── test_additional_spacing.1.png │ └── test_mixing_spacer.1.png │ ├── LayoutDescriptorTests │ ├── test_layout_0.1.png │ ├── test_layout_center.1.png │ └── test_layout_edge.1.png │ ├── OverlayTests │ ├── test_1.1.png │ └── test_2.1.png │ ├── RelativeTests │ ├── test_accumulate_padding.1.png │ ├── test_accumulate_relative.1.png │ └── test_centering_in_ambiguous.1.png │ ├── SizingTests │ └── test_sizing.1.png │ ├── SyntaxTests │ ├── test_hStack.1.png │ └── test_vStack.1.png │ ├── Tests │ ├── test_1.1.png │ └── test_mondrian.1.png │ ├── VGridTests │ └── test_basic.1.png │ ├── VStackTests │ ├── test_additional_spacing.1.png │ ├── test_enter.1.png │ ├── test_including_layoutGuide.1.png │ ├── test_label_alignment.1.png │ ├── test_leading.1.png │ ├── test_mixing_spacer.1.png │ └── test_trailing.1.png │ └── ZStackTests │ ├── test_alignSelf.1.png │ ├── test_expandsElementIfCanBeExpanding.1.png │ ├── test_expandsElementWithRelative.1.png │ └── test_minimum_padding.1.png └── fastlane ├── Appfile ├── Fastfile └── README.md /.github/workflows/CocoaPodsRelease.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | tags: 6 | - "*" 7 | 8 | jobs: 9 | pod-trunk-push: 10 | runs-on: macOS-11 11 | steps: 12 | - uses: maxim-lobanov/setup-xcode@v1.1 13 | with: 14 | xcode-version: "12.5" 15 | - uses: actions/checkout@v2 16 | - name: Deploy 17 | env: 18 | COCOAPODS_TRUNK_TOKEN: ${{ secrets.COCOAPODS_TRUNK_TOKEN }} 19 | run: pod trunk push --allow-warnings 20 | -------------------------------------------------------------------------------- /.github/workflows/swift.yml: -------------------------------------------------------------------------------- 1 | name: Swift 2 | 3 | on: 4 | push: 5 | branches: "*" 6 | pull_request: 7 | branches: "*" 8 | 9 | jobs: 10 | test: 11 | runs-on: macos-11 12 | 13 | steps: 14 | - uses: maxim-lobanov/setup-xcode@v1.1 15 | with: 16 | xcode-version: "12.5" 17 | - uses: actions/checkout@v2 18 | - name: Run test 19 | run: fastlane test_project 20 | pod-lint: 21 | runs-on: macos-11 22 | 23 | steps: 24 | - uses: maxim-lobanov/setup-xcode@v1.1 25 | with: 26 | xcode-version: "12.5" 27 | - uses: actions/checkout@v2 28 | - name: Run lint 29 | run: pod lib lint --allow-warnings 30 | swift-pm: 31 | runs-on: macos-11 32 | 33 | steps: 34 | - uses: maxim-lobanov/setup-xcode@v1.1 35 | with: 36 | xcode-version: "12.5" 37 | - uses: actions/checkout@v2 38 | - name: Build 39 | run: swift build -Xswiftc "-sdk" -Xswiftc "`xcrun --sdk iphoneos --show-sdk-path`" -Xswiftc "-target" -Xswiftc "arm64-apple-ios14.0" 40 | 41 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.toptal.com/developers/gitignore/api/swift 3 | # Edit at https://www.toptal.com/developers/gitignore?templates=swift 4 | 5 | ### Swift ### 6 | # Xcode 7 | # 8 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 9 | 10 | ## User settings 11 | xcuserdata/ 12 | 13 | ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) 14 | *.xcscmblueprint 15 | *.xccheckout 16 | 17 | ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) 18 | build/ 19 | DerivedData/ 20 | *.moved-aside 21 | *.pbxuser 22 | !default.pbxuser 23 | *.mode1v3 24 | !default.mode1v3 25 | *.mode2v3 26 | !default.mode2v3 27 | *.perspectivev3 28 | !default.perspectivev3 29 | 30 | ## Obj-C/Swift specific 31 | *.hmap 32 | 33 | ## App packaging 34 | *.ipa 35 | *.dSYM.zip 36 | *.dSYM 37 | 38 | ## Playgrounds 39 | timeline.xctimeline 40 | playground.xcworkspace 41 | 42 | # Swift Package Manager 43 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 44 | # Packages/ 45 | # Package.pins 46 | # Package.resolved 47 | # *.xcodeproj 48 | # Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata 49 | # hence it is not needed unless you have added a package configuration file to your project 50 | # .swiftpm 51 | 52 | .build/ 53 | 54 | # CocoaPods 55 | # We recommend against adding the Pods directory to your .gitignore. However 56 | # you should judge for yourself, the pros and cons are mentioned at: 57 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 58 | Pods/ 59 | # Add this line if you want to avoid checking in source code from the Xcode workspace 60 | # *.xcworkspace 61 | 62 | # Carthage 63 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 64 | # Carthage/Checkouts 65 | 66 | Carthage/Build/ 67 | 68 | # Add this lines if you are using Accio dependency management (Deprecated since Xcode 12) 69 | # Dependencies/ 70 | # .accio/ 71 | 72 | # fastlane 73 | # It is recommended to not store the screenshots in the git repo. 74 | # Instead, use fastlane to re-generate the screenshots whenever they are needed. 75 | # For more information about the recommended setup visit: 76 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 77 | 78 | fastlane/report.xml 79 | fastlane/Preview.html 80 | fastlane/screenshots/**/*.png 81 | fastlane/test_output 82 | 83 | # Code Injection 84 | # After new code Injection tools there's a generated folder /iOSInjectionProject 85 | # https://github.com/johnno1962/injectionforxcode 86 | 87 | iOSInjectionProject/ 88 | 89 | .swiftpm 90 | 91 | .DS_Store 92 | # End of https://www.toptal.com/developers/gitignore/api/swift 93 | -------------------------------------------------------------------------------- /Demo_MondrianLayout/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | @UIApplicationMain 4 | class AppDelegate: UIResponder, UIApplicationDelegate { 5 | 6 | var window: UIWindow? 7 | 8 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 9 | 10 | let newWindow = UIWindow() 11 | newWindow.rootViewController = RootContainerViewController() 12 | newWindow.makeKeyAndVisible() 13 | self.window = newWindow 14 | return true 15 | } 16 | 17 | } 18 | 19 | -------------------------------------------------------------------------------- /Demo_MondrianLayout/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Demo_MondrianLayout/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "2x", 6 | "size" : "20x20" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "scale" : "3x", 11 | "size" : "20x20" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "scale" : "2x", 16 | "size" : "29x29" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "scale" : "3x", 21 | "size" : "29x29" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "scale" : "2x", 26 | "size" : "40x40" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "scale" : "3x", 31 | "size" : "40x40" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "scale" : "2x", 36 | "size" : "60x60" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "scale" : "3x", 41 | "size" : "60x60" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "scale" : "1x", 46 | "size" : "20x20" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "scale" : "2x", 51 | "size" : "20x20" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "scale" : "1x", 56 | "size" : "29x29" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "scale" : "2x", 61 | "size" : "29x29" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "scale" : "1x", 66 | "size" : "40x40" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "scale" : "2x", 71 | "size" : "40x40" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "scale" : "1x", 76 | "size" : "76x76" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "scale" : "2x", 81 | "size" : "76x76" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "scale" : "2x", 86 | "size" : "83.5x83.5" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "scale" : "1x", 91 | "size" : "1024x1024" 92 | } 93 | ], 94 | "info" : { 95 | "author" : "xcode", 96 | "version" : 1 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /Demo_MondrianLayout/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Demo_MondrianLayout/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Demo_MondrianLayout/Book.Background.swift: -------------------------------------------------------------------------------- 1 | import StorybookKit 2 | import UIKit 3 | 4 | import MondrianLayout 5 | 6 | var _book_background: BookView { 7 | BookNavigationLink(title: "Background") { 8 | BookPreview { 9 | ExampleView(width: nil, height: nil) { (view: UIView) in 10 | 11 | Mondrian.buildSubviews(on: view) { 12 | VStackBlock { 13 | UIView.mock( 14 | backgroundColor: .mondrianYellow, 15 | preferredSize: .smallSquare 16 | ) 17 | UIView.mock( 18 | backgroundColor: .mondrianYellow, 19 | preferredSize: .smallSquare 20 | ) 21 | UIView.mock( 22 | backgroundColor: .mondrianYellow, 23 | preferredSize: .smallSquare 24 | ) 25 | } 26 | .padding(10) 27 | .background(UIView.mock(backgroundColor: .mondrianGray)) 28 | } 29 | 30 | } 31 | } 32 | 33 | BookPreview { 34 | ExampleView(width: nil, height: nil) { (view: UIView) in 35 | Mondrian.buildSubviews(on: view) { 36 | VStackBlock(spacing: 2) { 37 | UIView.mock( 38 | backgroundColor: .mondrianYellow, 39 | preferredSize: .smallSquare 40 | ) 41 | UIView.mock( 42 | backgroundColor: .mondrianYellow, 43 | preferredSize: .smallSquare 44 | ) 45 | UIView.mock( 46 | backgroundColor: .mondrianYellow, 47 | preferredSize: .smallSquare 48 | ) 49 | 50 | HStackBlock(spacing: 2) { 51 | UIView.mock( 52 | backgroundColor: .mondrianBlue, 53 | preferredSize: .smallSquare 54 | ) 55 | UIView.mock( 56 | backgroundColor: .mondrianBlue, 57 | preferredSize: .smallSquare 58 | ) 59 | UIView.mock( 60 | backgroundColor: .mondrianBlue, 61 | preferredSize: .smallSquare 62 | ) 63 | } 64 | 65 | } 66 | .padding(10) 67 | .background(UIView.mock(backgroundColor: .mondrianGray)) 68 | } 69 | } 70 | } 71 | 72 | BookPreview { 73 | ExampleView(width: nil, height: nil) { (view: UIView) in 74 | Mondrian.buildSubviews(on: view) { 75 | VStackBlock { 76 | UIView.mock( 77 | backgroundColor: .mondrianYellow, 78 | preferredSize: .smallSquare 79 | ) 80 | UIView.mock( 81 | backgroundColor: .mondrianYellow, 82 | preferredSize: .smallSquare 83 | ) 84 | UIView.mock( 85 | backgroundColor: .mondrianYellow, 86 | preferredSize: .smallSquare 87 | ) 88 | .viewBlock 89 | .padding(10) 90 | .background(UIView.mock(backgroundColor: .mondrianGray)) 91 | } 92 | .padding(10) 93 | .background(UIView.mock(backgroundColor: .mondrianGray)) 94 | } 95 | } 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /Demo_MondrianLayout/Book.LayoutContainer.swift: -------------------------------------------------------------------------------- 1 | import MondrianLayout 2 | import StorybookKit 3 | import UIKit 4 | 5 | var _book_layoutContainer: BookView { 6 | 7 | BookNavigationLink(title: "LayoutContainer") { 8 | 9 | BookPreview { 10 | 11 | let view = UIView.mock() 12 | Mondrian.buildSubviews(on: view) { 13 | LayoutContainer(attachedSafeAreaEdges: .all) { 14 | VStackBlock { 15 | 16 | } 17 | } 18 | } 19 | return view 20 | 21 | } 22 | 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /Demo_MondrianLayout/Book.Manager.swift: -------------------------------------------------------------------------------- 1 | import MondrianLayout 2 | import StorybookKit 3 | import UIKit 4 | 5 | var _book_layoutManager: BookView { 6 | BookNavigationLink(title: "LayoutManager") { 7 | 8 | let manager = LayoutManager() 9 | var counter = 0 10 | 11 | BookPreview { 12 | ExampleView(width: 200, height: 200) { view in 13 | 14 | let box1 = UIView.mock(backgroundColor: .neon(.cyan)) 15 | let box2 = UIView.mock(backgroundColor: .neon(.cyan)) 16 | let box3 = UIView.mock(backgroundColor: .neon(.cyan)) 17 | 18 | manager.setup(on: view) { 19 | 20 | if counter % 2 == 0 { 21 | 22 | VStackBlock { 23 | box1 24 | .viewBlock 25 | .size(.smallSquare) 26 | 27 | box2 28 | .viewBlock 29 | .size(.smallSquare) 30 | 31 | box3 32 | .viewBlock 33 | .size(.smallSquare) 34 | 35 | StackingSpacer(minLength: 0) 36 | } 37 | } else { 38 | 39 | HStackBlock { 40 | box1 41 | .viewBlock 42 | .size(.largeSquare) 43 | 44 | box2 45 | .viewBlock 46 | .size(.largeSquare) 47 | 48 | box3 49 | .viewBlock 50 | .size(.largeSquare) 51 | 52 | StackingSpacer(minLength: 0) 53 | } 54 | } 55 | 56 | } 57 | 58 | } 59 | } 60 | .addButton( 61 | "Update", 62 | handler: { view in 63 | counter += 1 64 | manager.reloadLayout() 65 | } 66 | ) 67 | .addButton( 68 | "Update Animated", 69 | handler: { view in 70 | counter += 1 71 | UIViewPropertyAnimator(duration: 1.2, dampingRatio: 0.9) { 72 | manager.reloadLayout() 73 | view.layoutIfNeeded() 74 | } 75 | .startAnimation() 76 | } 77 | ) 78 | .title("LayoutManager") 79 | 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /Demo_MondrianLayout/Book.Overlay.swift: -------------------------------------------------------------------------------- 1 | import MondrianLayout 2 | import StorybookKit 3 | import UIKit 4 | 5 | var _book_overlay: BookView { 6 | BookNavigationLink(title: "Overlay") { 7 | 8 | BookPreview { 9 | ExampleView(width: nil, height: nil) { (view: UIView) in 10 | 11 | Mondrian.buildSubviews(on: view) { 12 | UIView.mock( 13 | backgroundColor: .mondrianYellow, 14 | preferredSize: .init(width: 100, height: 100) 15 | ) 16 | .viewBlock 17 | .overlay( 18 | UIView.mock(backgroundColor: .layeringColor) 19 | .viewBlock 20 | .overlay( 21 | UIView.mock(backgroundColor: .layeringColor) 22 | .viewBlock 23 | .padding(10) 24 | ) 25 | .padding(10) 26 | ) 27 | } 28 | } 29 | } 30 | 31 | 32 | BookPreview { 33 | ExampleView(width: nil, height: nil) { (view: UIView) in 34 | Mondrian.buildSubviews(on: view) { 35 | UIView.mock( 36 | backgroundColor: .mondrianYellow, 37 | preferredSize: .init(width: 100, height: 100) 38 | ) 39 | .viewBlock 40 | .overlay( 41 | UIView.mock(backgroundColor: .layeringColor) 42 | .viewBlock 43 | .overlay( 44 | UIView.mock(backgroundColor: .layeringColor) 45 | .viewBlock 46 | .padding(10) 47 | ) 48 | .padding(10) 49 | ) 50 | } 51 | } 52 | } 53 | 54 | BookPreview { 55 | ExampleView(width: nil, height: nil) { (view: UIView) in 56 | Mondrian.buildSubviews(on: view) { 57 | VStackBlock { 58 | UIView.mock( 59 | backgroundColor: .mondrianYellow, 60 | preferredSize: .smallSquare 61 | ) 62 | UIView.mock( 63 | backgroundColor: .mondrianYellow, 64 | preferredSize: .smallSquare 65 | ) 66 | UIView.mock( 67 | backgroundColor: .mondrianYellow, 68 | preferredSize: .smallSquare 69 | ) 70 | } 71 | .padding(10) 72 | .overlay(UIView.mock(backgroundColor: .layeringColor)) 73 | } 74 | } 75 | } 76 | 77 | BookPreview { 78 | ExampleView(width: nil, height: nil) { (view: UIView) in 79 | Mondrian.buildSubviews(on: view) { 80 | VStackBlock(spacing: 2) { 81 | UIView.mock( 82 | backgroundColor: .mondrianYellow, 83 | preferredSize: .smallSquare 84 | ) 85 | UIView.mock( 86 | backgroundColor: .mondrianYellow, 87 | preferredSize: .smallSquare 88 | ) 89 | UIView.mock( 90 | backgroundColor: .mondrianYellow, 91 | preferredSize: .smallSquare 92 | ) 93 | 94 | HStackBlock(spacing: 2) { 95 | UIView.mock( 96 | backgroundColor: .mondrianBlue, 97 | preferredSize: .smallSquare 98 | ) 99 | UIView.mock( 100 | backgroundColor: .mondrianBlue, 101 | preferredSize: .smallSquare 102 | ) 103 | UIView.mock( 104 | backgroundColor: .mondrianBlue, 105 | preferredSize: .smallSquare 106 | ) 107 | } 108 | 109 | } 110 | .padding(10) 111 | .overlay(UIView.mock(backgroundColor: .layeringColor)) 112 | } 113 | } 114 | } 115 | 116 | BookPreview { 117 | ExampleView(width: nil, height: nil) { (view: UIView) in 118 | Mondrian.buildSubviews(on: view) { 119 | VStackBlock { 120 | UIView.mock( 121 | backgroundColor: .mondrianYellow, 122 | preferredSize: .smallSquare 123 | ) 124 | UIView.mock( 125 | backgroundColor: .mondrianYellow, 126 | preferredSize: .smallSquare 127 | ) 128 | UIView.mock( 129 | backgroundColor: .mondrianYellow, 130 | preferredSize: .smallSquare 131 | ) 132 | .viewBlock 133 | .padding(10) 134 | .overlay(UIView.mock(backgroundColor: .layeringColor)) 135 | } 136 | .padding(10) 137 | .overlay(UIView.mock(backgroundColor: .layeringColor)) 138 | } 139 | } 140 | } 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /Demo_MondrianLayout/Book.RelativeBlock.swift: -------------------------------------------------------------------------------- 1 | import MondrianLayout 2 | import StorybookKit 3 | import UIKit 4 | 5 | extension FixedWidthInteger { 6 | 7 | public var bk: CGFloat { 8 | return 4 * CGFloat(self) 9 | } 10 | } 11 | 12 | var _book_RelativeBlock: BookView { 13 | BookNavigationLink(title: "RelativeBlock") { 14 | 15 | BookPreview { 16 | ExampleView(width: 100, height: 100) { view in 17 | Mondrian.buildSubviews(on: view) { 18 | LayoutContainer(attachedSafeAreaEdges: .all) { 19 | ZStackBlock { 20 | 21 | UIView.mock(backgroundColor: .layeringColor) 22 | .viewBlock.alignSelf(.attach(.all)) 23 | 24 | UIView.mock(backgroundColor: .layeringColor, preferredSize: .smallSquare) 25 | .viewBlock 26 | .relative(.bottom, 20) 27 | .relative(.trailing, 20) 28 | } 29 | } 30 | } 31 | } 32 | } 33 | 34 | BookPreview { 35 | ExampleView(width: 100, height: 100) { view in 36 | Mondrian.buildSubviews(on: view) { 37 | ZStackBlock { 38 | UILabel.mockSingleline(text: "A") 39 | .viewBlock 40 | .background(UIView.mock()) 41 | .relative(.all, .min(20)) 42 | } 43 | .background(UIView.mock()) 44 | } 45 | } 46 | } 47 | 48 | BookPreview { 49 | ExampleView(width: 100, height: 100) { view in 50 | Mondrian.buildSubviews(on: view) { 51 | ZStackBlock { 52 | ZStackBlock { 53 | UILabel.mockSingleline(text: "Hello Hello Hello") 54 | .viewBlock 55 | .background(UIView.mock()) 56 | } 57 | .padding(20) 58 | } 59 | .background(UIView.mock()) 60 | } 61 | } 62 | } 63 | 64 | BookPreview { 65 | ExampleView(width: 100, height: 100) { view in 66 | Mondrian.buildSubviews(on: view) { 67 | ZStackBlock { 68 | ZStackBlock { 69 | UILabel.mockSingleline(text: "Hello Hello Hello") 70 | .viewBlock 71 | } 72 | .relative(.horizontal, 20) 73 | } 74 | } 75 | } 76 | } 77 | 78 | BookPreview { 79 | ExampleView(width: nil, height: nil) { (view: UIView) in 80 | Mondrian.buildSubviews(on: view) { 81 | VStackBlock { 82 | ZStackBlock { 83 | UIView.mock( 84 | backgroundColor: .mondrianYellow, 85 | preferredSize: .init(width: 100, height: 100) 86 | ) 87 | 88 | UIView.mock( 89 | backgroundColor: .systemBlue, 90 | preferredSize: .init(width: 10, height: 10) 91 | ) 92 | .viewBlock 93 | .relative(.top, 2.bk) 94 | .relative([.trailing], 10) 95 | .padding(20) 96 | 97 | } 98 | } 99 | } 100 | } 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /Demo_MondrianLayout/Book.SafeArea.swift: -------------------------------------------------------------------------------- 1 | import MondrianLayout 2 | import StorybookKit 3 | import UIKit 4 | 5 | var _book_SafeArea: BookView { 6 | BookNavigationLink(title: "SafeArea") { 7 | 8 | BookPush(title: "Push") { 9 | AnyViewController { view in 10 | Mondrian.buildSubviews(on: view) { 11 | LayoutContainer(attachedSafeAreaEdges: .vertical) { 12 | VStackBlock { 13 | UIView.mock( 14 | backgroundColor: .layeringColor, 15 | preferredSize: .init(width: 100, height: 100) 16 | ) 17 | UIView.mock( 18 | backgroundColor: .layeringColor, 19 | preferredSize: .init(width: 100, height: 100) 20 | ) 21 | UIView.mock( 22 | backgroundColor: .layeringColor, 23 | preferredSize: .init(width: 100, height: 100) 24 | ) 25 | } 26 | } 27 | 28 | } 29 | } 30 | } 31 | 32 | BookPush(title: "Push custom") { 33 | AnyViewController { view in 34 | 35 | Mondrian.buildSubviews(on: view) { 36 | LayoutContainer(top: .view(.top), leading: .view(.leading), bottom: .safeArea(.top), trailing: .view(.trailing)) { 37 | UIView.mock( 38 | backgroundColor: .layeringColor, 39 | preferredSize: .init(width: 100, height: 100) 40 | ) 41 | .viewBlock 42 | } 43 | 44 | } 45 | 46 | Mondrian.buildSubviews(on: view) { 47 | LayoutContainer(attachedSafeAreaEdges: .vertical) { 48 | UIView.mock( 49 | backgroundColor: .layeringColor, 50 | preferredSize: .init(width: 100, height: 100) 51 | ) 52 | .viewBlock 53 | } 54 | 55 | } 56 | } 57 | } 58 | 59 | BookPush(title: "Push") { 60 | AnyViewController { view in 61 | Mondrian.buildSubviews(on: view) { 62 | LayoutContainer(attachedSafeAreaEdges: .vertical) { 63 | ZStackBlock { 64 | UIView.mock( 65 | backgroundColor: .layeringColor 66 | ) 67 | 68 | UIView.mock( 69 | backgroundColor: .mondrianBlue, 70 | preferredSize: .smallSquare 71 | ) 72 | } 73 | } 74 | } 75 | } 76 | } 77 | 78 | BookPush(title: "Bottom Buttons") { 79 | AnyViewController { view in 80 | Mondrian.buildSubviews(on: view) { 81 | LayoutContainer(attachedSafeAreaEdges: .vertical) { 82 | ZStackBlock { 83 | 84 | VStackBlock(alignment: .center) { 85 | HStackBlock { 86 | UIView.mock( 87 | backgroundColor: .mondrianBlue, 88 | preferredSize: .init(width: 20, height: 30) 89 | ) 90 | UIView.mock( 91 | backgroundColor: .mondrianBlue, 92 | preferredSize: .init(width: 20, height: 10) 93 | ) 94 | UIView.mock( 95 | backgroundColor: .mondrianBlue, 96 | preferredSize: .init(width: 20, height: 20) 97 | ) 98 | } 99 | } 100 | .padding(20) 101 | .background( 102 | UIView.mock( 103 | backgroundColor: .layeringColor 104 | ) 105 | ) 106 | .relative([.horizontal, .bottom], 0) 107 | 108 | } 109 | } 110 | } 111 | } 112 | } 113 | 114 | } 115 | } 116 | 117 | final class AnyViewController: UIViewController { 118 | 119 | private let onViewDidLoad: (UIView) -> Void 120 | 121 | init( 122 | onViewDidLoad: @escaping (UIView) -> Void 123 | ) { 124 | self.onViewDidLoad = onViewDidLoad 125 | 126 | super.init(nibName: nil, bundle: nil) 127 | 128 | if #available(iOS 13.0, *) { 129 | view.backgroundColor = .systemBackground 130 | } else { 131 | view.backgroundColor = .white 132 | } 133 | 134 | additionalSafeAreaInsets = .init(top: 60, left: 60, bottom: 60, right: 60) 135 | } 136 | 137 | required init?( 138 | coder: NSCoder 139 | ) { 140 | fatalError() 141 | } 142 | 143 | override func viewDidLoad() { 144 | super.viewDidLoad() 145 | 146 | onViewDidLoad(view) 147 | } 148 | 149 | } 150 | -------------------------------------------------------------------------------- /Demo_MondrianLayout/Book.Sizing.swift: -------------------------------------------------------------------------------- 1 | import MondrianLayout 2 | import StorybookKit 3 | import UIKit 4 | 5 | var _book_sizing: BookView { 6 | 7 | BookNavigationLink(title: "Sizing") { 8 | 9 | BookPreview { 10 | ExampleView(width: 200, height: 200) { (view: UIView) in 11 | Mondrian.buildSubviews(on: view) { 12 | ZStackBlock { 13 | 14 | HStackBlock { 15 | VStackBlock(alignment: .leading) { 16 | UIView.mock( 17 | backgroundColor: .layeringColor, 18 | preferredSize: .init(width: 36, height: 36) 19 | ) 20 | .viewBlock 21 | .alignSelf(.fill) 22 | 23 | UIView.mock( 24 | backgroundColor: .layeringColor, 25 | preferredSize: .init(width: 36, height: 36) 26 | ) 27 | .viewBlock 28 | .alignSelf(.fill) 29 | } 30 | .width(20) 31 | 32 | VStackBlock(alignment: .leading) { 33 | UIView.mock( 34 | backgroundColor: .layeringColor, 35 | preferredSize: .init(width: 36, height: 36) 36 | ) 37 | .viewBlock 38 | .alignSelf(.fill) 39 | 40 | UIView.mock( 41 | backgroundColor: .layeringColor, 42 | preferredSize: .init(width: 36, height: 36) 43 | ) 44 | .viewBlock 45 | .alignSelf(.fill) 46 | } 47 | .width(40) 48 | 49 | UIView.mock( 50 | backgroundColor: .layeringColor, 51 | preferredSize: .init(width: 36, height: 36) 52 | ) 53 | .viewBlock 54 | .padding(10) 55 | .width(.max(30)) 56 | 57 | } 58 | .height(50) 59 | 60 | } 61 | } 62 | } 63 | } 64 | 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /Demo_MondrianLayout/Book.VGridBlock.swift: -------------------------------------------------------------------------------- 1 | import MondrianLayout 2 | import StorybookKit 3 | import UIKit 4 | 5 | var _book_VGridConstraint: BookView { 6 | 7 | BookNavigationLink(title: "VGridBlock") { 8 | 9 | BookPreview { 10 | ExampleView(width: 100, height: nil) { view in 11 | Mondrian.buildSubviews(on: view) { 12 | VGridBlock( 13 | columns: [ 14 | .init(.flexible(), spacing: 16), 15 | .init(.flexible(), spacing: 16), 16 | ], 17 | spacing: 4 18 | ) { 19 | 20 | UILabel.mockMultiline(text: "Helloooo") 21 | .viewBlock 22 | .overlay(UIView.mock(backgroundColor: .layeringColor, preferredSize: .smallSquare)) 23 | 24 | UIView.mock(backgroundColor: .neon(.cyan), preferredSize: .smallSquare) 25 | UIView.mock(backgroundColor: .neon(.cyan), preferredSize: .smallSquare) 26 | UIView.mock(backgroundColor: .neon(.cyan), preferredSize: .smallSquare) 27 | 28 | UIView.mock(backgroundColor: .neon(.cyan), preferredSize: .smallSquare) 29 | UIView.mock(backgroundColor: .neon(.cyan), preferredSize: .smallSquare) 30 | UIView.mock(backgroundColor: .neon(.cyan), preferredSize: .smallSquare) 31 | UIView.mock(backgroundColor: .neon(.cyan), preferredSize: .smallSquare) 32 | 33 | UIView.mock(backgroundColor: .neon(.cyan), preferredSize: .smallSquare) 34 | UIView.mock(backgroundColor: .neon(.cyan), preferredSize: .smallSquare) 35 | } 36 | } 37 | } 38 | } 39 | .title("Grid") 40 | 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /Demo_MondrianLayout/Book.VStackBlock.swift: -------------------------------------------------------------------------------- 1 | import MondrianLayout 2 | import StorybookKit 3 | import UIKit 4 | 5 | var _book_VStackBlock: BookView { 6 | 7 | BookNavigationLink(title: "VStackBlock") { 8 | 9 | BookPreview { 10 | ExampleView(width: 200, height: 200) { (view: UIView) in 11 | Mondrian.buildSubviews(on: view) { 12 | VStackBlock(alignment: .leading) { 13 | UILabel.mockMultiline(text: BookGenerator.loremIpsum(length: 10)) 14 | .viewBlock 15 | .padding(.horizontal, 10) 16 | UIView.mock(backgroundColor: .layeringColor) 17 | .viewBlock 18 | .alignSelf(.fill) 19 | } 20 | .background(UIView.mock(backgroundColor: .layeringColor)) 21 | } 22 | } 23 | } 24 | .title("Spacing") 25 | 26 | BookPreview { 27 | ExampleView(width: nil, height: 180) { (view: UIView) in 28 | Mondrian.buildSubviews(on: view) { 29 | VStackBlock(spacing: 4) { 30 | UIView.mock( 31 | backgroundColor: .mondrianYellow, 32 | preferredSize: .smallSquare 33 | ) 34 | 35 | StackingSpacer(minLength: 20) 36 | 37 | UIView.mock( 38 | backgroundColor: .mondrianYellow, 39 | preferredSize: .smallSquare 40 | ) 41 | 42 | UIView.mock( 43 | backgroundColor: .mondrianYellow, 44 | preferredSize: .smallSquare 45 | ) 46 | 47 | StackingSpacer(minLength: 20, expands: false) 48 | } 49 | .background(UIView.mock(backgroundColor: .layeringColor)) 50 | } 51 | } 52 | } 53 | .title("Spacing") 54 | 55 | BookForEach(data: [.center, .leading, .trailing, .fill] as [VStackBlock.XAxisAlignment]) { alignment in 56 | BookPreview { 57 | ExampleView(width: nil, height: nil) { (view: UIView) in 58 | Mondrian.buildSubviews(on: view) { 59 | VStackBlock(spacing: 4, alignment: alignment) { 60 | UILabel.mockMultiline(text: "Hello", textColor: .white) 61 | .viewBlock 62 | .padding(8) 63 | .background(UIView.mock(backgroundColor: .mondrianYellow)) 64 | UILabel.mockMultiline(text: "Mondrian", textColor: .white) 65 | .viewBlock 66 | .padding(8) 67 | .background(UIView.mock(backgroundColor: .mondrianRed)) 68 | UILabel.mockMultiline(text: "Layout!", textColor: .white) 69 | .viewBlock 70 | .padding(8) 71 | .background(UIView.mock(backgroundColor: .mondrianBlue)) 72 | } 73 | } 74 | } 75 | } 76 | .title("Labels - align: \(alignment)") 77 | } 78 | 79 | BookPreview { 80 | ExampleView(width: nil, height: nil) { (view: UIView) in 81 | Mondrian.buildSubviews(on: view) { 82 | VStackBlock(spacing: 4) { 83 | UIView.mock( 84 | backgroundColor: .mondrianYellow, 85 | preferredSize: .smallSquare 86 | ) 87 | 88 | UIView.mock( 89 | backgroundColor: .mondrianYellow, 90 | preferredSize: .smallSquare 91 | ) 92 | 93 | UIView.mock( 94 | backgroundColor: .mondrianYellow, 95 | preferredSize: .smallSquare 96 | ) 97 | } 98 | } 99 | } 100 | } 101 | .title("Spacing") 102 | 103 | BookPreview { 104 | ExampleView(width: nil, height: nil) { (view: UIView) in 105 | Mondrian.buildSubviews(on: view) { 106 | VStackBlock(spacing: 4) { 107 | UIView.mock( 108 | backgroundColor: .mondrianYellow, 109 | preferredSize: .smallSquare 110 | ) 111 | 112 | StackingSpacer(minLength: 4) 113 | 114 | UIView.mock( 115 | backgroundColor: .mondrianYellow, 116 | preferredSize: .smallSquare 117 | ) 118 | 119 | UIView.mock( 120 | backgroundColor: .mondrianYellow, 121 | preferredSize: .smallSquare 122 | ) 123 | } 124 | } 125 | } 126 | } 127 | .title("Spacing with additional spacer") 128 | 129 | BookPreview { 130 | ExampleView(width: 200, height: 200) { (view: UIView) in 131 | 132 | let boxes = (0..<3).map { _ in UIView.mock(backgroundColor: .layeringColor) } 133 | let guides = (0..<2).map { _ in UILayoutGuide() } 134 | 135 | Mondrian.buildSubviews(on: view) { 136 | VStackBlock(alignment: .leading) { 137 | 138 | boxes[0] 139 | 140 | guides[0] 141 | 142 | boxes[1] 143 | 144 | guides[1] 145 | 146 | boxes[2] 147 | 148 | StackingSpacer(minLength: 0) 149 | 150 | } 151 | .background(UIView.mock(backgroundColor: .layeringColor)) 152 | } 153 | 154 | mondrianBatchLayout { 155 | 156 | boxes.map { $0.mondrian.layout.height(20) } 157 | 158 | guides[0].mondrian.layout.height(.to(boxes[0])) 159 | guides[1].mondrian.layout.height(.to(boxes[2]), multiplier: 2) 160 | } 161 | } 162 | } 163 | .title("Including LayoutGuide") 164 | 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /Demo_MondrianLayout/Book.ViewController.swift: -------------------------------------------------------------------------------- 1 | import MondrianLayout 2 | import StorybookKit 3 | import UIKit 4 | 5 | var _book_ViewController: BookView { 6 | 7 | BookNavigationLink(title: "ViewController") { 8 | 9 | BookPush(title: "Push") { 10 | 11 | let body = ExampleView(width: nil, height: nil) { view in 12 | Mondrian.buildSubviews(on: view) { 13 | HStackBlock { 14 | UIView.mock( 15 | backgroundColor: .layeringColor, 16 | preferredSize: .smallSquare 17 | ) 18 | UIView.mock( 19 | backgroundColor: .layeringColor, 20 | preferredSize: .smallSquare 21 | ) 22 | UIView.mock( 23 | backgroundColor: .layeringColor, 24 | preferredSize: .smallSquare 25 | ) 26 | } 27 | } 28 | } 29 | 30 | let container = ExampleView(width: nil, height: nil) { view in 31 | Mondrian.buildSubviews(on: view) { 32 | ZStackBlock { 33 | body.viewBlock.padding(10) 34 | } 35 | } 36 | } 37 | 38 | return AnyViewController { view in 39 | Mondrian.buildSubviews(on: view) { 40 | LayoutContainer(attachedSafeAreaEdges: .all) { 41 | ZStackBlock { 42 | container 43 | .viewBlock 44 | .padding(20) 45 | .background(UIView.mock(backgroundColor: .layeringColor)) 46 | .relative([.bottom, .horizontal], 0) 47 | } 48 | } 49 | } 50 | } 51 | } 52 | 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Demo_MondrianLayout/Book.swift: -------------------------------------------------------------------------------- 1 | import MondrianLayout 2 | import StorybookKit 3 | import UIKit 4 | 5 | let book = Book(title: "MondrianLayout") { 6 | 7 | _book_neonGrid 8 | 9 | _book_mondrian 10 | 11 | _book_sizing 12 | 13 | _book_background 14 | 15 | _book_overlay 16 | 17 | _book_VStackBlock 18 | 19 | _book_HStackBlock 20 | 21 | _book_ZStackConstraint 22 | 23 | _book_VGridConstraint 24 | 25 | _book_RelativeBlock 26 | 27 | _book_SafeArea 28 | 29 | _book_ViewController 30 | 31 | _book_classic 32 | 33 | _book_layoutManager 34 | 35 | BookNavigationLink(title: "Instagram Post") { 36 | BookPreview { 37 | InstagramPostView() 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Demo_MondrianLayout/CGSize+.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CGSize+.swift 3 | // Demo_MondrianLayout 4 | // 5 | // Created by Muukii on 2021/06/17. 6 | // 7 | 8 | import UIKit 9 | 10 | extension CGSize { 11 | 12 | static var smallSquare: Self { 13 | .init(width: 20, height: 20) 14 | } 15 | 16 | static var largeSquare: Self { 17 | .init(width: 60, height: 60) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Demo_MondrianLayout/ExampleView.swift: -------------------------------------------------------------------------------- 1 | 2 | import UIKit 3 | import MondrianLayout 4 | 5 | final class ExampleView: UIView { 6 | 7 | init( 8 | width: CGFloat?, 9 | height: CGFloat?, 10 | build: (UIView) -> Void 11 | ) { 12 | 13 | super.init(frame: .zero) 14 | build(self) 15 | 16 | translatesAutoresizingMaskIntoConstraints = false 17 | 18 | mondrian.layout 19 | .width(width.map { .exact($0) } ?? .exact(0, .fittingSizeLevel)) 20 | .height(height.map { .exact($0) } ?? .exact(0, .fittingSizeLevel)) 21 | .activate() 22 | 23 | layoutIfNeeded() 24 | 25 | } 26 | 27 | required init?( 28 | coder: NSCoder 29 | ) { 30 | fatalError("init(coder:) has not been implemented") 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /Demo_MondrianLayout/Extensions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Extensions.swift 3 | // Demo_BoxLayout2 4 | // 5 | // Created by Muukii on 2021/06/13. 6 | // 7 | 8 | import UIKit 9 | 10 | extension UIView { 11 | 12 | static func mock(backgroundColor: UIColor = .layeringColor) -> UIView { 13 | let view = UIView() 14 | view.backgroundColor = backgroundColor 15 | view.layer.borderWidth = 3 16 | view.layer.borderColor = UIColor(white: 0, alpha: 0.2).cgColor 17 | return view 18 | } 19 | 20 | static func mock(backgroundColor: UIColor = .layeringColor, preferredSize: CGSize) -> UIView { 21 | let view = IntrinsicSizeView(preferredSize: preferredSize) 22 | view.backgroundColor = backgroundColor 23 | view.layer.borderWidth = 3 24 | view.layer.borderColor = UIColor(white: 0, alpha: 0.2).cgColor 25 | return view 26 | } 27 | } 28 | 29 | extension UIImageView { 30 | 31 | static func mock(image: UIImage) -> UIView { 32 | let view = UIImageView(image: image) 33 | return view 34 | 35 | } 36 | } 37 | 38 | final class IntrinsicSizeView: UIView { 39 | 40 | private let preferredSize: CGSize 41 | 42 | init( 43 | preferredSize: CGSize 44 | ) { 45 | self.preferredSize = preferredSize 46 | super.init(frame: .zero) 47 | } 48 | 49 | required init?( 50 | coder: NSCoder 51 | ) { 52 | fatalError("init(coder:) has not been implemented") 53 | } 54 | 55 | override var intrinsicContentSize: CGSize { 56 | preferredSize 57 | } 58 | 59 | } 60 | 61 | extension UILabel { 62 | 63 | static func mockSingleline(text: String, textColor: UIColor = .black) -> UILabel { 64 | let label = UILabel() 65 | label.font = .preferredFont(forTextStyle: .headline) 66 | label.textColor = textColor 67 | label.numberOfLines = 1 68 | label.text = text 69 | return label 70 | } 71 | 72 | static func mockMultiline(text: String, textColor: UIColor = .black) -> UILabel { 73 | let label = UILabel() 74 | label.font = .preferredFont(forTextStyle: .headline) 75 | label.textColor = textColor 76 | label.numberOfLines = 0 77 | label.text = text 78 | return label 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /Demo_MondrianLayout/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 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UIApplicationSupportsIndirectInputEvents 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /Demo_MondrianLayout/InstagramPostView.swift: -------------------------------------------------------------------------------- 1 | import MondrianLayout 2 | import StorybookKit 3 | import UIKit 4 | 5 | final class InstagramPostView: UIView { 6 | 7 | private let profileImageView = UIView.mock( 8 | backgroundColor: .mondrianBlue, 9 | preferredSize: .init(width: 32, height: 32) 10 | ) 11 | 12 | private let nicknameLabel = UILabel.mockMultiline(text: "Muukii") 13 | 14 | private let imageView = UIView.mock(backgroundColor: .mondrianYellow) 15 | 16 | private let likeButton = UIView.mock( 17 | backgroundColor: .mondrianRed, 18 | preferredSize: .init(width: 32, height: 32) 19 | ) 20 | 21 | private let commentButton = UIView.mock( 22 | backgroundColor: .mondrianRed, 23 | preferredSize: .init(width: 32, height: 32) 24 | ) 25 | 26 | private let messageButton = UIView.mock( 27 | backgroundColor: .mondrianRed, 28 | preferredSize: .init(width: 32, height: 32) 29 | ) 30 | 31 | private let saveButton = UIView.mock( 32 | backgroundColor: .mondrianCyan, 33 | preferredSize: .init(width: 32, height: 32) 34 | ) 35 | 36 | private let captionBackground = UIView.mock(backgroundColor: .layeringColor) 37 | private let captionLabel = UILabel.mockMultiline(text: "Caption", textColor: .white) 38 | 39 | init() { 40 | 41 | super.init(frame: .zero) 42 | 43 | self.mondrian.buildSelfSizing { 44 | $0.width(200) 45 | } 46 | 47 | Mondrian.buildSubviews(on: self) { 48 | VStackBlock(alignment: .fill) { 49 | 50 | HStackBlock { 51 | ViewBlock(profileImageView) 52 | .huggingPriority(.horizontal, .required) 53 | .spacingAfter(4) 54 | nicknameLabel 55 | } 56 | .spacingAfter(10) 57 | 58 | ViewBlock(imageView) 59 | .aspectRatio(1) 60 | .overlay( 61 | captionLabel.viewBlock 62 | .padding(10) 63 | .background(captionBackground) 64 | .relative([.bottom, .trailing], 8) 65 | ) 66 | .spacingAfter(10) 67 | 68 | HStackBlock { 69 | HStackBlock(spacing: 2) { 70 | likeButton 71 | commentButton 72 | messageButton 73 | } 74 | StackingSpacer(minLength: 8) 75 | saveButton 76 | } 77 | .spacingAfter(10) 78 | 79 | } 80 | } 81 | 82 | } 83 | 84 | required init?( 85 | coder: NSCoder 86 | ) { 87 | fatalError("init(coder:) has not been implemented") 88 | } 89 | 90 | } 91 | -------------------------------------------------------------------------------- /Demo_MondrianLayout/RootContainerViewController.swift: -------------------------------------------------------------------------------- 1 | import StorybookKit 2 | import StorybookUI 3 | import UIKit 4 | 5 | final class RootContainerViewController: UIViewController { 6 | 7 | init() { 8 | super.init(nibName: nil, bundle: nil) 9 | 10 | let child = StorybookViewController( 11 | book: book, 12 | dismissHandler: nil 13 | ) 14 | 15 | addChild(child) 16 | view.addSubview(child.view) 17 | child.view.frame = view.bounds 18 | child.view.autoresizingMask = [.flexibleWidth, .flexibleHeight] 19 | 20 | } 21 | 22 | required init?(coder: NSCoder) { 23 | fatalError("init(coder:) has not been implemented") 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Demo_MondrianLayout/SwiftUIComparison.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | @available(iOS 13, *) 4 | enum Padding { 5 | 6 | struct ContentView: View { 7 | 8 | var body: some View { 9 | BorderedRect() 10 | .frame(maxWidth: 100, maxHeight: 100) 11 | .foregroundColor(.init(white: 0, opacity: 0.2)) 12 | .overlay( 13 | BorderedRect() 14 | .foregroundColor(.init(white: 0, opacity: 0.2)) 15 | .overlay( 16 | BorderedRect() 17 | .foregroundColor(.init(white: 0, opacity: 0.2)) 18 | .padding(10) 19 | ) 20 | .padding(10) 21 | ) 22 | 23 | } 24 | } 25 | 26 | struct BorderedRect: View { 27 | 28 | var body: some View { 29 | Rectangle() 30 | .border(Color(white: 0, opacity: 0.2), width: 3) 31 | .foregroundColor(.init(white: 0, opacity: 0.2)) 32 | } 33 | } 34 | 35 | enum Preview: PreviewProvider { 36 | 37 | static var previews: some View { 38 | 39 | Group { 40 | 41 | Group { 42 | VStack { 43 | Text("Financial Results") 44 | .font(.title) 45 | .background(BorderedRect()) 46 | 47 | HStack(alignment: .center) { 48 | Text("Q1 Sales") 49 | .font(.headline) 50 | .frame(maxHeight: .infinity) 51 | .background(BorderedRect()) 52 | 53 | 54 | VStack { 55 | Text("January") 56 | Text("February") 57 | Text("March") 58 | } 59 | .background(BorderedRect()) 60 | 61 | VStack { 62 | Text("$1000") 63 | Text("$200") 64 | Text("$3000") 65 | } 66 | .background(BorderedRect()) 67 | } 68 | } 69 | } 70 | 71 | ContentView() 72 | 73 | } 74 | } 75 | 76 | } 77 | 78 | } 79 | -------------------------------------------------------------------------------- /Demo_MondrianLayout/UIColor+Mondrian.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | extension UIColor { 4 | 5 | static var mondrianRed: UIColor = #colorLiteral(red: 1, green: 0.5661777854, blue: 0.3006193042, alpha: 1) 6 | static var mondrianBlue: UIColor = #colorLiteral(red: 0.09076789767, green: 0.3224385977, blue: 0.9202803969, alpha: 1) 7 | static var mondrianCyan: UIColor = #colorLiteral(red: 0, green: 0.856479466, blue: 1, alpha: 1) 8 | static var mondrianYellow: UIColor = #colorLiteral(red: 1, green: 0.4053340554, blue: 0, alpha: 1) 9 | static var mondrianGray: UIColor = #colorLiteral(red: 0.6430723071, green: 0.6431827545, blue: 0.6430577636, alpha: 1) 10 | static var layeringColor: UIColor { 11 | return .init(white: 0, alpha: 0.2) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | gem "fastlane" 4 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | CFPropertyList (3.0.3) 5 | addressable (2.7.0) 6 | public_suffix (>= 2.0.2, < 5.0) 7 | artifactory (3.0.15) 8 | atomos (0.1.3) 9 | aws-eventstream (1.1.1) 10 | aws-partitions (1.467.0) 11 | aws-sdk-core (3.114.2) 12 | aws-eventstream (~> 1, >= 1.0.2) 13 | aws-partitions (~> 1, >= 1.239.0) 14 | aws-sigv4 (~> 1.1) 15 | jmespath (~> 1.0) 16 | aws-sdk-kms (1.43.0) 17 | aws-sdk-core (~> 3, >= 3.112.0) 18 | aws-sigv4 (~> 1.1) 19 | aws-sdk-s3 (1.96.1) 20 | aws-sdk-core (~> 3, >= 3.112.0) 21 | aws-sdk-kms (~> 1) 22 | aws-sigv4 (~> 1.1) 23 | aws-sigv4 (1.2.3) 24 | aws-eventstream (~> 1, >= 1.0.2) 25 | babosa (1.0.4) 26 | claide (1.0.3) 27 | colored (1.2) 28 | colored2 (3.1.2) 29 | commander (4.6.0) 30 | highline (~> 2.0.0) 31 | declarative (0.0.20) 32 | digest-crc (0.6.3) 33 | rake (>= 12.0.0, < 14.0.0) 34 | domain_name (0.5.20190701) 35 | unf (>= 0.0.5, < 1.0.0) 36 | dotenv (2.7.6) 37 | emoji_regex (3.2.2) 38 | excon (0.82.0) 39 | faraday (1.4.2) 40 | faraday-em_http (~> 1.0) 41 | faraday-em_synchrony (~> 1.0) 42 | faraday-excon (~> 1.1) 43 | faraday-net_http (~> 1.0) 44 | faraday-net_http_persistent (~> 1.1) 45 | multipart-post (>= 1.2, < 3) 46 | ruby2_keywords (>= 0.0.4) 47 | faraday-cookie_jar (0.0.7) 48 | faraday (>= 0.8.0) 49 | http-cookie (~> 1.0.0) 50 | faraday-em_http (1.0.0) 51 | faraday-em_synchrony (1.0.0) 52 | faraday-excon (1.1.0) 53 | faraday-net_http (1.0.1) 54 | faraday-net_http_persistent (1.1.0) 55 | faraday_middleware (1.0.0) 56 | faraday (~> 1.0) 57 | fastimage (2.2.4) 58 | fastlane (2.185.1) 59 | CFPropertyList (>= 2.3, < 4.0.0) 60 | addressable (>= 2.3, < 3.0.0) 61 | artifactory (~> 3.0) 62 | aws-sdk-s3 (~> 1.0) 63 | babosa (>= 1.0.3, < 2.0.0) 64 | bundler (>= 1.12.0, < 3.0.0) 65 | colored 66 | commander (~> 4.6) 67 | dotenv (>= 2.1.1, < 3.0.0) 68 | emoji_regex (>= 0.1, < 4.0) 69 | excon (>= 0.71.0, < 1.0.0) 70 | faraday (~> 1.0) 71 | faraday-cookie_jar (~> 0.0.6) 72 | faraday_middleware (~> 1.0) 73 | fastimage (>= 2.1.0, < 3.0.0) 74 | gh_inspector (>= 1.1.2, < 2.0.0) 75 | google-apis-androidpublisher_v3 (~> 0.1) 76 | google-apis-playcustomapp_v1 (~> 0.1) 77 | google-cloud-storage (~> 1.31) 78 | highline (~> 2.0) 79 | json (< 3.0.0) 80 | jwt (>= 2.1.0, < 3) 81 | mini_magick (>= 4.9.4, < 5.0.0) 82 | multipart-post (~> 2.0.0) 83 | naturally (~> 2.2) 84 | plist (>= 3.1.0, < 4.0.0) 85 | rubyzip (>= 2.0.0, < 3.0.0) 86 | security (= 0.1.3) 87 | simctl (~> 1.6.3) 88 | terminal-notifier (>= 2.0.0, < 3.0.0) 89 | terminal-table (>= 1.4.5, < 2.0.0) 90 | tty-screen (>= 0.6.3, < 1.0.0) 91 | tty-spinner (>= 0.8.0, < 1.0.0) 92 | word_wrap (~> 1.0.0) 93 | xcodeproj (>= 1.13.0, < 2.0.0) 94 | xcpretty (~> 0.3.0) 95 | xcpretty-travis-formatter (>= 0.0.3) 96 | gh_inspector (1.1.3) 97 | google-apis-androidpublisher_v3 (0.6.0) 98 | google-apis-core (~> 0.1) 99 | google-apis-core (0.3.0) 100 | addressable (~> 2.5, >= 2.5.1) 101 | googleauth (~> 0.14) 102 | httpclient (>= 2.8.1, < 3.0) 103 | mini_mime (~> 1.0) 104 | representable (~> 3.0) 105 | retriable (>= 2.0, < 4.0) 106 | rexml 107 | signet (~> 0.14) 108 | webrick 109 | google-apis-iamcredentials_v1 (0.4.0) 110 | google-apis-core (~> 0.1) 111 | google-apis-playcustomapp_v1 (0.3.0) 112 | google-apis-core (~> 0.1) 113 | google-apis-storage_v1 (0.4.0) 114 | google-apis-core (~> 0.1) 115 | google-cloud-core (1.6.0) 116 | google-cloud-env (~> 1.0) 117 | google-cloud-errors (~> 1.0) 118 | google-cloud-env (1.5.0) 119 | faraday (>= 0.17.3, < 2.0) 120 | google-cloud-errors (1.1.0) 121 | google-cloud-storage (1.31.1) 122 | addressable (~> 2.5) 123 | digest-crc (~> 0.4) 124 | google-apis-iamcredentials_v1 (~> 0.1) 125 | google-apis-storage_v1 (~> 0.1) 126 | google-cloud-core (~> 1.2) 127 | googleauth (~> 0.9) 128 | mini_mime (~> 1.0) 129 | googleauth (0.16.2) 130 | faraday (>= 0.17.3, < 2.0) 131 | jwt (>= 1.4, < 3.0) 132 | memoist (~> 0.16) 133 | multi_json (~> 1.11) 134 | os (>= 0.9, < 2.0) 135 | signet (~> 0.14) 136 | highline (2.0.3) 137 | http-cookie (1.0.4) 138 | domain_name (~> 0.5) 139 | httpclient (2.8.3) 140 | jmespath (1.4.0) 141 | json (2.5.1) 142 | jwt (2.2.3) 143 | memoist (0.16.2) 144 | mini_magick (4.11.0) 145 | mini_mime (1.1.0) 146 | multi_json (1.15.0) 147 | multipart-post (2.0.0) 148 | nanaimo (0.3.0) 149 | naturally (2.2.1) 150 | os (1.1.1) 151 | plist (3.6.0) 152 | public_suffix (4.0.6) 153 | rake (13.0.3) 154 | representable (3.1.1) 155 | declarative (< 0.1.0) 156 | trailblazer-option (>= 0.1.1, < 0.2.0) 157 | uber (< 0.2.0) 158 | retriable (3.1.2) 159 | rexml (3.2.5) 160 | rouge (2.0.7) 161 | ruby2_keywords (0.0.4) 162 | rubyzip (2.3.0) 163 | security (0.1.3) 164 | signet (0.15.0) 165 | addressable (~> 2.3) 166 | faraday (>= 0.17.3, < 2.0) 167 | jwt (>= 1.5, < 3.0) 168 | multi_json (~> 1.10) 169 | simctl (1.6.8) 170 | CFPropertyList 171 | naturally 172 | terminal-notifier (2.0.0) 173 | terminal-table (1.8.0) 174 | unicode-display_width (~> 1.1, >= 1.1.1) 175 | trailblazer-option (0.1.1) 176 | tty-cursor (0.7.1) 177 | tty-screen (0.8.1) 178 | tty-spinner (0.9.3) 179 | tty-cursor (~> 0.7) 180 | uber (0.1.0) 181 | unf (0.1.4) 182 | unf_ext 183 | unf_ext (0.0.7.7) 184 | unicode-display_width (1.7.0) 185 | webrick (1.7.0) 186 | word_wrap (1.0.0) 187 | xcodeproj (1.19.0) 188 | CFPropertyList (>= 2.3.3, < 4.0) 189 | atomos (~> 0.1.3) 190 | claide (>= 1.0.2, < 2.0) 191 | colored2 (~> 3.1) 192 | nanaimo (~> 0.3.0) 193 | xcpretty (0.3.0) 194 | rouge (~> 2.0.7) 195 | xcpretty-travis-formatter (1.0.1) 196 | xcpretty (~> 0.2, >= 0.0.7) 197 | 198 | PLATFORMS 199 | ruby 200 | 201 | DEPENDENCIES 202 | fastlane 203 | 204 | BUNDLED WITH 205 | 2.1.4 206 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 muukii 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 | -------------------------------------------------------------------------------- /MondrianLayout.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |spec| 2 | spec.name = "MondrianLayout" 3 | spec.version = "0.10.0" 4 | spec.summary = "A DSL based layout builder for AutoLayout" 5 | spec.description = <<-DESC 6 | This library is a layout builder by DSL described for AutoLayout. 7 | DESC 8 | 9 | spec.homepage = "https://github.com/muukii/MondrianLayout" 10 | spec.license = "MIT" 11 | spec.author = { "Muukii" => "muukii.app@gmail.com" } 12 | spec.social_media_url = "https://twitter.com/muukii_app" 13 | 14 | spec.ios.deployment_target = "12.0" 15 | # spec.osx.deployment_target = "10.7" 16 | # spec.watchos.deployment_target = "2.0" 17 | # spec.tvos.deployment_target = "9.0" 18 | 19 | spec.source = { :git => "https://github.com/muukii/MondrianLayout.git", :tag => "#{spec.version}" } 20 | spec.source_files = "MondrianLayout/**/*.swift" 21 | spec.framework = "UIKit" 22 | spec.requires_arc = true 23 | spec.swift_versions = ["5.3", "5.4", "5.5"] 24 | end 25 | -------------------------------------------------------------------------------- /MondrianLayout.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /MondrianLayout.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /MondrianLayout.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "object": { 3 | "pins": [ 4 | { 5 | "package": "Ne", 6 | "repositoryURL": "https://github.com/muukii/Ne.git", 7 | "state": { 8 | "branch": null, 9 | "revision": "6555f94596b634c14bd0818265ef8c86bb3005f3", 10 | "version": "1.0.1" 11 | } 12 | }, 13 | { 14 | "package": "Storybook", 15 | "repositoryURL": "https://github.com/eure/Storybook-ios.git", 16 | "state": { 17 | "branch": null, 18 | "revision": "cd9238e7800ead2a120e81291bbbf5c0012131c8", 19 | "version": "1.8.0" 20 | } 21 | }, 22 | { 23 | "package": "SnapshotTesting", 24 | "repositoryURL": "https://github.com/pointfreeco/swift-snapshot-testing.git", 25 | "state": { 26 | "branch": null, 27 | "revision": "f8a9c997c3c1dab4e216a8ec9014e23144cbab37", 28 | "version": "1.9.0" 29 | } 30 | } 31 | ] 32 | }, 33 | "version": 1 34 | } 35 | -------------------------------------------------------------------------------- /MondrianLayout.xcodeproj/xcshareddata/xcschemes/Demo_MondrianLayout.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 44 | 45 | 47 | 53 | 54 | 55 | 56 | 57 | 67 | 69 | 75 | 76 | 77 | 78 | 84 | 86 | 92 | 93 | 94 | 95 | 97 | 98 | 101 | 102 | 103 | -------------------------------------------------------------------------------- /MondrianLayout.xcodeproj/xcshareddata/xcschemes/MondrianLayout.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 44 | 45 | 47 | 53 | 54 | 55 | 56 | 57 | 67 | 68 | 74 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /MondrianLayout.xcodeproj/xcshareddata/xcschemes/Tests-Record.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 35 | 36 | 37 | 38 | 40 | 46 | 47 | 48 | 49 | 50 | 60 | 61 | 67 | 68 | 74 | 75 | 76 | 77 | 79 | 80 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /MondrianLayout.xcodeproj/xcshareddata/xcschemes/Tests.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 53 | 54 | 60 | 61 | 67 | 68 | 69 | 70 | 72 | 73 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /MondrianLayout/Classic/MondrianArrayBuilder.swift: -------------------------------------------------------------------------------- 1 | 2 | func buildArray(type: T.Type, @MondrianArrayBuilder _ build: () -> [T]) -> [T] { 3 | build() 4 | } 5 | 6 | @resultBuilder 7 | public struct MondrianArrayBuilder { 8 | 9 | public static func buildBlock() -> [Element] { 10 | [] 11 | } 12 | 13 | public static func buildBlock(_ contents: C...) -> [Element] where C.Element == Element { 14 | return contents.flatMap { $0 } 15 | } 16 | 17 | public static func buildOptional(_ component: [Element]?) -> [Element] { 18 | return component ?? [] 19 | } 20 | 21 | public static func buildEither(first component: [Element]) -> [Element] { 22 | return component 23 | } 24 | 25 | public static func buildEither(second component: [Element]) -> [Element] { 26 | return component 27 | } 28 | 29 | public static func buildArray(_ components: [[Element]]) -> [Element] { 30 | components.flatMap { $0 } 31 | } 32 | 33 | public static func buildExpression(_ element: Element?) -> [Element] { 34 | return element.map { [$0] } ?? [] 35 | } 36 | 37 | public static func buildExpression(_ element: Element) -> [Element] { 38 | return [element] 39 | } 40 | 41 | public static func buildExpression(_ elements: C) -> [Element] where C.Element == Element { 42 | Array(elements) 43 | } 44 | 45 | public static func buildExpression(_ elements: C) -> [Element] where C.Element == Optional { 46 | elements.compactMap { $0 } 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /MondrianLayout/Descriptors/Edge.swift: -------------------------------------------------------------------------------- 1 | 2 | public enum Edge: Int8, CaseIterable { 3 | 4 | case top = 0 5 | case leading = 1 6 | case bottom = 2 7 | case trailing = 3 8 | 9 | public struct Set: OptionSet { 10 | 11 | public var rawValue: Int8 12 | public var isEmpty: Bool { 13 | rawValue == 0 14 | } 15 | 16 | public init(rawValue: Int8) { 17 | self.rawValue = rawValue 18 | } 19 | 20 | public static let top: Set = .init(rawValue: 1 << 1) 21 | 22 | /// In LTR - meaning `left` 23 | public static let leading: Set = .init(rawValue: 1 << 2) 24 | 25 | public static let bottom: Set = .init(rawValue: 1 << 3) 26 | 27 | /// In LTR - meaning `right` 28 | public static let trailing: Set = .init(rawValue: 1 << 4) 29 | 30 | public static var horizontal: Set { 31 | [.leading, .trailing] 32 | } 33 | 34 | public static var vertical: Set { 35 | [.top, .bottom] 36 | } 37 | 38 | public static var all: Set { 39 | [.top, .bottom, .trailing, leading] 40 | } 41 | 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /MondrianLayout/Descriptors/LayoutBuilderContext.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | /** 4 | A building layout enviroment 5 | - constraints 6 | - layout guides 7 | - tasks apply to view (setting content hugging and compression resistance) 8 | */ 9 | public final class LayoutBuilderContext { 10 | 11 | public weak var targetView: UIView? 12 | public let name: String? 13 | public private(set) var isActive = false 14 | 15 | public init( 16 | name: String? = nil, 17 | targetView: UIView 18 | ) { 19 | self.name = name 20 | self.targetView = targetView 21 | } 22 | 23 | public private(set) var managedLayoutGuides: [UILayoutGuide] = [] 24 | public private(set) var constraints: [NSLayoutConstraint] = [] 25 | public private(set) var viewBlocks: [ViewBlock] = [] 26 | public private(set) var unmanagedLayoutGuides: [LayoutGuideBlock] = [] 27 | public private(set) var viewAppliers: [() -> Void] = [] 28 | 29 | func add(constraints: [NSLayoutConstraint]) { 30 | self.constraints.append(contentsOf: constraints) 31 | } 32 | 33 | func makeLayoutGuide(identifier: String) -> UILayoutGuide { 34 | 35 | let guide = UILayoutGuide() 36 | if let name = name { 37 | guide.identifier = "\(identifier):\(name)" 38 | } else { 39 | guide.identifier = identifier 40 | } 41 | 42 | managedLayoutGuides.append(guide) 43 | return guide 44 | } 45 | 46 | func register(viewBlock: ViewBlock) { 47 | assert(viewBlocks.contains(where: { $0.view == viewBlock.view }) == false) 48 | 49 | viewBlocks.append(viewBlock) 50 | constraints.append(contentsOf: viewBlock.makeConstraints()) 51 | viewAppliers.append(viewBlock.makeApplier()) 52 | } 53 | 54 | func register(layoutGuideBlock: LayoutGuideBlock) { 55 | unmanagedLayoutGuides.append(layoutGuideBlock) 56 | constraints.append(contentsOf: layoutGuideBlock.makeConstraints()) 57 | } 58 | 59 | /// Add including views to the target view. 60 | public func prepareViewHierarchy() { 61 | 62 | guard let targetView = targetView else { 63 | return 64 | } 65 | 66 | viewBlocks.forEach { 67 | $0.view.translatesAutoresizingMaskIntoConstraints = false 68 | targetView.addSubview($0.view) 69 | } 70 | } 71 | 72 | /** 73 | Activate constraints and layout guides. 74 | */ 75 | public func activate() { 76 | 77 | assert(Thread.isMainThread) 78 | 79 | guard let targetView = targetView else { 80 | return 81 | } 82 | 83 | guard isActive == false else { 84 | return 85 | } 86 | 87 | isActive = true 88 | 89 | viewAppliers.forEach { $0() } 90 | 91 | managedLayoutGuides.forEach { 92 | targetView.addLayoutGuide($0) 93 | } 94 | 95 | unmanagedLayoutGuides.forEach { 96 | targetView.addLayoutGuide($0.layoutGuide) 97 | } 98 | 99 | NSLayoutConstraint.activate(constraints) 100 | 101 | } 102 | 103 | /** 104 | Deactivate constraints and layout guides. 105 | */ 106 | public func deactivate() { 107 | 108 | guard let targetView = targetView else { 109 | return 110 | } 111 | 112 | guard isActive == true else { 113 | return 114 | } 115 | 116 | isActive = false 117 | 118 | managedLayoutGuides.forEach { 119 | targetView.removeLayoutGuide($0) 120 | } 121 | 122 | unmanagedLayoutGuides.forEach { 123 | targetView.removeLayoutGuide($0.layoutGuide) 124 | } 125 | 126 | NSLayoutConstraint.deactivate(constraints) 127 | } 128 | 129 | } 130 | 131 | -------------------------------------------------------------------------------- /MondrianLayout/Descriptors/Optimization.swift: -------------------------------------------------------------------------------- 1 | import CoreGraphics 2 | 3 | extension Array where Element : _StackElementNodeType { 4 | 5 | /// merging continuous spacing into one 6 | func optimizedSpacing() -> [Element] { 7 | 8 | var spacing: CGFloat = 0 9 | var expands: Bool = false 10 | 11 | /** 12 | 13 | __X_ _X___X__ 14 | 15 | _X_X_X 16 | 17 | */ 18 | 19 | var array: [Element] = [] 20 | 21 | for element in self { 22 | 23 | if let spacer = element._spacer { 24 | 25 | spacing += spacer.minLength 26 | expands = spacer.expands ? true : expands 27 | 28 | continue 29 | } 30 | 31 | if let content = element._content { 32 | 33 | spacing += content.spacingBefore ?? 0 34 | 35 | if spacing > 0 || expands { 36 | array.append(.spacer(.init(minLength: spacing, expands: expands))) 37 | } 38 | array.append(element) 39 | 40 | spacing = content.spacingAfter ?? 0 41 | expands = false 42 | 43 | continue 44 | } 45 | 46 | preconditionFailure() 47 | 48 | } 49 | 50 | if spacing > 0 || expands { 51 | array.append(.spacer(.init(minLength: spacing, expands: expands))) 52 | } 53 | 54 | return array 55 | 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /MondrianLayout/Descriptors/_LayoutBlockNode.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | public protocol _LayoutBlockNodeConvertible: _VStackItemConvertible, _HStackItemConvertible, _ZStackItemConvertible { 4 | var _layoutBlockNode: _LayoutBlockNode { get } 5 | } 6 | 7 | extension _LayoutBlockNodeConvertible { 8 | public var _vStackItem: _VStackItem { 9 | return .init(node: _layoutBlockNode) 10 | } 11 | 12 | public var _hStackItem: _HStackItem { 13 | return .init(node: _layoutBlockNode) 14 | } 15 | 16 | public var _zStackItem: _ZStackItem { 17 | return .init(node: _layoutBlockNode) 18 | } 19 | } 20 | 21 | public indirect enum _LayoutBlockNode: _LayoutBlockNodeConvertible { 22 | 23 | case view(ViewBlock) 24 | case layoutGuide(LayoutGuideBlock) 25 | case vStack(VStackBlock) 26 | case hStack(HStackBlock) 27 | case zStack(ZStackBlock) 28 | case relative(RelativeBlock) 29 | case overlay(OverlayBlock) 30 | case background(BackgroundBlock) 31 | case vGrid(VGridBlock) 32 | 33 | public var _layoutBlockNode: _LayoutBlockNode { self } 34 | } 35 | 36 | extension _LayoutBlockNodeConvertible { 37 | 38 | public func background(_ view: UIView) -> BackgroundBlock { 39 | return .init(content: _layoutBlockNode, backgroundContent: .view(view.viewBlock)) 40 | } 41 | 42 | public func background(_ block: Block) -> BackgroundBlock { 43 | return .init(content: _layoutBlockNode, backgroundContent: block._layoutBlockNode) 44 | } 45 | 46 | public func overlay(_ view: UIView) -> OverlayBlock { 47 | return .init(content: _layoutBlockNode, overlayContent: .view(view.viewBlock)) 48 | } 49 | 50 | public func overlay(_ block: Block) -> OverlayBlock { 51 | return .init(content: _layoutBlockNode, overlayContent: block._layoutBlockNode) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /MondrianLayout/Descriptors/_LayoutBlockType.swift: -------------------------------------------------------------------------------- 1 | 2 | public protocol _LayoutBlockType: _LayoutBlockNodeConvertible { 3 | 4 | var name: String { get } 5 | func setupConstraints(parent: _LayoutElement, in context: LayoutBuilderContext) 6 | } 7 | -------------------------------------------------------------------------------- /MondrianLayout/EntryPoint.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | public enum Mondrian { 4 | 5 | @discardableResult 6 | public static func layout( 7 | @MondrianArrayBuilder _ closure: () -> [LayoutDescriptor] 8 | ) -> ConstraintGroup { 9 | 10 | let descriptors = closure() 11 | 12 | let group = ConstraintGroup(constraints: []) 13 | 14 | descriptors.forEach { 15 | let g = $0.activate() 16 | group.append(g) 17 | } 18 | 19 | return group 20 | 21 | } 22 | 23 | /** 24 | Builds subviews of this view. 25 | - activating layout-constraints 26 | - adding layout-guides 27 | - applying content-hugging, content-compression-resistance 28 | 29 | You can start to describe like followings: 30 | 31 | ```swift 32 | Mondrian.buildSubviews(on: view) { 33 | ZStackBlock { 34 | ... 35 | } 36 | } 37 | ``` 38 | 39 | ```swift 40 | Mondrian.buildSubviews(on: view) { 41 | LayoutContainer(attachedSafeAreaEdges: .vertical) { 42 | ... 43 | } 44 | } 45 | ``` 46 | 47 | 48 | ```swift 49 | Mondrian.buildSubviews(on: view) { 50 | LayoutContainer(attachedSafeAreaEdges: .vertical) { 51 | ... 52 | } 53 | ZStackBlock { 54 | ... 55 | } 56 | } 57 | ``` 58 | */ 59 | @discardableResult 60 | public static func buildSubviews( 61 | on view: UIView, 62 | @EntrypointBuilder _ build: () -> [EntrypointBuilder.Either] 63 | ) -> LayoutBuilderContext { 64 | 65 | let entrypoints = build() 66 | 67 | let context = LayoutBuilderContext(targetView: view) 68 | 69 | for entrypoint in entrypoints { 70 | switch entrypoint { 71 | case .block(let block): 72 | block.setupConstraints(parent: .init(view: view), in: context) 73 | case .container(let container): 74 | container.setupConstraints(parent: view, in: context) 75 | } 76 | } 77 | 78 | context.prepareViewHierarchy() 79 | context.activate() 80 | 81 | return context 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /MondrianLayout/Extensions/NSLayoutConstraint+.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | extension NSLayoutConstraint { 4 | 5 | @discardableResult 6 | func setInternalIdentifier(_ string: String) -> NSLayoutConstraint { 7 | self.identifier = "BoxLayout." + string 8 | return self 9 | } 10 | 11 | @discardableResult 12 | func setIdentifier(_ string: String) -> NSLayoutConstraint { 13 | self.identifier = string 14 | return self 15 | } 16 | 17 | @discardableResult 18 | func setPriority(_ priority: UILayoutPriority) -> NSLayoutConstraint { 19 | self.priority = priority 20 | return self 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /MondrianLayout/Extensions/UILayoutGuide+Mondrian.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | extension UILayoutGuide { 4 | 5 | public var mondrian: MondrianNamespace { 6 | return .init(base: self) 7 | } 8 | 9 | public var layoutGuideBlock: LayoutGuideBlock { 10 | .init(self) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /MondrianLayout/Extensions/UIView+Mondrian.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | @resultBuilder 4 | public enum EntrypointBuilder { 5 | 6 | public enum Either { 7 | case block(_LayoutBlockType) 8 | case container(LayoutContainer) 9 | } 10 | 11 | public static func buildBlock() -> [EntrypointBuilder.Either] { 12 | return [] 13 | } 14 | 15 | public static func buildBlock(_ components: [Either]...) -> [Either] { 16 | return components.flatMap { $0 } 17 | } 18 | 19 | public static func buildExpression(_ components: Block...) -> [Either] { 20 | return components.map { .block($0) } 21 | } 22 | 23 | public static func buildExpression(_ components: LayoutContainer...) -> [EntrypointBuilder.Either] { 24 | return components.map { .container($0) } 25 | } 26 | 27 | public static func buildEither(first component: [EntrypointBuilder.Either]) -> [EntrypointBuilder.Either] { 28 | return component 29 | } 30 | 31 | public static func buildEither(second component: [EntrypointBuilder.Either]) -> [EntrypointBuilder.Either] { 32 | return component 33 | } 34 | 35 | } 36 | 37 | extension MondrianNamespace where Base : UIView { 38 | 39 | @available(*, deprecated, message: "Use Mondrian.buildSubviews") 40 | @discardableResult 41 | public func buildSubviews(@EntrypointBuilder _ build: () -> [EntrypointBuilder.Either]) -> LayoutBuilderContext { 42 | Mondrian.buildSubviews(on: base, build) 43 | } 44 | 45 | @available(*, deprecated, message: "Use classic layout") 46 | /// Applies the layout of the dimension in itself. 47 | public func buildSelfSizing(build: (ViewBlock) -> ViewBlock) { 48 | 49 | let constraint = ViewBlock(base) 50 | let modifiedConstraint = build(constraint) 51 | 52 | modifiedConstraint.makeApplier()() 53 | NSLayoutConstraint.activate( 54 | modifiedConstraint.makeConstraints() 55 | ) 56 | 57 | } 58 | 59 | } 60 | 61 | extension UIView { 62 | 63 | public var mondrian: MondrianNamespace { 64 | return .init(base: self) 65 | } 66 | 67 | } 68 | 69 | 70 | 71 | extension UIView { 72 | 73 | /// Returns an instance of ViewBlock to describe layout. 74 | public var viewBlock: ViewBlock { 75 | .init(self) 76 | } 77 | 78 | public var hasAmbiguousLayoutRecursively: Bool { 79 | 80 | var hasAmbiguous: Bool = false 81 | 82 | func traverse(_ view: UIView) { 83 | 84 | if view.hasAmbiguousLayout { 85 | hasAmbiguous = true 86 | } 87 | 88 | for subview in view.subviews { 89 | traverse(subview) 90 | } 91 | 92 | } 93 | 94 | traverse(self) 95 | 96 | return hasAmbiguous 97 | } 98 | 99 | } 100 | -------------------------------------------------------------------------------- /MondrianLayout/Internal/Utils.swift: -------------------------------------------------------------------------------- 1 | 2 | func modify(_ value: inout Value, _ modifier: (inout Value) throws -> Void) rethrows { 3 | try modifier(&value) 4 | } 5 | 6 | func modified(_ value: Value, _ modifier: (inout Value) throws -> Void) rethrows -> Value { 7 | var new = value 8 | try modifier(&new) 9 | return new 10 | } 11 | -------------------------------------------------------------------------------- /MondrianLayout/Internal/_LayoutElement.swift: -------------------------------------------------------------------------------- 1 | 2 | import UIKit 3 | 4 | /// MondrianLayout internal protocol 5 | public protocol __LayoutElementConvertible { 6 | var _layoutElement: _LayoutElement { get } 7 | } 8 | 9 | extension UIView: __LayoutElementConvertible { 10 | /// MondrianLayout internal property 11 | public var _layoutElement: _LayoutElement { 12 | return .init(view: self) 13 | } 14 | } 15 | 16 | extension UILayoutGuide: __LayoutElementConvertible { 17 | /// MondrianLayout internal property 18 | public var _layoutElement: _LayoutElement { 19 | return .init(layoutGuide: self) 20 | } 21 | } 22 | 23 | /** 24 | Abstraction of element that can be laied out by layout anchors. 25 | */ 26 | public struct _LayoutElement: __LayoutElementConvertible { 27 | 28 | public enum XAxisAnchor: Equatable { 29 | case right 30 | case left 31 | case leading 32 | case trailing 33 | case centerX 34 | } 35 | 36 | public enum DimensionAnchor: Equatable { 37 | case width 38 | case height 39 | } 40 | 41 | public enum YAxisAnchor: Equatable { 42 | case top 43 | case bottom 44 | case centerY 45 | } 46 | 47 | public var _layoutElement: _LayoutElement { 48 | self 49 | } 50 | 51 | let leadingAnchor: NSLayoutXAxisAnchor 52 | let trailingAnchor: NSLayoutXAxisAnchor 53 | let leftAnchor: NSLayoutXAxisAnchor 54 | let rightAnchor: NSLayoutXAxisAnchor 55 | let topAnchor: NSLayoutYAxisAnchor 56 | let bottomAnchor: NSLayoutYAxisAnchor 57 | let widthAnchor: NSLayoutDimension 58 | let heightAnchor: NSLayoutDimension 59 | let centerXAnchor: NSLayoutXAxisAnchor 60 | let centerYAnchor: NSLayoutYAxisAnchor 61 | 62 | var owningView: UIView? { 63 | return view?.superview ?? layoutGuide?.owningView 64 | } 65 | 66 | let view: UIView? 67 | let layoutGuide: UILayoutGuide? 68 | 69 | public init(view: UIView) { 70 | 71 | self.view = view 72 | self.layoutGuide = nil 73 | 74 | leadingAnchor = view.leadingAnchor 75 | trailingAnchor = view.trailingAnchor 76 | leftAnchor = view.leftAnchor 77 | rightAnchor = view.rightAnchor 78 | topAnchor = view.topAnchor 79 | bottomAnchor = view.bottomAnchor 80 | widthAnchor = view.widthAnchor 81 | heightAnchor = view.heightAnchor 82 | centerXAnchor = view.centerXAnchor 83 | centerYAnchor = view.centerYAnchor 84 | } 85 | 86 | public init(layoutGuide: UILayoutGuide) { 87 | 88 | self.view = nil 89 | self.layoutGuide = layoutGuide 90 | 91 | leadingAnchor = layoutGuide.leadingAnchor 92 | trailingAnchor = layoutGuide.trailingAnchor 93 | leftAnchor = layoutGuide.leftAnchor 94 | rightAnchor = layoutGuide.rightAnchor 95 | topAnchor = layoutGuide.topAnchor 96 | bottomAnchor = layoutGuide.bottomAnchor 97 | widthAnchor = layoutGuide.widthAnchor 98 | heightAnchor = layoutGuide.heightAnchor 99 | centerXAnchor = layoutGuide.centerXAnchor 100 | centerYAnchor = layoutGuide.centerYAnchor 101 | 102 | } 103 | 104 | func anchor(_ type: XAxisAnchor) -> NSLayoutXAxisAnchor { 105 | switch type { 106 | case .right: 107 | return rightAnchor 108 | case .left: 109 | return leftAnchor 110 | case .leading: 111 | return leadingAnchor 112 | case .trailing: 113 | return trailingAnchor 114 | case .centerX: 115 | return centerXAnchor 116 | } 117 | } 118 | 119 | func anchor(_ type: YAxisAnchor) -> NSLayoutYAxisAnchor { 120 | switch type { 121 | case .top: 122 | return topAnchor 123 | case .bottom: 124 | return bottomAnchor 125 | case .centerY: 126 | return centerYAnchor 127 | } 128 | } 129 | 130 | func anchor(_ type: DimensionAnchor) -> NSLayoutDimension { 131 | switch type { 132 | case .width: 133 | return widthAnchor 134 | case .height: 135 | return heightAnchor 136 | } 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /MondrianLayout/LayoutBlocks/BackgroundBlock.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | /// [MondrianLayout] 4 | /// A descriptor that lays out the content and background content in the parent layout element. 5 | public struct BackgroundBlock: 6 | _LayoutBlockType 7 | { 8 | 9 | // MARK: - Properties 10 | 11 | public var name: String = "Background" 12 | 13 | public var _layoutBlockNode: _LayoutBlockNode { 14 | .background(self) 15 | } 16 | 17 | let content: _LayoutBlockNode 18 | let backgroundContent: _LayoutBlockNode 19 | 20 | // MARK: - Initializers 21 | 22 | init( 23 | content: _LayoutBlockNode, 24 | backgroundContent: _LayoutBlockNode 25 | ) { 26 | 27 | self.content = content 28 | self.backgroundContent = backgroundContent 29 | } 30 | 31 | // MARK: - Functions 32 | 33 | public func setupConstraints(parent: _LayoutElement, in context: LayoutBuilderContext) { 34 | 35 | setupBackground: do { 36 | 37 | switch backgroundContent { 38 | case .layoutGuide(let block): 39 | 40 | context.register(layoutGuideBlock: block) 41 | context.add( 42 | constraints: block.makeConstraintsToEdge(parent) 43 | ) 44 | 45 | case .view(let c): 46 | 47 | context.register(viewBlock: c) 48 | context.add( 49 | constraints: c.makeConstraintsToEdge(parent) 50 | ) 51 | 52 | case .relative(let c as _LayoutBlockType), 53 | .vStack(let c as _LayoutBlockType), 54 | .hStack(let c as _LayoutBlockType), 55 | .zStack(let c as _LayoutBlockType), 56 | .overlay(let c as _LayoutBlockType), 57 | .background(let c as _LayoutBlockType), 58 | .vGrid(let c as _LayoutBlockType): 59 | 60 | let backgroundLayoutGuide = context.makeLayoutGuide(identifier: "Background") 61 | 62 | context.add( 63 | constraints: 64 | backgroundLayoutGuide.mondrian.layout.edges(.to(parent)).makeConstraints() 65 | ) 66 | 67 | c.setupConstraints( 68 | parent: .init(layoutGuide: backgroundLayoutGuide), 69 | in: context 70 | ) 71 | } 72 | 73 | } 74 | 75 | setupContent: do { 76 | 77 | switch content { 78 | case .layoutGuide(let block): 79 | 80 | context.register(layoutGuideBlock: block) 81 | context.add( 82 | constraints: block.makeConstraintsToEdge(parent) 83 | ) 84 | 85 | case .view(let c): 86 | context.register(viewBlock: c) 87 | context.add( 88 | constraints: c.makeConstraintsToEdge(parent) 89 | ) 90 | case .relative(let c as _LayoutBlockType), 91 | .vStack(let c as _LayoutBlockType), 92 | .hStack(let c as _LayoutBlockType), 93 | .zStack(let c as _LayoutBlockType), 94 | .overlay(let c as _LayoutBlockType), 95 | .background(let c as _LayoutBlockType), 96 | .vGrid(let c as _LayoutBlockType): 97 | c.setupConstraints(parent: parent, in: context) 98 | } 99 | } 100 | 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /MondrianLayout/LayoutBlocks/LayoutGuideBlock.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | public struct LayoutGuideBlock: 4 | _LayoutBlockNodeConvertible, 5 | _DimensionConstraintType, 6 | Equatable 7 | { 8 | 9 | public var _layoutBlockNode: _LayoutBlockNode { 10 | return .layoutGuide(self) 11 | } 12 | 13 | public let layoutGuide: UILayoutGuide 14 | 15 | public var dimensionConstraints: DimensionDescriptor = .init() 16 | 17 | public init( 18 | _ layoutGuide: UILayoutGuide 19 | ) { 20 | self.layoutGuide = layoutGuide 21 | } 22 | 23 | func makeConstraintsToEdge(_ element: _LayoutElement) -> [NSLayoutConstraint] { 24 | return layoutGuide.mondrian.layout.edges(.to(element)).makeConstraints() 25 | } 26 | 27 | func makeConstraints() -> [NSLayoutConstraint] { 28 | dimensionConstraints.makeConstraints(for: layoutGuide) 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /MondrianLayout/LayoutBlocks/OverlayBlock.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | /// [MondrianLayout] 4 | /// A descriptor that lays out the content and overlay content in the parent layout element. 5 | public struct OverlayBlock: 6 | _LayoutBlockType 7 | { 8 | 9 | // MARK: - Properties 10 | 11 | public var name: String = "Overlay" 12 | 13 | public var _layoutBlockNode: _LayoutBlockNode { 14 | .overlay(self) 15 | } 16 | 17 | public let content: _LayoutBlockNode 18 | public let overlayContent: _LayoutBlockNode 19 | 20 | // MARK: - Initializers 21 | 22 | init( 23 | content: _LayoutBlockNode, 24 | overlayContent: _LayoutBlockNode 25 | ) { 26 | 27 | self.content = content 28 | self.overlayContent = overlayContent 29 | 30 | } 31 | 32 | // MARK: - Functions 33 | 34 | public func setupConstraints(parent: _LayoutElement, in context: LayoutBuilderContext) { 35 | 36 | setupContent: do { 37 | 38 | switch content { 39 | case .layoutGuide(let block): 40 | context.register(layoutGuideBlock: block) 41 | context.add( 42 | constraints: block.makeConstraintsToEdge(parent) 43 | ) 44 | case .view(let block): 45 | context.register(viewBlock: block) 46 | context.add( 47 | constraints: block.makeConstraintsToEdge(parent) 48 | ) 49 | case .relative(let c as _LayoutBlockType), 50 | .vStack(let c as _LayoutBlockType), 51 | .hStack(let c as _LayoutBlockType), 52 | .zStack(let c as _LayoutBlockType), 53 | .overlay(let c as _LayoutBlockType), 54 | .background(let c as _LayoutBlockType), 55 | .vGrid(let c as _LayoutBlockType): 56 | c.setupConstraints(parent: parent, in: context) 57 | } 58 | } 59 | 60 | setupOverlay: do { 61 | 62 | switch overlayContent { 63 | case .layoutGuide(let block): 64 | context.register(layoutGuideBlock: block) 65 | context.add( 66 | constraints: block.makeConstraintsToEdge(parent) 67 | ) 68 | case .view(let block): 69 | 70 | context.register(viewBlock: block) 71 | 72 | context.add( 73 | constraints: block.makeConstraintsToEdge(parent) 74 | ) 75 | 76 | case .relative(let c as _LayoutBlockType), 77 | .vStack(let c as _LayoutBlockType), 78 | .hStack(let c as _LayoutBlockType), 79 | .zStack(let c as _LayoutBlockType), 80 | .overlay(let c as _LayoutBlockType), 81 | .background(let c as _LayoutBlockType), 82 | .vGrid(let c as _LayoutBlockType): 83 | 84 | let overlayLayoutGuide = context.makeLayoutGuide(identifier: "Overlay") 85 | 86 | context.add( 87 | constraints: 88 | overlayLayoutGuide.mondrian.layout.edges(.to(parent)).makeConstraints() 89 | ) 90 | 91 | c.setupConstraints( 92 | parent: .init(layoutGuide: overlayLayoutGuide), 93 | in: context 94 | ) 95 | 96 | } 97 | 98 | } 99 | 100 | } 101 | 102 | } 103 | -------------------------------------------------------------------------------- /MondrianLayout/LayoutBlocks/StackingSpacer.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | /// A flexible space that expands along the major axis of its containing stack layout, or on both axes if not contained in a stack. 4 | /// Currently it does work only inside stacking. 5 | public struct StackingSpacer { 6 | 7 | public let minLength: CGFloat 8 | public let expands: Bool 9 | 10 | public init( 11 | minLength: CGFloat, 12 | expands: Bool = true 13 | ) { 14 | self.minLength = minLength 15 | self.expands = expands 16 | } 17 | } 18 | 19 | -------------------------------------------------------------------------------- /MondrianLayout/LayoutBlocks/ViewBlock.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | public struct AxisMask: OptionSet { 4 | 5 | public var rawValue: Int8 6 | public var isEmpty: Bool { 7 | rawValue == 0 8 | } 9 | 10 | public init(rawValue: Int8) { 11 | self.rawValue = rawValue 12 | } 13 | 14 | public static let vertical: Self = .init(rawValue: 1 << 1) 15 | public static let horizontal: Self = .init(rawValue: 1 << 2) 16 | } 17 | 18 | public struct ViewBlock: _LayoutBlockNodeConvertible, _DimensionConstraintType, 19 | Equatable 20 | { 21 | 22 | public var _layoutBlockNode: _LayoutBlockNode { 23 | return .view(self) 24 | } 25 | 26 | public let view: UIView 27 | 28 | public var dimensionConstraints: DimensionDescriptor = .init() 29 | 30 | var verticalHuggingPriority: UILayoutPriority? 31 | var horizontalHuggingPriority: UILayoutPriority? 32 | 33 | var verticalCompressionResistancePriority: UILayoutPriority? 34 | var horizontalCompressionResistancePriority: UILayoutPriority? 35 | 36 | public init( 37 | _ view: UIView 38 | ) { 39 | self.view = view 40 | } 41 | 42 | private func _modify(_ modifier: (inout Self) -> Void) -> Self { 43 | var new = self 44 | modifier(&new) 45 | return new 46 | } 47 | 48 | public func huggingPriority( 49 | _ axisMask: AxisMask, 50 | _ priority: UILayoutPriority = .required 51 | ) -> Self { 52 | _modify { 53 | if axisMask.contains(.horizontal) { 54 | $0.horizontalHuggingPriority = priority 55 | } 56 | if axisMask.contains(.vertical) { 57 | $0.verticalHuggingPriority = priority 58 | } 59 | } 60 | } 61 | 62 | public func compressionResistancePriority( 63 | _ axisMask: AxisMask, 64 | _ priority: UILayoutPriority = .required 65 | ) -> Self { 66 | _modify { 67 | if axisMask.contains(.horizontal) { 68 | $0.horizontalCompressionResistancePriority = priority 69 | } 70 | if axisMask.contains(.vertical) { 71 | $0.verticalCompressionResistancePriority = priority 72 | } 73 | } 74 | } 75 | 76 | func makeConstraintsToEdge(_ element: _LayoutElement) -> [NSLayoutConstraint] { 77 | return 78 | [ 79 | view.topAnchor.constraint(equalTo: element.topAnchor), 80 | view.leadingAnchor.constraint(equalTo: element.leadingAnchor), 81 | view.bottomAnchor.constraint(equalTo: element.bottomAnchor), 82 | view.trailingAnchor.constraint(equalTo: element.trailingAnchor), 83 | ] 84 | 85 | } 86 | 87 | func makeApplier() -> () -> Void { 88 | 89 | return { [weak view] in 90 | 91 | guard let view = view else { return } 92 | 93 | if let priority = verticalHuggingPriority { 94 | view.setContentHuggingPriority(priority, for: .vertical) 95 | } 96 | if let priority = horizontalHuggingPriority { 97 | view.setContentHuggingPriority(priority, for: .horizontal) 98 | } 99 | if let priority = verticalCompressionResistancePriority { 100 | view.setContentCompressionResistancePriority(priority, for: .vertical) 101 | } 102 | if let priority = horizontalCompressionResistancePriority { 103 | view.setContentCompressionResistancePriority(priority, for: .horizontal) 104 | } 105 | } 106 | } 107 | 108 | func makeConstraints() -> [NSLayoutConstraint] { 109 | dimensionConstraints.makeConstraints(for: view) 110 | } 111 | 112 | } 113 | -------------------------------------------------------------------------------- /MondrianLayout/Manager/LayoutManager.swift: -------------------------------------------------------------------------------- 1 | 2 | import UIKit 3 | 4 | /** 5 | An object that manages layout. 6 | It supports updating the layout. 7 | In the case of defining distinct layouts under some conditions, this helps. 8 | 9 | ```swift 10 | /// Defines a instance 11 | /// It needs to be retained somewhere such as inside view controller or view. 12 | let manager = LayoutManager() 13 | 14 | /// Sets up the layout in the escaping closure 15 | manager.setup(on: view) { 16 | VStackBlock { 17 | ... 18 | } 19 | } 20 | 21 | /// Calls when you need to get a new layout. 22 | /// It calls the closure set in `setup` to build subviews again. 23 | manager.reloadLayout() 24 | ``` 25 | 26 | */ 27 | public final class LayoutManager { 28 | 29 | private var _layoutBuilder: (() -> [EntrypointBuilder.Either])? 30 | private var currentContext: LayoutBuilderContext? 31 | private weak var targetView: UIView? 32 | 33 | public init() { 34 | } 35 | 36 | /** 37 | Setting up the layout of subviews on the view. 38 | the layout closure is called each calling `reloadLayout` and after `setup`. 39 | 40 | Please avoid creating view and layout-guide instances in the closure. Performance decreases by creating a new instance and destroying the previous one. 41 | */ 42 | public func setup( 43 | on view: UIView, 44 | @EntrypointBuilder layout: @escaping () -> [EntrypointBuilder.Either] 45 | ) { 46 | targetView = view 47 | _layoutBuilder = layout 48 | reloadLayout() 49 | } 50 | 51 | /** 52 | Re lays out the subviews with deactivating the current layout. 53 | */ 54 | public func reloadLayout() { 55 | guard let _layoutBuilder = _layoutBuilder else { 56 | return 57 | } 58 | 59 | guard let targetView = targetView else { 60 | return 61 | } 62 | 63 | let previousContext = currentContext 64 | 65 | previousContext?.deactivate() 66 | let newContext = targetView.mondrian.buildSubviews(_layoutBuilder) 67 | currentContext = newContext 68 | 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /MondrianLayout/MondrianNamespace.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | public struct MondrianNamespace { 4 | 5 | public let base: Base 6 | 7 | init(base: Base) { 8 | self.base = base 9 | } 10 | } 11 | 12 | extension MondrianNamespace where Base: UIView { 13 | 14 | /** 15 | Entry point to describe layout constraints 16 | Activates by calling `activate()` or using `mondrianBatchLayout` 17 | 18 | ```swift 19 | view.mondrian.layout 20 | .top(.toSuperview) 21 | .left(.toSuperview) 22 | .right(.to(box2).left) 23 | .bottom(.to(box2).bottom) 24 | .activate() 25 | ``` 26 | */ 27 | public var layout: LayoutDescriptor { 28 | .init(view: base) 29 | } 30 | // 31 | // public var block: ViewBlock { 32 | // .init(base) 33 | // } 34 | 35 | } 36 | 37 | extension MondrianNamespace where Base: UILayoutGuide { 38 | 39 | /** 40 | Entry point to describe layout constraints 41 | Activates by calling `activate()` or using `mondrianBatchLayout` 42 | 43 | ```swift 44 | view.mondrian.layout 45 | .top(.toSuperview) 46 | .left(.toSuperview) 47 | .right(.to(box2).left) 48 | .bottom(.to(box2).bottom) 49 | .activate() 50 | ``` 51 | */ 52 | public var layout: LayoutDescriptor { 53 | .init(layoutGuide: base) 54 | } 55 | // 56 | // public var block: LayoutGuideBlock { 57 | // .init(base) 58 | // } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.3 2 | import PackageDescription 3 | 4 | let package = Package( 5 | name: "MondrianLayout", 6 | platforms: [.iOS(.v12)], 7 | products: [ 8 | .library(name: "MondrianLayout", type: .static, targets: ["MondrianLayout"]), 9 | .library(name: "MondrianLayoutDynamic", type: .dynamic, targets: ["MondrianLayout"]) 10 | ], 11 | dependencies: [], 12 | targets: [ 13 | .target( 14 | name: "MondrianLayout", 15 | dependencies: [], 16 | path: "MondrianLayout" 17 | ) 18 | ] 19 | ) 20 | -------------------------------------------------------------------------------- /RevealServer.xcframework/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | AvailableLibraries 6 | 7 | 8 | DebugSymbolsPath 9 | dSYMs 10 | LibraryIdentifier 11 | ios-arm64_x86_64-maccatalyst 12 | LibraryPath 13 | RevealServer.framework 14 | SupportedArchitectures 15 | 16 | arm64 17 | x86_64 18 | 19 | SupportedPlatform 20 | ios 21 | SupportedPlatformVariant 22 | maccatalyst 23 | 24 | 25 | DebugSymbolsPath 26 | dSYMs 27 | LibraryIdentifier 28 | ios-arm64_armv7 29 | LibraryPath 30 | RevealServer.framework 31 | SupportedArchitectures 32 | 33 | arm64 34 | armv7 35 | 36 | SupportedPlatform 37 | ios 38 | 39 | 40 | DebugSymbolsPath 41 | dSYMs 42 | LibraryIdentifier 43 | ios-arm64_i386_x86_64-simulator 44 | LibraryPath 45 | RevealServer.framework 46 | SupportedArchitectures 47 | 48 | arm64 49 | i386 50 | x86_64 51 | 52 | SupportedPlatform 53 | ios 54 | SupportedPlatformVariant 55 | simulator 56 | 57 | 58 | DebugSymbolsPath 59 | dSYMs 60 | LibraryIdentifier 61 | tvos-arm64_x86_64-simulator 62 | LibraryPath 63 | RevealServer.framework 64 | SupportedArchitectures 65 | 66 | arm64 67 | x86_64 68 | 69 | SupportedPlatform 70 | tvos 71 | SupportedPlatformVariant 72 | simulator 73 | 74 | 75 | DebugSymbolsPath 76 | dSYMs 77 | LibraryIdentifier 78 | tvos-arm64 79 | LibraryPath 80 | RevealServer.framework 81 | SupportedArchitectures 82 | 83 | arm64 84 | 85 | SupportedPlatform 86 | tvos 87 | 88 | 89 | CFBundlePackageType 90 | XFWK 91 | XCFrameworkFormatVersion 92 | 1.0 93 | 94 | 95 | -------------------------------------------------------------------------------- /RevealServer.xcframework/Scripts/integrate_revealserver.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -o errexit 3 | set -o nounset 4 | 5 | # This script takes care of integrating Reveal Server into your iOS application target when you have already manually linked the XCFramework into your app. 6 | 7 | # If the current build configuration as defined by the environment variable CONFIGURATION does not match the name provided by REVEAL_LOAD_FOR_CONFIGURATION (this defaults to "Debug"), then any copies of RevealServer that Xcode has copied into your application will be removed. 8 | 9 | # NOTE: This script is intended to be run from within an Xcode run script build phase, and won't work without the environment variables provided therein. 10 | 11 | # If you want to inspect your app using Reveal in build configurations that are not the default "Debug" configuration, override the REVEAL_LOAD_FOR_CONFIGURATION environment variable with the full name of your desired configuration. 12 | load_trigger=${REVEAL_LOAD_FOR_CONFIGURATION:-Debug} 13 | 14 | if [ "${PLATFORM_NAME}" == *simulator ]; then 15 | echo "Reveal Server not integrated into ${TARGET_NAME}: Targeted platform is simulated, and does not require it." 16 | exit 0 17 | fi 18 | 19 | if [ "${CONFIGURATION}" != "${load_trigger}" ]; then 20 | # If we are not running in the expected configuration, remove the RevealServer framework (which needs to have been weakly linked). 21 | bundled_reveal_server="${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RevealServer.framework" 22 | 23 | if [ -e "${bundled_reveal_server}" ]; then 24 | rm -r "${bundled_reveal_server}" 25 | fi 26 | 27 | echo "Reveal Server has been removed from ${TARGET_NAME}: Current build configuration is not '${load_trigger}'." 28 | exit 0 29 | fi 30 | 31 | # Otherwise, we need to insert the NSBonjourServices array into the app's Info.plist. It's OK if this fails due to the key already existing. 32 | plutil -insert NSBonjourServices -xml '' "${CONFIGURATION_BUILD_DIR}/${INFOPLIST_PATH}" || true 33 | 34 | # Insert Reveal's Bonjour details into the NSBonjourServices array. 35 | plutil -insert NSBonjourServices.0 -string "_reveal._tcp" "${CONFIGURATION_BUILD_DIR}/${INFOPLIST_PATH}" 36 | 37 | echo "An NSBonjourServices entry for Reveal (_reveal._tcp) has been added to ${INFOPLIST_PATH}" 38 | 39 | echo "Reveal Server has been successfully integrated into ${TARGET_NAME}." 40 | -------------------------------------------------------------------------------- /RevealServer.xcframework/_CodeSignature/CodeDirectory: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluidGroup/MondrianLayout/b26edaa5a469bc5fd58f9afe37d257ca1b3771e0/RevealServer.xcframework/_CodeSignature/CodeDirectory -------------------------------------------------------------------------------- /RevealServer.xcframework/_CodeSignature/CodeRequirements: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluidGroup/MondrianLayout/b26edaa5a469bc5fd58f9afe37d257ca1b3771e0/RevealServer.xcframework/_CodeSignature/CodeRequirements -------------------------------------------------------------------------------- /RevealServer.xcframework/_CodeSignature/CodeRequirements-1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluidGroup/MondrianLayout/b26edaa5a469bc5fd58f9afe37d257ca1b3771e0/RevealServer.xcframework/_CodeSignature/CodeRequirements-1 -------------------------------------------------------------------------------- /RevealServer.xcframework/_CodeSignature/CodeSignature: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluidGroup/MondrianLayout/b26edaa5a469bc5fd58f9afe37d257ca1b3771e0/RevealServer.xcframework/_CodeSignature/CodeSignature -------------------------------------------------------------------------------- /RevealServer.xcframework/ios-arm64_armv7/RevealServer.framework/Headers/RevealServer.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright © 2015 Itty Bitty Apps, Pty Ltd. All rights reserved. 3 | 4 | #import 5 | 6 | //! Project version number for RevealServer. 7 | FOUNDATION_EXPORT double RevealServerVersionNumber; 8 | 9 | //! Project version string for RevealServer. 10 | FOUNDATION_EXPORT const unsigned char RevealServerVersionString[]; 11 | 12 | -------------------------------------------------------------------------------- /RevealServer.xcframework/ios-arm64_armv7/RevealServer.framework/Info.plist: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluidGroup/MondrianLayout/b26edaa5a469bc5fd58f9afe37d257ca1b3771e0/RevealServer.xcframework/ios-arm64_armv7/RevealServer.framework/Info.plist -------------------------------------------------------------------------------- /RevealServer.xcframework/ios-arm64_armv7/RevealServer.framework/Modules/module.modulemap: -------------------------------------------------------------------------------- 1 | framework module RevealServer { 2 | umbrella header "RevealServer.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /RevealServer.xcframework/ios-arm64_armv7/RevealServer.framework/RevealServer: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluidGroup/MondrianLayout/b26edaa5a469bc5fd58f9afe37d257ca1b3771e0/RevealServer.xcframework/ios-arm64_armv7/RevealServer.framework/RevealServer -------------------------------------------------------------------------------- /RevealServer.xcframework/ios-arm64_armv7/RevealServer.framework/Scripts/integrate_revealserver.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -o errexit 3 | set -o nounset 4 | 5 | # This script takes care of integrating Reveal Server into your iOS application target when you have already manually linked the XCFramework into your app. 6 | 7 | # If the current build configuration as defined by the environment variable CONFIGURATION does not match the name provided by REVEAL_LOAD_FOR_CONFIGURATION (this defaults to "Debug"), then any copies of RevealServer that Xcode has copied into your application will be removed. 8 | 9 | # NOTE: This script is intended to be run from within an Xcode run script build phase, and won't work without the environment variables provided therein. 10 | 11 | # If you want to inspect your app using Reveal in build configurations that are not the default "Debug" configuration, override the REVEAL_LOAD_FOR_CONFIGURATION environment variable with the full name of your desired configuration. 12 | load_trigger=${REVEAL_LOAD_FOR_CONFIGURATION:-Debug} 13 | 14 | if [ "${PLATFORM_NAME}" == *simulator ]; then 15 | echo "Reveal Server not integrated into ${TARGET_NAME}: Targeted platform is simulated, and does not require it." 16 | exit 0 17 | fi 18 | 19 | if [ "${CONFIGURATION}" != "${load_trigger}" ]; then 20 | # If we are not running in the expected configuration, remove the RevealServer framework (which needs to have been weakly linked). 21 | bundled_reveal_server="${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RevealServer.framework" 22 | 23 | if [ -e "${bundled_reveal_server}" ]; then 24 | rm -r "${bundled_reveal_server}" 25 | fi 26 | 27 | echo "Reveal Server has been removed from ${TARGET_NAME}: Current build configuration is not '${load_trigger}'." 28 | exit 0 29 | fi 30 | 31 | # Otherwise, we need to insert the NSBonjourServices array into the app's Info.plist. It's OK if this fails due to the key already existing. 32 | plutil -insert NSBonjourServices -xml '' "${CONFIGURATION_BUILD_DIR}/${INFOPLIST_PATH}" || true 33 | 34 | # Insert Reveal's Bonjour details into the NSBonjourServices array. 35 | plutil -insert NSBonjourServices.0 -string "_reveal._tcp" "${CONFIGURATION_BUILD_DIR}/${INFOPLIST_PATH}" 36 | 37 | echo "An NSBonjourServices entry for Reveal (_reveal._tcp) has been added to ${INFOPLIST_PATH}" 38 | 39 | echo "Reveal Server has been successfully integrated into ${TARGET_NAME}." 40 | -------------------------------------------------------------------------------- /RevealServer.xcframework/ios-arm64_armv7/RevealServer.framework/_CodeSignature/CodeResources: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | files 6 | 7 | Headers/RevealServer.h 8 | 9 | JavE2LI6bNGfH9W90/bDxwIxVlg= 10 | 11 | Info.plist 12 | 13 | Oo7PlxKe5fz4rMQ1m3/8Z6QxBVo= 14 | 15 | Modules/module.modulemap 16 | 17 | EuDEeG1dcC1sd+hIW2SkUAImUg8= 18 | 19 | Scripts/integrate_revealserver.sh 20 | 21 | pDjnAHC1xiLNLJDDIIrTACRCcFA= 22 | 23 | 24 | files2 25 | 26 | Headers/RevealServer.h 27 | 28 | hash 29 | 30 | JavE2LI6bNGfH9W90/bDxwIxVlg= 31 | 32 | hash2 33 | 34 | weLK/65HyV1EiL9dd4xoAqQhf0/0r/Oy6os2nXCA+Ew= 35 | 36 | 37 | Modules/module.modulemap 38 | 39 | hash 40 | 41 | EuDEeG1dcC1sd+hIW2SkUAImUg8= 42 | 43 | hash2 44 | 45 | tstqiJpIPr4iEd3MDHClLuTB/ciSC/zNlke1AjfSVuU= 46 | 47 | 48 | Scripts/integrate_revealserver.sh 49 | 50 | hash 51 | 52 | pDjnAHC1xiLNLJDDIIrTACRCcFA= 53 | 54 | hash2 55 | 56 | XOjBVOZRN/oNePfbAbXae72e3s7W6HqdKnPKJdHque8= 57 | 58 | 59 | 60 | rules 61 | 62 | ^.* 63 | 64 | ^.*\.lproj/ 65 | 66 | optional 67 | 68 | weight 69 | 1000 70 | 71 | ^.*\.lproj/locversion.plist$ 72 | 73 | omit 74 | 75 | weight 76 | 1100 77 | 78 | ^Base\.lproj/ 79 | 80 | weight 81 | 1010 82 | 83 | ^version.plist$ 84 | 85 | 86 | rules2 87 | 88 | .*\.dSYM($|/) 89 | 90 | weight 91 | 11 92 | 93 | ^(.*/)?\.DS_Store$ 94 | 95 | omit 96 | 97 | weight 98 | 2000 99 | 100 | ^.* 101 | 102 | ^.*\.lproj/ 103 | 104 | optional 105 | 106 | weight 107 | 1000 108 | 109 | ^.*\.lproj/locversion.plist$ 110 | 111 | omit 112 | 113 | weight 114 | 1100 115 | 116 | ^Base\.lproj/ 117 | 118 | weight 119 | 1010 120 | 121 | ^Info\.plist$ 122 | 123 | omit 124 | 125 | weight 126 | 20 127 | 128 | ^PkgInfo$ 129 | 130 | omit 131 | 132 | weight 133 | 20 134 | 135 | ^embedded\.provisionprofile$ 136 | 137 | weight 138 | 20 139 | 140 | ^version\.plist$ 141 | 142 | weight 143 | 20 144 | 145 | 146 | 147 | 148 | -------------------------------------------------------------------------------- /RevealServer.xcframework/ios-arm64_i386_x86_64-simulator/RevealServer.framework/Headers/RevealServer.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright © 2015 Itty Bitty Apps, Pty Ltd. All rights reserved. 3 | 4 | #import 5 | 6 | //! Project version number for RevealServer. 7 | FOUNDATION_EXPORT double RevealServerVersionNumber; 8 | 9 | //! Project version string for RevealServer. 10 | FOUNDATION_EXPORT const unsigned char RevealServerVersionString[]; 11 | 12 | -------------------------------------------------------------------------------- /RevealServer.xcframework/ios-arm64_i386_x86_64-simulator/RevealServer.framework/Info.plist: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluidGroup/MondrianLayout/b26edaa5a469bc5fd58f9afe37d257ca1b3771e0/RevealServer.xcframework/ios-arm64_i386_x86_64-simulator/RevealServer.framework/Info.plist -------------------------------------------------------------------------------- /RevealServer.xcframework/ios-arm64_i386_x86_64-simulator/RevealServer.framework/Modules/module.modulemap: -------------------------------------------------------------------------------- 1 | framework module RevealServer { 2 | umbrella header "RevealServer.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /RevealServer.xcframework/ios-arm64_i386_x86_64-simulator/RevealServer.framework/RevealServer: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluidGroup/MondrianLayout/b26edaa5a469bc5fd58f9afe37d257ca1b3771e0/RevealServer.xcframework/ios-arm64_i386_x86_64-simulator/RevealServer.framework/RevealServer -------------------------------------------------------------------------------- /RevealServer.xcframework/ios-arm64_i386_x86_64-simulator/RevealServer.framework/Scripts/integrate_revealserver.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -o errexit 3 | set -o nounset 4 | 5 | # This script takes care of integrating Reveal Server into your iOS application target when you have already manually linked the XCFramework into your app. 6 | 7 | # If the current build configuration as defined by the environment variable CONFIGURATION does not match the name provided by REVEAL_LOAD_FOR_CONFIGURATION (this defaults to "Debug"), then any copies of RevealServer that Xcode has copied into your application will be removed. 8 | 9 | # NOTE: This script is intended to be run from within an Xcode run script build phase, and won't work without the environment variables provided therein. 10 | 11 | # If you want to inspect your app using Reveal in build configurations that are not the default "Debug" configuration, override the REVEAL_LOAD_FOR_CONFIGURATION environment variable with the full name of your desired configuration. 12 | load_trigger=${REVEAL_LOAD_FOR_CONFIGURATION:-Debug} 13 | 14 | if [ "${PLATFORM_NAME}" == *simulator ]; then 15 | echo "Reveal Server not integrated into ${TARGET_NAME}: Targeted platform is simulated, and does not require it." 16 | exit 0 17 | fi 18 | 19 | if [ "${CONFIGURATION}" != "${load_trigger}" ]; then 20 | # If we are not running in the expected configuration, remove the RevealServer framework (which needs to have been weakly linked). 21 | bundled_reveal_server="${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RevealServer.framework" 22 | 23 | if [ -e "${bundled_reveal_server}" ]; then 24 | rm -r "${bundled_reveal_server}" 25 | fi 26 | 27 | echo "Reveal Server has been removed from ${TARGET_NAME}: Current build configuration is not '${load_trigger}'." 28 | exit 0 29 | fi 30 | 31 | # Otherwise, we need to insert the NSBonjourServices array into the app's Info.plist. It's OK if this fails due to the key already existing. 32 | plutil -insert NSBonjourServices -xml '' "${CONFIGURATION_BUILD_DIR}/${INFOPLIST_PATH}" || true 33 | 34 | # Insert Reveal's Bonjour details into the NSBonjourServices array. 35 | plutil -insert NSBonjourServices.0 -string "_reveal._tcp" "${CONFIGURATION_BUILD_DIR}/${INFOPLIST_PATH}" 36 | 37 | echo "An NSBonjourServices entry for Reveal (_reveal._tcp) has been added to ${INFOPLIST_PATH}" 38 | 39 | echo "Reveal Server has been successfully integrated into ${TARGET_NAME}." 40 | -------------------------------------------------------------------------------- /RevealServer.xcframework/ios-arm64_i386_x86_64-simulator/RevealServer.framework/_CodeSignature/CodeResources: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | files 6 | 7 | Headers/RevealServer.h 8 | 9 | JavE2LI6bNGfH9W90/bDxwIxVlg= 10 | 11 | Info.plist 12 | 13 | hpLxIgCYsaLsKpf7FYEcsZa3Fmw= 14 | 15 | Modules/module.modulemap 16 | 17 | EuDEeG1dcC1sd+hIW2SkUAImUg8= 18 | 19 | Scripts/integrate_revealserver.sh 20 | 21 | pDjnAHC1xiLNLJDDIIrTACRCcFA= 22 | 23 | 24 | files2 25 | 26 | Headers/RevealServer.h 27 | 28 | hash 29 | 30 | JavE2LI6bNGfH9W90/bDxwIxVlg= 31 | 32 | hash2 33 | 34 | weLK/65HyV1EiL9dd4xoAqQhf0/0r/Oy6os2nXCA+Ew= 35 | 36 | 37 | Modules/module.modulemap 38 | 39 | hash 40 | 41 | EuDEeG1dcC1sd+hIW2SkUAImUg8= 42 | 43 | hash2 44 | 45 | tstqiJpIPr4iEd3MDHClLuTB/ciSC/zNlke1AjfSVuU= 46 | 47 | 48 | Scripts/integrate_revealserver.sh 49 | 50 | hash 51 | 52 | pDjnAHC1xiLNLJDDIIrTACRCcFA= 53 | 54 | hash2 55 | 56 | XOjBVOZRN/oNePfbAbXae72e3s7W6HqdKnPKJdHque8= 57 | 58 | 59 | 60 | rules 61 | 62 | ^.* 63 | 64 | ^.*\.lproj/ 65 | 66 | optional 67 | 68 | weight 69 | 1000 70 | 71 | ^.*\.lproj/locversion.plist$ 72 | 73 | omit 74 | 75 | weight 76 | 1100 77 | 78 | ^Base\.lproj/ 79 | 80 | weight 81 | 1010 82 | 83 | ^version.plist$ 84 | 85 | 86 | rules2 87 | 88 | .*\.dSYM($|/) 89 | 90 | weight 91 | 11 92 | 93 | ^(.*/)?\.DS_Store$ 94 | 95 | omit 96 | 97 | weight 98 | 2000 99 | 100 | ^.* 101 | 102 | ^.*\.lproj/ 103 | 104 | optional 105 | 106 | weight 107 | 1000 108 | 109 | ^.*\.lproj/locversion.plist$ 110 | 111 | omit 112 | 113 | weight 114 | 1100 115 | 116 | ^Base\.lproj/ 117 | 118 | weight 119 | 1010 120 | 121 | ^Info\.plist$ 122 | 123 | omit 124 | 125 | weight 126 | 20 127 | 128 | ^PkgInfo$ 129 | 130 | omit 131 | 132 | weight 133 | 20 134 | 135 | ^embedded\.provisionprofile$ 136 | 137 | weight 138 | 20 139 | 140 | ^version\.plist$ 141 | 142 | weight 143 | 20 144 | 145 | 146 | 147 | 148 | -------------------------------------------------------------------------------- /RevealServer.xcframework/ios-arm64_x86_64-maccatalyst/RevealServer.framework/Headers: -------------------------------------------------------------------------------- 1 | Versions/Current/Headers -------------------------------------------------------------------------------- /RevealServer.xcframework/ios-arm64_x86_64-maccatalyst/RevealServer.framework/Modules: -------------------------------------------------------------------------------- 1 | Versions/Current/Modules -------------------------------------------------------------------------------- /RevealServer.xcframework/ios-arm64_x86_64-maccatalyst/RevealServer.framework/Resources: -------------------------------------------------------------------------------- 1 | Versions/Current/Resources -------------------------------------------------------------------------------- /RevealServer.xcframework/ios-arm64_x86_64-maccatalyst/RevealServer.framework/RevealServer: -------------------------------------------------------------------------------- 1 | Versions/Current/RevealServer -------------------------------------------------------------------------------- /RevealServer.xcframework/ios-arm64_x86_64-maccatalyst/RevealServer.framework/Versions/A/Headers/RevealServer.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright © 2015 Itty Bitty Apps, Pty Ltd. All rights reserved. 3 | 4 | #import 5 | 6 | //! Project version number for RevealServer. 7 | FOUNDATION_EXPORT double RevealServerVersionNumber; 8 | 9 | //! Project version string for RevealServer. 10 | FOUNDATION_EXPORT const unsigned char RevealServerVersionString[]; 11 | 12 | -------------------------------------------------------------------------------- /RevealServer.xcframework/ios-arm64_x86_64-maccatalyst/RevealServer.framework/Versions/A/Modules/module.modulemap: -------------------------------------------------------------------------------- 1 | framework module RevealServer { 2 | umbrella header "RevealServer.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /RevealServer.xcframework/ios-arm64_x86_64-maccatalyst/RevealServer.framework/Versions/A/Resources/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | BuildMachineOSBuild 6 | 21A559 7 | CFBundleDevelopmentRegion 8 | en 9 | CFBundleExecutable 10 | RevealServer 11 | CFBundleIdentifier 12 | com.ittybittyapps.RevealServer 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | RevealServer 17 | CFBundlePackageType 18 | FMWK 19 | CFBundleShortVersionString 20 | 2.0 21 | CFBundleSupportedPlatforms 22 | 23 | MacOSX 24 | 25 | CFBundleVersion 26 | 2.0.0 27 | DTCompiler 28 | com.apple.compilers.llvm.clang.1_0 29 | DTPlatformBuild 30 | 13C90 31 | DTPlatformName 32 | macosx 33 | DTPlatformVersion 34 | 12.1 35 | DTSDKBuild 36 | 21C46 37 | DTSDKName 38 | macosx12.1 39 | DTXcode 40 | 1320 41 | DTXcodeBuild 42 | 13C90 43 | LSMinimumSystemVersion 44 | 10.15 45 | UIDeviceFamily 46 | 47 | 2 48 | 6 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /RevealServer.xcframework/ios-arm64_x86_64-maccatalyst/RevealServer.framework/Versions/A/Resources/Scripts/integrate_revealserver.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -o errexit 3 | set -o nounset 4 | 5 | # This script takes care of integrating Reveal Server into your iOS application target when you have already manually linked the XCFramework into your app. 6 | 7 | # If the current build configuration as defined by the environment variable CONFIGURATION does not match the name provided by REVEAL_LOAD_FOR_CONFIGURATION (this defaults to "Debug"), then any copies of RevealServer that Xcode has copied into your application will be removed. 8 | 9 | # NOTE: This script is intended to be run from within an Xcode run script build phase, and won't work without the environment variables provided therein. 10 | 11 | # If you want to inspect your app using Reveal in build configurations that are not the default "Debug" configuration, override the REVEAL_LOAD_FOR_CONFIGURATION environment variable with the full name of your desired configuration. 12 | load_trigger=${REVEAL_LOAD_FOR_CONFIGURATION:-Debug} 13 | 14 | if [ "${PLATFORM_NAME}" == *simulator ]; then 15 | echo "Reveal Server not integrated into ${TARGET_NAME}: Targeted platform is simulated, and does not require it." 16 | exit 0 17 | fi 18 | 19 | if [ "${CONFIGURATION}" != "${load_trigger}" ]; then 20 | # If we are not running in the expected configuration, remove the RevealServer framework (which needs to have been weakly linked). 21 | bundled_reveal_server="${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RevealServer.framework" 22 | 23 | if [ -e "${bundled_reveal_server}" ]; then 24 | rm -r "${bundled_reveal_server}" 25 | fi 26 | 27 | echo "Reveal Server has been removed from ${TARGET_NAME}: Current build configuration is not '${load_trigger}'." 28 | exit 0 29 | fi 30 | 31 | # Otherwise, we need to insert the NSBonjourServices array into the app's Info.plist. It's OK if this fails due to the key already existing. 32 | plutil -insert NSBonjourServices -xml '' "${CONFIGURATION_BUILD_DIR}/${INFOPLIST_PATH}" || true 33 | 34 | # Insert Reveal's Bonjour details into the NSBonjourServices array. 35 | plutil -insert NSBonjourServices.0 -string "_reveal._tcp" "${CONFIGURATION_BUILD_DIR}/${INFOPLIST_PATH}" 36 | 37 | echo "An NSBonjourServices entry for Reveal (_reveal._tcp) has been added to ${INFOPLIST_PATH}" 38 | 39 | echo "Reveal Server has been successfully integrated into ${TARGET_NAME}." 40 | -------------------------------------------------------------------------------- /RevealServer.xcframework/ios-arm64_x86_64-maccatalyst/RevealServer.framework/Versions/A/RevealServer: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluidGroup/MondrianLayout/b26edaa5a469bc5fd58f9afe37d257ca1b3771e0/RevealServer.xcframework/ios-arm64_x86_64-maccatalyst/RevealServer.framework/Versions/A/RevealServer -------------------------------------------------------------------------------- /RevealServer.xcframework/ios-arm64_x86_64-maccatalyst/RevealServer.framework/Versions/A/_CodeSignature/CodeResources: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | files 6 | 7 | Resources/Info.plist 8 | 9 | et//D8sEtGeZE028IqHQT2le8nA= 10 | 11 | Resources/Scripts/integrate_revealserver.sh 12 | 13 | pDjnAHC1xiLNLJDDIIrTACRCcFA= 14 | 15 | 16 | files2 17 | 18 | Headers/RevealServer.h 19 | 20 | hash2 21 | 22 | weLK/65HyV1EiL9dd4xoAqQhf0/0r/Oy6os2nXCA+Ew= 23 | 24 | 25 | Modules/module.modulemap 26 | 27 | hash2 28 | 29 | tstqiJpIPr4iEd3MDHClLuTB/ciSC/zNlke1AjfSVuU= 30 | 31 | 32 | Resources/Info.plist 33 | 34 | hash2 35 | 36 | 7hhQs89td/Za58K/uMm6HB7+XekoilqLk0CBY1wHHyM= 37 | 38 | 39 | Resources/Scripts/integrate_revealserver.sh 40 | 41 | hash2 42 | 43 | XOjBVOZRN/oNePfbAbXae72e3s7W6HqdKnPKJdHque8= 44 | 45 | 46 | 47 | rules 48 | 49 | ^Resources/ 50 | 51 | ^Resources/.*\.lproj/ 52 | 53 | optional 54 | 55 | weight 56 | 1000 57 | 58 | ^Resources/.*\.lproj/locversion.plist$ 59 | 60 | omit 61 | 62 | weight 63 | 1100 64 | 65 | ^Resources/Base\.lproj/ 66 | 67 | weight 68 | 1010 69 | 70 | ^version.plist$ 71 | 72 | 73 | rules2 74 | 75 | .*\.dSYM($|/) 76 | 77 | weight 78 | 11 79 | 80 | ^(.*/)?\.DS_Store$ 81 | 82 | omit 83 | 84 | weight 85 | 2000 86 | 87 | ^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/ 88 | 89 | nested 90 | 91 | weight 92 | 10 93 | 94 | ^.* 95 | 96 | ^Info\.plist$ 97 | 98 | omit 99 | 100 | weight 101 | 20 102 | 103 | ^PkgInfo$ 104 | 105 | omit 106 | 107 | weight 108 | 20 109 | 110 | ^Resources/ 111 | 112 | weight 113 | 20 114 | 115 | ^Resources/.*\.lproj/ 116 | 117 | optional 118 | 119 | weight 120 | 1000 121 | 122 | ^Resources/.*\.lproj/locversion.plist$ 123 | 124 | omit 125 | 126 | weight 127 | 1100 128 | 129 | ^Resources/Base\.lproj/ 130 | 131 | weight 132 | 1010 133 | 134 | ^[^/]+$ 135 | 136 | nested 137 | 138 | weight 139 | 10 140 | 141 | ^embedded\.provisionprofile$ 142 | 143 | weight 144 | 20 145 | 146 | ^version\.plist$ 147 | 148 | weight 149 | 20 150 | 151 | 152 | 153 | 154 | -------------------------------------------------------------------------------- /RevealServer.xcframework/ios-arm64_x86_64-maccatalyst/RevealServer.framework/Versions/Current: -------------------------------------------------------------------------------- 1 | A -------------------------------------------------------------------------------- /RevealServer.xcframework/ios-x86_64-maccatalyst/RevealServer.framework/Headers: -------------------------------------------------------------------------------- 1 | Versions/Current/Headers -------------------------------------------------------------------------------- /RevealServer.xcframework/ios-x86_64-maccatalyst/RevealServer.framework/Modules: -------------------------------------------------------------------------------- 1 | Versions/Current/Modules -------------------------------------------------------------------------------- /RevealServer.xcframework/ios-x86_64-maccatalyst/RevealServer.framework/Resources: -------------------------------------------------------------------------------- 1 | Versions/Current/Resources -------------------------------------------------------------------------------- /RevealServer.xcframework/ios-x86_64-maccatalyst/RevealServer.framework/RevealServer: -------------------------------------------------------------------------------- 1 | Versions/Current/RevealServer -------------------------------------------------------------------------------- /RevealServer.xcframework/ios-x86_64-maccatalyst/RevealServer.framework/Versions/A/Headers/RevealServer.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright © 2015 Itty Bitty Apps, Pty Ltd. All rights reserved. 3 | 4 | #import 5 | 6 | //! Project version number for RevealServer. 7 | FOUNDATION_EXPORT double RevealServerVersionNumber; 8 | 9 | //! Project version string for RevealServer. 10 | FOUNDATION_EXPORT const unsigned char RevealServerVersionString[]; 11 | 12 | -------------------------------------------------------------------------------- /RevealServer.xcframework/ios-x86_64-maccatalyst/RevealServer.framework/Versions/A/Modules/module.modulemap: -------------------------------------------------------------------------------- 1 | framework module RevealServer { 2 | umbrella header "RevealServer.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /RevealServer.xcframework/ios-x86_64-maccatalyst/RevealServer.framework/Versions/A/Resources/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | BuildMachineOSBuild 6 | 19H2 7 | CFBundleDevelopmentRegion 8 | en 9 | CFBundleExecutable 10 | RevealServer 11 | CFBundleIdentifier 12 | com.ittybittyapps.RevealServer 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | RevealServer 17 | CFBundlePackageType 18 | FMWK 19 | CFBundleShortVersionString 20 | 2.0 21 | CFBundleSupportedPlatforms 22 | 23 | MacOSX 24 | 25 | CFBundleVersion 26 | 2.0.0 27 | DTCompiler 28 | com.apple.compilers.llvm.clang.1_0 29 | DTPlatformBuild 30 | 12A7300 31 | DTPlatformName 32 | macosx 33 | DTPlatformVersion 34 | 10.15.6 35 | DTSDKBuild 36 | 19G68 37 | DTSDKName 38 | macosx10.15 39 | DTXcode 40 | 1201 41 | DTXcodeBuild 42 | 12A7300 43 | LSMinimumSystemVersion 44 | 10.15 45 | UIDeviceFamily 46 | 47 | 2 48 | 6 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /RevealServer.xcframework/ios-x86_64-maccatalyst/RevealServer.framework/Versions/A/Resources/Scripts/integrate_revealserver.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -o errexit 3 | set -o nounset 4 | 5 | # This script takes care of integrating Reveal Server into your iOS application target when you have already manually linked the XCFramework into your app. 6 | 7 | # If the current build configuration as defined by the environment variable CONFIGURATION does not match the name provided by REVEAL_LOAD_FOR_CONFIGURATION (this defaults to "Debug"), then any copies of RevealServer that Xcode has copied into your application will be removed. 8 | 9 | # NOTE: This script is intended to be run from within an Xcode run script build phase, and won't work without the environment variables provided therein. 10 | 11 | # If you want to inspect your app using Reveal in build configurations that are not the default "Debug" configuration, override the REVEAL_LOAD_FOR_CONFIGURATION environment variable with the full name of your desired configuration. 12 | load_trigger=${REVEAL_LOAD_FOR_CONFIGURATION:=Debug} 13 | 14 | if [ "${PLATFORM_NAME}" == *simulator ]; then 15 | echo "Reveal Server not integrated into ${TARGET_NAME}: Targeted platform is simulated, and does not require it." 16 | exit 0 17 | fi 18 | 19 | if [ "${CONFIGURATION}" != "${load_trigger}" ]; then 20 | # If we are not running in the expected configuration, remove the RevealServer framework (which needs to have been weakly linked). 21 | bundled_reveal_server="${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RevealServer.framework" 22 | 23 | if [ -e "${bundled_reveal_server}" ]; then 24 | rm -r "${bundled_reveal_server}" 25 | fi 26 | 27 | echo "Reveal Server has been removed from ${TARGET_NAME}: Current build configuration is not '${load_trigger}'." 28 | exit 0 29 | fi 30 | 31 | # Otherwise, we need to insert the NSBonjourServices array into the app's Info.plist. It's OK if this fails due to the key already existing. 32 | plutil -insert NSBonjourServices -xml '' "${CONFIGURATION_BUILD_DIR}/${INFOPLIST_PATH}" || true 33 | 34 | # Insert Reveal's Bonjour details into the NSBonjourServices array. 35 | plutil -insert NSBonjourServices.0 -string "_reveal._tcp" "${CONFIGURATION_BUILD_DIR}/${INFOPLIST_PATH}" 36 | 37 | echo "An NSBonjourServices entry for Reveal (_reveal._tcp) has been added to ${INFOPLIST_PATH}" 38 | 39 | echo "Reveal Server has been successfully integrated into ${TARGET_NAME}." 40 | -------------------------------------------------------------------------------- /RevealServer.xcframework/ios-x86_64-maccatalyst/RevealServer.framework/Versions/A/RevealServer: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluidGroup/MondrianLayout/b26edaa5a469bc5fd58f9afe37d257ca1b3771e0/RevealServer.xcframework/ios-x86_64-maccatalyst/RevealServer.framework/Versions/A/RevealServer -------------------------------------------------------------------------------- /RevealServer.xcframework/ios-x86_64-maccatalyst/RevealServer.framework/Versions/A/_CodeSignature/CodeResources: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | files 6 | 7 | Resources/Info.plist 8 | 9 | bqlFKmmTGdMgqxUofdTTciy8v18= 10 | 11 | Resources/Scripts/integrate_revealserver.sh 12 | 13 | a3jybJGZUpf0alfGOc1ePZz/ZKo= 14 | 15 | 16 | files2 17 | 18 | Headers/RevealServer.h 19 | 20 | hash2 21 | 22 | weLK/65HyV1EiL9dd4xoAqQhf0/0r/Oy6os2nXCA+Ew= 23 | 24 | 25 | Modules/module.modulemap 26 | 27 | hash2 28 | 29 | tstqiJpIPr4iEd3MDHClLuTB/ciSC/zNlke1AjfSVuU= 30 | 31 | 32 | Resources/Info.plist 33 | 34 | hash2 35 | 36 | PA4Mey+/2kWidWGMf4PjFncRyKBdU1qFZsfUlTJniho= 37 | 38 | 39 | Resources/Scripts/integrate_revealserver.sh 40 | 41 | hash2 42 | 43 | 7XROfzVgRqq/iFxwUMYYXR9BlPfvd905/+vDF/eKsZs= 44 | 45 | 46 | 47 | rules 48 | 49 | ^Resources/ 50 | 51 | ^Resources/.*\.lproj/ 52 | 53 | optional 54 | 55 | weight 56 | 1000 57 | 58 | ^Resources/.*\.lproj/locversion.plist$ 59 | 60 | omit 61 | 62 | weight 63 | 1100 64 | 65 | ^Resources/Base\.lproj/ 66 | 67 | weight 68 | 1010 69 | 70 | ^version.plist$ 71 | 72 | 73 | rules2 74 | 75 | .*\.dSYM($|/) 76 | 77 | weight 78 | 11 79 | 80 | ^(.*/)?\.DS_Store$ 81 | 82 | omit 83 | 84 | weight 85 | 2000 86 | 87 | ^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/ 88 | 89 | nested 90 | 91 | weight 92 | 10 93 | 94 | ^.* 95 | 96 | ^Info\.plist$ 97 | 98 | omit 99 | 100 | weight 101 | 20 102 | 103 | ^PkgInfo$ 104 | 105 | omit 106 | 107 | weight 108 | 20 109 | 110 | ^Resources/ 111 | 112 | weight 113 | 20 114 | 115 | ^Resources/.*\.lproj/ 116 | 117 | optional 118 | 119 | weight 120 | 1000 121 | 122 | ^Resources/.*\.lproj/locversion.plist$ 123 | 124 | omit 125 | 126 | weight 127 | 1100 128 | 129 | ^Resources/Base\.lproj/ 130 | 131 | weight 132 | 1010 133 | 134 | ^[^/]+$ 135 | 136 | nested 137 | 138 | weight 139 | 10 140 | 141 | ^embedded\.provisionprofile$ 142 | 143 | weight 144 | 20 145 | 146 | ^version\.plist$ 147 | 148 | weight 149 | 20 150 | 151 | 152 | 153 | 154 | -------------------------------------------------------------------------------- /RevealServer.xcframework/ios-x86_64-maccatalyst/RevealServer.framework/Versions/Current: -------------------------------------------------------------------------------- 1 | A -------------------------------------------------------------------------------- /RevealServer.xcframework/tvos-arm64/RevealServer.framework/Headers/RevealServer.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright © 2015 Itty Bitty Apps, Pty Ltd. All rights reserved. 3 | 4 | #import 5 | 6 | //! Project version number for RevealServer. 7 | FOUNDATION_EXPORT double RevealServerVersionNumber; 8 | 9 | //! Project version string for RevealServer. 10 | FOUNDATION_EXPORT const unsigned char RevealServerVersionString[]; 11 | 12 | -------------------------------------------------------------------------------- /RevealServer.xcframework/tvos-arm64/RevealServer.framework/Info.plist: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluidGroup/MondrianLayout/b26edaa5a469bc5fd58f9afe37d257ca1b3771e0/RevealServer.xcframework/tvos-arm64/RevealServer.framework/Info.plist -------------------------------------------------------------------------------- /RevealServer.xcframework/tvos-arm64/RevealServer.framework/Modules/module.modulemap: -------------------------------------------------------------------------------- 1 | framework module RevealServer { 2 | umbrella header "RevealServer.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /RevealServer.xcframework/tvos-arm64/RevealServer.framework/RevealServer: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluidGroup/MondrianLayout/b26edaa5a469bc5fd58f9afe37d257ca1b3771e0/RevealServer.xcframework/tvos-arm64/RevealServer.framework/RevealServer -------------------------------------------------------------------------------- /RevealServer.xcframework/tvos-arm64/RevealServer.framework/Scripts/integrate_revealserver.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -o errexit 3 | set -o nounset 4 | 5 | # This script takes care of integrating Reveal Server into your iOS application target when you have already manually linked the XCFramework into your app. 6 | 7 | # If the current build configuration as defined by the environment variable CONFIGURATION does not match the name provided by REVEAL_LOAD_FOR_CONFIGURATION (this defaults to "Debug"), then any copies of RevealServer that Xcode has copied into your application will be removed. 8 | 9 | # NOTE: This script is intended to be run from within an Xcode run script build phase, and won't work without the environment variables provided therein. 10 | 11 | # If you want to inspect your app using Reveal in build configurations that are not the default "Debug" configuration, override the REVEAL_LOAD_FOR_CONFIGURATION environment variable with the full name of your desired configuration. 12 | load_trigger=${REVEAL_LOAD_FOR_CONFIGURATION:-Debug} 13 | 14 | if [ "${PLATFORM_NAME}" == *simulator ]; then 15 | echo "Reveal Server not integrated into ${TARGET_NAME}: Targeted platform is simulated, and does not require it." 16 | exit 0 17 | fi 18 | 19 | if [ "${CONFIGURATION}" != "${load_trigger}" ]; then 20 | # If we are not running in the expected configuration, remove the RevealServer framework (which needs to have been weakly linked). 21 | bundled_reveal_server="${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RevealServer.framework" 22 | 23 | if [ -e "${bundled_reveal_server}" ]; then 24 | rm -r "${bundled_reveal_server}" 25 | fi 26 | 27 | echo "Reveal Server has been removed from ${TARGET_NAME}: Current build configuration is not '${load_trigger}'." 28 | exit 0 29 | fi 30 | 31 | # Otherwise, we need to insert the NSBonjourServices array into the app's Info.plist. It's OK if this fails due to the key already existing. 32 | plutil -insert NSBonjourServices -xml '' "${CONFIGURATION_BUILD_DIR}/${INFOPLIST_PATH}" || true 33 | 34 | # Insert Reveal's Bonjour details into the NSBonjourServices array. 35 | plutil -insert NSBonjourServices.0 -string "_reveal._tcp" "${CONFIGURATION_BUILD_DIR}/${INFOPLIST_PATH}" 36 | 37 | echo "An NSBonjourServices entry for Reveal (_reveal._tcp) has been added to ${INFOPLIST_PATH}" 38 | 39 | echo "Reveal Server has been successfully integrated into ${TARGET_NAME}." 40 | -------------------------------------------------------------------------------- /RevealServer.xcframework/tvos-arm64/RevealServer.framework/_CodeSignature/CodeResources: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | files 6 | 7 | Headers/RevealServer.h 8 | 9 | JavE2LI6bNGfH9W90/bDxwIxVlg= 10 | 11 | Info.plist 12 | 13 | uGUpa+zfZp7ETHQS3+BBbJBXfgc= 14 | 15 | Modules/module.modulemap 16 | 17 | EuDEeG1dcC1sd+hIW2SkUAImUg8= 18 | 19 | Scripts/integrate_revealserver.sh 20 | 21 | pDjnAHC1xiLNLJDDIIrTACRCcFA= 22 | 23 | 24 | files2 25 | 26 | Headers/RevealServer.h 27 | 28 | hash 29 | 30 | JavE2LI6bNGfH9W90/bDxwIxVlg= 31 | 32 | hash2 33 | 34 | weLK/65HyV1EiL9dd4xoAqQhf0/0r/Oy6os2nXCA+Ew= 35 | 36 | 37 | Modules/module.modulemap 38 | 39 | hash 40 | 41 | EuDEeG1dcC1sd+hIW2SkUAImUg8= 42 | 43 | hash2 44 | 45 | tstqiJpIPr4iEd3MDHClLuTB/ciSC/zNlke1AjfSVuU= 46 | 47 | 48 | Scripts/integrate_revealserver.sh 49 | 50 | hash 51 | 52 | pDjnAHC1xiLNLJDDIIrTACRCcFA= 53 | 54 | hash2 55 | 56 | XOjBVOZRN/oNePfbAbXae72e3s7W6HqdKnPKJdHque8= 57 | 58 | 59 | 60 | rules 61 | 62 | ^.* 63 | 64 | ^.*\.lproj/ 65 | 66 | optional 67 | 68 | weight 69 | 1000 70 | 71 | ^.*\.lproj/locversion.plist$ 72 | 73 | omit 74 | 75 | weight 76 | 1100 77 | 78 | ^Base\.lproj/ 79 | 80 | weight 81 | 1010 82 | 83 | ^version.plist$ 84 | 85 | 86 | rules2 87 | 88 | .*\.dSYM($|/) 89 | 90 | weight 91 | 11 92 | 93 | ^(.*/)?\.DS_Store$ 94 | 95 | omit 96 | 97 | weight 98 | 2000 99 | 100 | ^.* 101 | 102 | ^.*\.lproj/ 103 | 104 | optional 105 | 106 | weight 107 | 1000 108 | 109 | ^.*\.lproj/locversion.plist$ 110 | 111 | omit 112 | 113 | weight 114 | 1100 115 | 116 | ^Base\.lproj/ 117 | 118 | weight 119 | 1010 120 | 121 | ^Info\.plist$ 122 | 123 | omit 124 | 125 | weight 126 | 20 127 | 128 | ^PkgInfo$ 129 | 130 | omit 131 | 132 | weight 133 | 20 134 | 135 | ^embedded\.provisionprofile$ 136 | 137 | weight 138 | 20 139 | 140 | ^version\.plist$ 141 | 142 | weight 143 | 20 144 | 145 | 146 | 147 | 148 | -------------------------------------------------------------------------------- /RevealServer.xcframework/tvos-arm64_x86_64-simulator/RevealServer.framework/Headers/RevealServer.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright © 2015 Itty Bitty Apps, Pty Ltd. All rights reserved. 3 | 4 | #import 5 | 6 | //! Project version number for RevealServer. 7 | FOUNDATION_EXPORT double RevealServerVersionNumber; 8 | 9 | //! Project version string for RevealServer. 10 | FOUNDATION_EXPORT const unsigned char RevealServerVersionString[]; 11 | 12 | -------------------------------------------------------------------------------- /RevealServer.xcframework/tvos-arm64_x86_64-simulator/RevealServer.framework/Info.plist: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluidGroup/MondrianLayout/b26edaa5a469bc5fd58f9afe37d257ca1b3771e0/RevealServer.xcframework/tvos-arm64_x86_64-simulator/RevealServer.framework/Info.plist -------------------------------------------------------------------------------- /RevealServer.xcframework/tvos-arm64_x86_64-simulator/RevealServer.framework/Modules/module.modulemap: -------------------------------------------------------------------------------- 1 | framework module RevealServer { 2 | umbrella header "RevealServer.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /RevealServer.xcframework/tvos-arm64_x86_64-simulator/RevealServer.framework/RevealServer: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluidGroup/MondrianLayout/b26edaa5a469bc5fd58f9afe37d257ca1b3771e0/RevealServer.xcframework/tvos-arm64_x86_64-simulator/RevealServer.framework/RevealServer -------------------------------------------------------------------------------- /RevealServer.xcframework/tvos-arm64_x86_64-simulator/RevealServer.framework/Scripts/integrate_revealserver.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -o errexit 3 | set -o nounset 4 | 5 | # This script takes care of integrating Reveal Server into your iOS application target when you have already manually linked the XCFramework into your app. 6 | 7 | # If the current build configuration as defined by the environment variable CONFIGURATION does not match the name provided by REVEAL_LOAD_FOR_CONFIGURATION (this defaults to "Debug"), then any copies of RevealServer that Xcode has copied into your application will be removed. 8 | 9 | # NOTE: This script is intended to be run from within an Xcode run script build phase, and won't work without the environment variables provided therein. 10 | 11 | # If you want to inspect your app using Reveal in build configurations that are not the default "Debug" configuration, override the REVEAL_LOAD_FOR_CONFIGURATION environment variable with the full name of your desired configuration. 12 | load_trigger=${REVEAL_LOAD_FOR_CONFIGURATION:-Debug} 13 | 14 | if [ "${PLATFORM_NAME}" == *simulator ]; then 15 | echo "Reveal Server not integrated into ${TARGET_NAME}: Targeted platform is simulated, and does not require it." 16 | exit 0 17 | fi 18 | 19 | if [ "${CONFIGURATION}" != "${load_trigger}" ]; then 20 | # If we are not running in the expected configuration, remove the RevealServer framework (which needs to have been weakly linked). 21 | bundled_reveal_server="${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RevealServer.framework" 22 | 23 | if [ -e "${bundled_reveal_server}" ]; then 24 | rm -r "${bundled_reveal_server}" 25 | fi 26 | 27 | echo "Reveal Server has been removed from ${TARGET_NAME}: Current build configuration is not '${load_trigger}'." 28 | exit 0 29 | fi 30 | 31 | # Otherwise, we need to insert the NSBonjourServices array into the app's Info.plist. It's OK if this fails due to the key already existing. 32 | plutil -insert NSBonjourServices -xml '' "${CONFIGURATION_BUILD_DIR}/${INFOPLIST_PATH}" || true 33 | 34 | # Insert Reveal's Bonjour details into the NSBonjourServices array. 35 | plutil -insert NSBonjourServices.0 -string "_reveal._tcp" "${CONFIGURATION_BUILD_DIR}/${INFOPLIST_PATH}" 36 | 37 | echo "An NSBonjourServices entry for Reveal (_reveal._tcp) has been added to ${INFOPLIST_PATH}" 38 | 39 | echo "Reveal Server has been successfully integrated into ${TARGET_NAME}." 40 | -------------------------------------------------------------------------------- /RevealServer.xcframework/tvos-arm64_x86_64-simulator/RevealServer.framework/_CodeSignature/CodeResources: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | files 6 | 7 | Headers/RevealServer.h 8 | 9 | JavE2LI6bNGfH9W90/bDxwIxVlg= 10 | 11 | Info.plist 12 | 13 | Ozik2ierbamqUMW8Y5/SytPiqOg= 14 | 15 | Modules/module.modulemap 16 | 17 | EuDEeG1dcC1sd+hIW2SkUAImUg8= 18 | 19 | Scripts/integrate_revealserver.sh 20 | 21 | pDjnAHC1xiLNLJDDIIrTACRCcFA= 22 | 23 | 24 | files2 25 | 26 | Headers/RevealServer.h 27 | 28 | hash 29 | 30 | JavE2LI6bNGfH9W90/bDxwIxVlg= 31 | 32 | hash2 33 | 34 | weLK/65HyV1EiL9dd4xoAqQhf0/0r/Oy6os2nXCA+Ew= 35 | 36 | 37 | Modules/module.modulemap 38 | 39 | hash 40 | 41 | EuDEeG1dcC1sd+hIW2SkUAImUg8= 42 | 43 | hash2 44 | 45 | tstqiJpIPr4iEd3MDHClLuTB/ciSC/zNlke1AjfSVuU= 46 | 47 | 48 | Scripts/integrate_revealserver.sh 49 | 50 | hash 51 | 52 | pDjnAHC1xiLNLJDDIIrTACRCcFA= 53 | 54 | hash2 55 | 56 | XOjBVOZRN/oNePfbAbXae72e3s7W6HqdKnPKJdHque8= 57 | 58 | 59 | 60 | rules 61 | 62 | ^.* 63 | 64 | ^.*\.lproj/ 65 | 66 | optional 67 | 68 | weight 69 | 1000 70 | 71 | ^.*\.lproj/locversion.plist$ 72 | 73 | omit 74 | 75 | weight 76 | 1100 77 | 78 | ^Base\.lproj/ 79 | 80 | weight 81 | 1010 82 | 83 | ^version.plist$ 84 | 85 | 86 | rules2 87 | 88 | .*\.dSYM($|/) 89 | 90 | weight 91 | 11 92 | 93 | ^(.*/)?\.DS_Store$ 94 | 95 | omit 96 | 97 | weight 98 | 2000 99 | 100 | ^.* 101 | 102 | ^.*\.lproj/ 103 | 104 | optional 105 | 106 | weight 107 | 1000 108 | 109 | ^.*\.lproj/locversion.plist$ 110 | 111 | omit 112 | 113 | weight 114 | 1100 115 | 116 | ^Base\.lproj/ 117 | 118 | weight 119 | 1010 120 | 121 | ^Info\.plist$ 122 | 123 | omit 124 | 125 | weight 126 | 20 127 | 128 | ^PkgInfo$ 129 | 130 | omit 131 | 132 | weight 133 | 20 134 | 135 | ^embedded\.provisionprofile$ 136 | 137 | weight 138 | 20 139 | 140 | ^version\.plist$ 141 | 142 | weight 143 | 20 144 | 145 | 146 | 147 | 148 | -------------------------------------------------------------------------------- /Tests/ClassicTests.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import MondrianLayout 3 | import SnapshotTesting 4 | import XCTest 5 | 6 | final class ClassicTests: XCTestCase { 7 | 8 | func test_resultBuilder() { 9 | 10 | func syntax() { 11 | 12 | do { 13 | let views: [UIView] = [] 14 | 15 | mondrianBatchLayout { 16 | views.map { 17 | $0.mondrian.layout.height(.min(10)) 18 | } 19 | } 20 | } 21 | 22 | do { 23 | let views: [UIView] = [] 24 | 25 | mondrianBatchLayout { 26 | 27 | views.map { 28 | $0.mondrian.layout.height(.min(10)) 29 | } 30 | 31 | views.map { 32 | $0.mondrian.layout.height(.min(10)) 33 | } 34 | } 35 | } 36 | 37 | do { 38 | let view: UIView? = nil 39 | let views: [UIView] = [] 40 | 41 | mondrianBatchLayout { 42 | 43 | view?.mondrian.layout.height(.min(10)) 44 | 45 | views.map { 46 | $0.mondrian.layout.height(.min(10)) 47 | } 48 | 49 | views.map { 50 | $0.mondrian.layout.height(.min(10)) 51 | } 52 | } 53 | } 54 | 55 | do { 56 | let view: UIView? = nil 57 | let views: [UIView?] = [] 58 | 59 | mondrianBatchLayout { 60 | 61 | view?.mondrian.layout.height(.min(10)) 62 | 63 | views.map { 64 | $0?.mondrian.layout.height(.min(10)) 65 | } 66 | 67 | views.map { 68 | $0?.mondrian.layout.height(.min(10)) 69 | } 70 | } 71 | } 72 | 73 | do { 74 | let view: UIView? = nil 75 | mondrianBatchLayout { 76 | view?.mondrian.layout.height(.min(10)) 77 | } 78 | } 79 | 80 | do { 81 | let view: UIView = UIView() 82 | mondrianBatchLayout { 83 | view.mondrian.layout.height(.min(10)) 84 | } 85 | } 86 | 87 | do { 88 | let view: UIView = UIView() 89 | mondrianBatchLayout { 90 | if true { 91 | view.mondrian.layout.height(.min(10)) 92 | } 93 | } 94 | } 95 | 96 | do { 97 | let flag = true 98 | let view: UIView = UIView() 99 | mondrianBatchLayout { 100 | if flag { 101 | view.mondrian.layout.height(.min(10)) 102 | } else { 103 | view.mondrian.layout.height(.min(10)) 104 | } 105 | } 106 | } 107 | } 108 | 109 | } 110 | 111 | func test_multiplier_constraints() { 112 | 113 | let view = ExampleView(width: nil, height: nil) { view in 114 | 115 | let box1 = UIView.mock( 116 | backgroundColor: .layeringColor, 117 | preferredSize: .init(width: 30, height: 30) 118 | ) 119 | 120 | let box2 = UIView.mock( 121 | backgroundColor: .layeringColor, 122 | preferredSize: .init(width: 30, height: 30) 123 | ) 124 | 125 | view.addSubview(box1) 126 | view.addSubview(box2) 127 | 128 | mondrianBatchLayout { 129 | 130 | box1.mondrian.layout 131 | .top(.toSuperview, .min(0)) 132 | .left(.toSuperview) 133 | .right(.to(box2).left) 134 | .bottom(.to(box2).bottom) 135 | 136 | box2.mondrian.layout 137 | .top(.toSuperview.top) 138 | .height(.to(box1).height, multiplier: 2) 139 | .right(.toSuperview) 140 | .bottom(.toSuperview) 141 | 142 | } 143 | } 144 | 145 | assertSnapshot(matching: view, as: .image, record: _record) 146 | 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /Tests/HStackTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HStackTests.swift 3 | // Tests 4 | // 5 | // Created by Muukii on 2021/06/17. 6 | // 7 | 8 | import Foundation 9 | import MondrianLayout 10 | import SnapshotTesting 11 | import XCTest 12 | 13 | final class HStackTests: XCTestCase { 14 | 15 | func test_mixing_spacer() { 16 | 17 | let view = ExampleView(width: 180, height: nil) { (view: UIView) in 18 | Mondrian.buildSubviews(on: view) { 19 | HStackBlock(spacing: 4) { 20 | UIView.mock( 21 | backgroundColor: .layeringColor, 22 | preferredSize: .smallSquare 23 | ) as UIView? 24 | 25 | StackingSpacer(minLength: 20) 26 | 27 | UIView.mock( 28 | backgroundColor: .layeringColor, 29 | preferredSize: .smallSquare 30 | ) 31 | 32 | UIView.mock( 33 | backgroundColor: .layeringColor, 34 | preferredSize: .smallSquare 35 | ) 36 | 37 | StackingSpacer(minLength: 20, expands: false) 38 | } 39 | .background(UIView.mock(backgroundColor: .layeringColor)) 40 | } 41 | } 42 | 43 | assertSnapshot(matching: view, as: .image, record: _record) 44 | 45 | } 46 | 47 | func test_additional_spacing() { 48 | 49 | let view = ExampleView(width: nil, height: nil) { (view: UIView) in 50 | Mondrian.buildSubviews(on: view) { 51 | HStackBlock(spacing: 4) { 52 | UIView.mock( 53 | preferredSize: .smallSquare 54 | ) 55 | 56 | StackingSpacer(minLength: 4) 57 | 58 | UIView.mock( 59 | preferredSize: .smallSquare 60 | ) 61 | 62 | UIView.mock( 63 | preferredSize: .smallSquare 64 | ) 65 | } 66 | } 67 | } 68 | 69 | assertSnapshot(matching: view, as: .image, record: _record) 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /Tests/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 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /Tests/LayoutDescriptorTests.swift: -------------------------------------------------------------------------------- 1 | 2 | import Foundation 3 | import MondrianLayout 4 | import SnapshotTesting 5 | import XCTest 6 | 7 | final class LayoutDescriptorTests: XCTestCase { 8 | 9 | func testSyntax() { 10 | 11 | let container = UIView() 12 | let view = UIView() 13 | 14 | container.addSubview(view) 15 | 16 | let g = view.mondrian.layout 17 | .width(10) 18 | .top(.toSuperview) 19 | .right(.toSuperview) 20 | .leading(.toSuperview) 21 | .activate() 22 | 23 | XCTAssertEqual(g.constraints.count, 4) 24 | } 25 | 26 | func testSyntax_batch() { 27 | 28 | let container = UIView() 29 | 30 | let parent = UIView() 31 | let view = UIView() 32 | 33 | container.addSubview(parent) 34 | container.addSubview(view) 35 | 36 | let group = mondrianBatchLayout { 37 | view.mondrian.layout.width(10) 38 | view.mondrian.layout.top(.to(parent)) 39 | } 40 | 41 | XCTAssertEqual(group.constraints.count, 2) 42 | } 43 | 44 | func test_layout_0() { 45 | 46 | let view = ExampleView(width: nil, height: nil) { view in 47 | 48 | let box1 = UIView.mock(backgroundColor: .layeringColor, preferredSize: .smallSquare) 49 | let box2 = UIView.mock(backgroundColor: .layeringColor, preferredSize: .smallSquare) 50 | 51 | view.addSubview(box1) 52 | view.addSubview(box2) 53 | 54 | mondrianBatchLayout { 55 | 56 | box1.mondrian.layout 57 | .top(.toSuperview) 58 | .left(.toSuperview) 59 | .right(.to(box2).left) 60 | .bottom(.to(box2).bottom) 61 | 62 | box2.mondrian.layout 63 | .top(.toSuperview.top, .exact(10)) 64 | .right(.toSuperview) 65 | .bottom(.toSuperview) 66 | 67 | } 68 | } 69 | 70 | assertSnapshot(matching: view, as: .image, record: _record) 71 | 72 | } 73 | 74 | func test_layout_center() { 75 | 76 | let view = ExampleView(width: nil, height: nil) { view in 77 | 78 | let containerCenterDemo = UIView.mock(backgroundColor: .layeringColor, preferredSize: .largeSquare) 79 | let containeeCenterDemo = UIView.mock(backgroundColor: .layeringColor, preferredSize: .smallSquare) 80 | 81 | view.addSubview(containerCenterDemo) 82 | 83 | containerCenterDemo.addSubview(containeeCenterDemo) 84 | 85 | mondrianBatchLayout { 86 | 87 | containerCenterDemo.mondrian.layout 88 | .edges(.toSuperview) 89 | 90 | containeeCenterDemo.mondrian.layout 91 | .center(.toSuperview) 92 | 93 | } 94 | } 95 | 96 | assertSnapshot(matching: view, as: .image, record: _record) 97 | 98 | } 99 | 100 | func test_layout_edge() { 101 | 102 | let view = ExampleView(width: nil, height: nil) { view in 103 | 104 | let containerEdgesDemo = UIView.mock(backgroundColor: .layeringColor, preferredSize: .largeSquare) 105 | let containeeEdgesDemo = UIView.mock(backgroundColor: .layeringColor) 106 | 107 | view.addSubview(containerEdgesDemo) 108 | 109 | containerEdgesDemo.addSubview(containeeEdgesDemo) 110 | 111 | mondrianBatchLayout { 112 | 113 | containerEdgesDemo.mondrian.layout 114 | .edges(.toSuperview) 115 | 116 | containeeEdgesDemo.mondrian.layout 117 | .edges(.toSuperview, .exact(8)) 118 | } 119 | } 120 | 121 | assertSnapshot(matching: view, as: .image, record: _record) 122 | 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /Tests/OverlayTests.swift: -------------------------------------------------------------------------------- 1 | 2 | import Foundation 3 | import MondrianLayout 4 | import SnapshotTesting 5 | import XCTest 6 | 7 | final class OverlayTests: XCTestCase { 8 | 9 | func test_1() { 10 | 11 | let view = ExampleView(width: nil, height: nil) { (view: UIView) in 12 | Mondrian.buildSubviews(on: view) { 13 | UIView.mock( 14 | backgroundColor: .layeringColor, 15 | preferredSize: .init(width: 100, height: 100) 16 | ) 17 | .viewBlock 18 | .overlay( 19 | UIView.mock(backgroundColor: .layeringColor) 20 | .viewBlock 21 | .overlay( 22 | UIView.mock(backgroundColor: .layeringColor) 23 | .viewBlock 24 | .padding(10) 25 | ) 26 | .padding(10) 27 | ) 28 | } 29 | } 30 | 31 | assertSnapshot(matching: view, as: .image, record: _record) 32 | 33 | } 34 | 35 | func test_2() { 36 | 37 | let view = ExampleView(width: nil, height: nil) { (view: UIView) in 38 | Mondrian.buildSubviews(on: view) { 39 | VStackBlock { 40 | UIView.mock( 41 | preferredSize: .smallSquare 42 | ) 43 | UIView.mock( 44 | preferredSize: .smallSquare 45 | ) 46 | UIView.mock( 47 | preferredSize: .smallSquare 48 | ) 49 | } 50 | .padding(10) 51 | .overlay(UIView.mock(backgroundColor: .layeringColor)) 52 | } 53 | } 54 | 55 | XCTAssertFalse(view.hasAmbiguousLayoutRecursively) 56 | assertSnapshot(matching: view, as: .image, record: _record) 57 | 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /Tests/RelativeTests.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import MondrianLayout 3 | import SnapshotTesting 4 | import XCTest 5 | 6 | final class RelateiveTests: XCTestCase { 7 | 8 | func test_centering_in_ambiguous() { 9 | let view = ExampleView(width: 100, height: 100) { view in 10 | Mondrian.buildSubviews(on: view) { 11 | ZStackBlock { 12 | UILabel.mockSingleline(text: "A") 13 | .viewBlock 14 | .background(UIView.mock()) 15 | .relative(.all, .min(20)) 16 | } 17 | .background(UIView.mock()) 18 | } 19 | } 20 | assertSnapshot(matching: view, as: .image, record: _record) 21 | } 22 | 23 | func test_accumulate_relative() { 24 | let view = ExampleView(width: 100, height: 100) { view in 25 | Mondrian.buildSubviews(on: view) { 26 | LayoutContainer(attachedSafeAreaEdges: .all) { 27 | ZStackBlock { 28 | 29 | UIView.mock(backgroundColor: .layeringColor) 30 | .viewBlock.alignSelf(.attach(.all)) 31 | 32 | UIView.mock(backgroundColor: .layeringColor, preferredSize: .smallSquare) 33 | .viewBlock 34 | .relative(.bottom, 20) 35 | .relative(.trailing, 20) 36 | } 37 | } 38 | } 39 | } 40 | 41 | assertSnapshot(matching: view, as: .image, record: _record) 42 | } 43 | 44 | func test_accumulate_padding() { 45 | 46 | let view = ExampleView(width: 100, height: 100) { view in 47 | Mondrian.buildSubviews(on: view) { 48 | LayoutContainer(attachedSafeAreaEdges: .vertical) { 49 | VStackBlock { 50 | UIView.mock( 51 | backgroundColor: .layeringColor, 52 | preferredSize: .smallSquare 53 | ) 54 | .viewBlock 55 | .padding(.bottom, 10) 56 | .padding(.bottom, 10) 57 | .padding(.bottom, 10) 58 | } 59 | .background( 60 | UIView.mock( 61 | backgroundColor: .layeringColor, 62 | preferredSize: .smallSquare 63 | ) 64 | ) 65 | } 66 | } 67 | } 68 | 69 | assertSnapshot(matching: view, as: .image, record: _record) 70 | 71 | } 72 | 73 | func test_accumulation_0() { 74 | 75 | var base = RelativeBlock.ConstrainedValue() 76 | 77 | XCTAssert(base.min == nil) 78 | XCTAssert(base.exact == nil) 79 | XCTAssert(base.max == nil) 80 | 81 | let other = RelativeBlock.ConstrainedValue(min: 0, exact: nil, max: nil) 82 | 83 | base.accumulate(other) 84 | 85 | XCTAssertEqual(base.min, 0) 86 | XCTAssertEqual(base.exact, nil) 87 | XCTAssertEqual(base.max, nil) 88 | 89 | } 90 | 91 | func test_accumulation_1() { 92 | 93 | var base = RelativeBlock.ConstrainedValue(min: 1, exact: nil, max: nil) 94 | 95 | XCTAssertEqual(base.min, 1) 96 | XCTAssertEqual(base.exact, nil) 97 | XCTAssertEqual(base.max, nil) 98 | 99 | let other = RelativeBlock.ConstrainedValue(min: nil, exact: nil, max: nil) 100 | 101 | base.accumulate(other) 102 | 103 | XCTAssertEqual(base.min, 1) 104 | XCTAssertEqual(base.exact, nil) 105 | XCTAssertEqual(base.max, nil) 106 | 107 | } 108 | 109 | func test_accumulation_2() { 110 | 111 | var base = RelativeBlock.ConstrainedValue(min: 1, exact: nil, max: nil) 112 | 113 | XCTAssertEqual(base.min, 1) 114 | XCTAssertEqual(base.exact, nil) 115 | XCTAssertEqual(base.max, nil) 116 | 117 | let other = RelativeBlock.ConstrainedValue(min: 1, exact: nil, max: nil) 118 | 119 | base.accumulate(other) 120 | 121 | XCTAssertEqual(base.min, 2) 122 | XCTAssertEqual(base.exact, nil) 123 | XCTAssertEqual(base.max, nil) 124 | 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /Tests/SizingTests.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import MondrianLayout 3 | import SnapshotTesting 4 | import XCTest 5 | 6 | final class SizingTests: XCTestCase { 7 | 8 | func test_sizing() { 9 | let view = ExampleView(width: 200, height: 200) { (view: UIView) in 10 | Mondrian.buildSubviews(on: view) { 11 | ZStackBlock { 12 | 13 | HStackBlock { 14 | VStackBlock(alignment: .leading) { 15 | UIView.mock( 16 | backgroundColor: .layeringColor, 17 | preferredSize: .init(width: 36, height: 36) 18 | ) 19 | .viewBlock 20 | .alignSelf(.fill) 21 | 22 | UIView.mock( 23 | backgroundColor: .layeringColor, 24 | preferredSize: .init(width: 36, height: 36) 25 | ) 26 | .viewBlock 27 | .alignSelf(.fill) 28 | } 29 | .width(20) 30 | 31 | VStackBlock(alignment: .leading) { 32 | UIView.mock( 33 | backgroundColor: .layeringColor, 34 | preferredSize: .init(width: 36, height: 36) 35 | ) 36 | .viewBlock 37 | .alignSelf(.fill) 38 | 39 | UIView.mock( 40 | backgroundColor: .layeringColor, 41 | preferredSize: .init(width: 36, height: 36) 42 | ) 43 | .viewBlock 44 | .alignSelf(.fill) 45 | } 46 | .width(40) 47 | 48 | UIView.mock( 49 | backgroundColor: .layeringColor, 50 | preferredSize: .init(width: 36, height: 36) 51 | ) 52 | .viewBlock 53 | .padding(10) 54 | .width(.max(30)) 55 | 56 | } 57 | .height(50) 58 | 59 | } 60 | } 61 | } 62 | assertSnapshot(matching: view, as: .image, record: _record) 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /Tests/SyntaxTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ZStackTests.swift 3 | // Tests 4 | // 5 | // Created by Muukii on 2021/06/18. 6 | // 7 | 8 | import Foundation 9 | import MondrianLayout 10 | import SnapshotTesting 11 | import XCTest 12 | 13 | final class SyntaxTests: XCTestCase { 14 | 15 | func test_vStack() { 16 | 17 | let view = ExampleView(width: 20, height: nil) { view in 18 | Mondrian.buildSubviews(on: view) { 19 | LayoutContainer(attachedSafeAreaEdges: .vertical) { 20 | VStackBlock { 21 | 22 | UIView.mock(backgroundColor: .layeringColor) 23 | .viewBlock 24 | .height(10) 25 | .spacingBefore(10) 26 | 27 | UIView.mock(backgroundColor: .layeringColor) 28 | .viewBlock 29 | .height(10) 30 | .spacingAfter(10) 31 | 32 | UIView.mock(backgroundColor: .layeringColor) 33 | .viewBlock 34 | .height(10) 35 | .spacingAfter(10) 36 | .spacingAfter(10) 37 | .spacingBefore(10) 38 | .spacingBefore(10) 39 | 40 | } 41 | .background(UIView.mock(backgroundColor: .layeringColor)) 42 | } 43 | } 44 | } 45 | 46 | XCTAssertEqual(view.frame.height, 90) 47 | assertSnapshot(matching: view, as: .image, record: _record) 48 | 49 | } 50 | 51 | func test_hStack() { 52 | 53 | let view = ExampleView(width: nil, height: 20) { view in 54 | Mondrian.buildSubviews(on: view) { 55 | LayoutContainer(attachedSafeAreaEdges: .vertical) { 56 | HStackBlock { 57 | 58 | UIView.mock(backgroundColor: .layeringColor) 59 | .viewBlock 60 | .width(10) 61 | .spacingBefore(10) 62 | 63 | UIView.mock(backgroundColor: .layeringColor) 64 | .viewBlock 65 | .width(10) 66 | .spacingAfter(10) 67 | 68 | UIView.mock(backgroundColor: .layeringColor) 69 | .viewBlock 70 | .width(10) 71 | .spacingAfter(10) 72 | .spacingAfter(10) 73 | .spacingBefore(10) 74 | .spacingBefore(10) 75 | 76 | } 77 | .background(UIView.mock(backgroundColor: .layeringColor)) 78 | } 79 | } 80 | } 81 | 82 | XCTAssertEqual(view.frame.width, 90) 83 | assertSnapshot(matching: view, as: .image, record: _record) 84 | 85 | } 86 | 87 | } 88 | -------------------------------------------------------------------------------- /Tests/Tests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Tests.swift 3 | // Tests 4 | // 5 | // Created by Muukii on 2021/06/13. 6 | // 7 | 8 | import XCTest 9 | import MondrianLayout 10 | import SnapshotTesting 11 | 12 | let _record = ProcessInfo().environment["RECORD"] != nil 13 | 14 | final class LayoutSnapshotTests: XCTestCase { 15 | 16 | func test_1() { 17 | 18 | let view = ExampleView(width: 100, height: 100) { view in 19 | Mondrian.buildSubviews(on: view) { 20 | VStackBlock { 21 | ZStackBlock { 22 | UIView.mock( 23 | backgroundColor: .layeringColor, 24 | preferredSize: .init(width: 100, height: 100) 25 | ) 26 | 27 | UIView.mock( 28 | backgroundColor: .layeringColor, 29 | preferredSize: .init(width: 10, height: 10) 30 | ) 31 | .viewBlock 32 | .relative([.top, .trailing], 10) 33 | 34 | } 35 | } 36 | } 37 | } 38 | 39 | assertSnapshot(matching: view, as: .image, record: _record) 40 | 41 | } 42 | 43 | func test_mondrian() { 44 | 45 | let view = ExampleView(width: 200, height: 200) { (view: UIView) in 46 | Mondrian.buildSubviews(on: view) { 47 | 48 | HStackBlock(spacing: 2, alignment: .fill) { 49 | VStackBlock(spacing: 2, alignment: .fill) { 50 | UIView.mock( 51 | backgroundColor: .layeringColor, 52 | preferredSize: .smallSquare 53 | ) 54 | 55 | UIView.mock( 56 | backgroundColor: .layeringColor, 57 | preferredSize: .init(width: 28, height: 50) 58 | ) 59 | 60 | UIView.mock( 61 | backgroundColor: .layeringColor, 62 | preferredSize: .smallSquare 63 | ) 64 | 65 | UIView.mock( 66 | backgroundColor: .layeringColor, 67 | preferredSize: .smallSquare 68 | ) 69 | 70 | HStackBlock(spacing: 2, alignment: .fill) { 71 | UIView.mock( 72 | backgroundColor: .layeringColor, 73 | preferredSize: .smallSquare 74 | ) 75 | UIView.mock( 76 | backgroundColor: .layeringColor, 77 | preferredSize: .smallSquare 78 | ) 79 | } 80 | } 81 | 82 | VStackBlock(spacing: 2, alignment: .fill) { 83 | HStackBlock(spacing: 2, alignment: .fill) { 84 | UIView.mock( 85 | backgroundColor: .layeringColor, 86 | preferredSize: .smallSquare 87 | ) 88 | VStackBlock(spacing: 2, alignment: .fill) { 89 | HStackBlock(spacing: 2, alignment: .fill) { 90 | UIView.mock( 91 | backgroundColor: .layeringColor, 92 | preferredSize: .smallSquare 93 | ) 94 | UIView.mock( 95 | backgroundColor: .layeringColor, 96 | preferredSize: .smallSquare 97 | ) 98 | } 99 | UIView.mock( 100 | backgroundColor: .layeringColor, 101 | preferredSize: .smallSquare 102 | ) 103 | } 104 | } 105 | 106 | HStackBlock(spacing: 2, alignment: .fill) { 107 | VStackBlock(spacing: 2, alignment: .fill) { 108 | UIView.mock( 109 | backgroundColor: .layeringColor, 110 | preferredSize: .smallSquare 111 | ) 112 | UIView.mock( 113 | backgroundColor: .layeringColor, 114 | preferredSize: .smallSquare 115 | ) 116 | } 117 | 118 | UIView.mock( 119 | backgroundColor: .layeringColor, 120 | preferredSize: .smallSquare 121 | ) 122 | 123 | VStackBlock(spacing: 2, alignment: .fill) { 124 | UIView.mock( 125 | backgroundColor: .layeringColor, 126 | preferredSize: .smallSquare 127 | ) 128 | UIView.mock( 129 | backgroundColor: .layeringColor, 130 | preferredSize: .smallSquare 131 | ) 132 | } 133 | } 134 | 135 | HStackBlock(spacing: 2, alignment: .fill) { 136 | UIView.mock( 137 | backgroundColor: .layeringColor, 138 | preferredSize: .smallSquare 139 | ) 140 | VStackBlock(spacing: 2, alignment: .fill) { 141 | UIView.mock( 142 | backgroundColor: .layeringColor, 143 | preferredSize: .smallSquare 144 | ) 145 | UIView.mock( 146 | backgroundColor: .layeringColor, 147 | preferredSize: .smallSquare 148 | ) 149 | } 150 | } 151 | 152 | } 153 | 154 | } 155 | .overlay( 156 | UILabel.mockMultiline(text: "Mondrian Layout", textColor: .white) 157 | .viewBlock 158 | .padding(4) 159 | .background( 160 | UIView.mock( 161 | backgroundColor: .layeringColor 162 | ) 163 | .viewBlock 164 | ) 165 | .relative([.bottom, .trailing], 10) 166 | ) 167 | 168 | } 169 | } 170 | 171 | assertSnapshot(matching: view, as: .image, record: _record) 172 | } 173 | 174 | } 175 | 176 | /* 177 | @discardableResult 178 | func layout( 179 | @LayoutConstraintBuilder _ c: () -> Content 180 | ) -> Content { 181 | c() 182 | } 183 | 184 | class Tests: XCTestCase { 185 | 186 | func test_pattern_stack() { 187 | 188 | _ = layout { 189 | HStackBlock { 190 | UIView() 191 | } 192 | } 193 | 194 | _ = layout { 195 | HStackBlock { 196 | UIView() 197 | StackSpacer(minLength: 0) 198 | } 199 | } 200 | 201 | } 202 | 203 | func test_pattern1() { 204 | 205 | _ = layout { 206 | HStackBlock { 207 | // VStackBlock { 208 | // UIView() 209 | // } 210 | } 211 | } 212 | 213 | } 214 | 215 | func test_pattern2() { 216 | 217 | _ = layout { 218 | HStackBlock { 219 | UIView() 220 | // VStackBlock {} 221 | } 222 | } 223 | 224 | } 225 | 226 | func test_pattern3() { 227 | 228 | _ = layout { 229 | HStackBlock { 230 | UIView() 231 | UIView() 232 | } 233 | } 234 | 235 | } 236 | } 237 | */ 238 | -------------------------------------------------------------------------------- /Tests/VGridTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // VGridTests.swift 3 | // Tests 4 | // 5 | // Created by Muukii on 2021/12/15. 6 | // 7 | 8 | import Foundation 9 | import MondrianLayout 10 | import SnapshotTesting 11 | import XCTest 12 | 13 | final class VGridTests: XCTestCase { 14 | 15 | func test_basic() { 16 | 17 | let view = ExampleView(width: 100, height: nil) { view in 18 | Mondrian.buildSubviews(on: view) { 19 | VGridBlock( 20 | columns: [ 21 | .init(.flexible(), spacing: 16), 22 | .init(.flexible(), spacing: 16), 23 | ], 24 | spacing: 4 25 | ) { 26 | 27 | UILabel.mockMultiline(text: "Helloooo") 28 | .viewBlock 29 | .overlay(UIView.mock(backgroundColor: .layeringColor, preferredSize: .smallSquare)) 30 | 31 | UIView.mock(backgroundColor: .layeringColor, preferredSize: .smallSquare) 32 | UIView.mock(backgroundColor: .layeringColor, preferredSize: .smallSquare) 33 | UIView.mock(backgroundColor: .layeringColor, preferredSize: .smallSquare) 34 | 35 | UIView.mock(backgroundColor: .layeringColor, preferredSize: .smallSquare) 36 | UIView.mock(backgroundColor: .layeringColor, preferredSize: .smallSquare) 37 | UIView.mock(backgroundColor: .layeringColor, preferredSize: .smallSquare) 38 | UIView.mock(backgroundColor: .layeringColor, preferredSize: .smallSquare) 39 | 40 | UIView.mock(backgroundColor: .layeringColor, preferredSize: .smallSquare) 41 | UIView.mock(backgroundColor: .layeringColor, preferredSize: .smallSquare) 42 | } 43 | } 44 | } 45 | 46 | assertSnapshot(matching: view, as: .image, record: _record) 47 | 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Tests/VStackTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OverlayTests.swift 3 | // Tests 4 | // 5 | // Created by Muukii on 2021/06/16. 6 | // 7 | 8 | import Foundation 9 | import MondrianLayout 10 | import SnapshotTesting 11 | import XCTest 12 | 13 | final class VStackTests: XCTestCase { 14 | 15 | func test_mixing_spacer() { 16 | let view = ExampleView(width: nil, height: 180) { (view: UIView) in 17 | Mondrian.buildSubviews(on: view) { 18 | VStackBlock(spacing: 4) { 19 | UIView.mock( 20 | backgroundColor: .layeringColor, 21 | preferredSize: .smallSquare 22 | ) 23 | 24 | StackingSpacer(minLength: 20) 25 | 26 | UIView.mock( 27 | backgroundColor: .layeringColor, 28 | preferredSize: .smallSquare 29 | ) 30 | 31 | UIView.mock( 32 | backgroundColor: .layeringColor, 33 | preferredSize: .smallSquare 34 | ) 35 | 36 | StackingSpacer(minLength: 20, expands: false) 37 | } 38 | .background(UIView.mock(backgroundColor: .layeringColor)) 39 | } 40 | } 41 | 42 | assertSnapshot(matching: view, as: .image, record: _record) 43 | } 44 | 45 | func test_enter() { 46 | 47 | let view = ExampleView(width: nil, height: nil) { (view: UIView) in 48 | Mondrian.buildSubviews(on: view) { 49 | VStackBlock(spacing: 4, alignment: .center) { 50 | UILabel.mockMultiline(text: "Hello", textColor: .white) 51 | .viewBlock 52 | .padding(8) 53 | .background(UIView.mock(backgroundColor: .layeringColor)) 54 | UILabel.mockMultiline(text: "Mondrian", textColor: .white) 55 | .viewBlock 56 | .padding(8) 57 | .background(UIView.mock(backgroundColor: .layeringColor)) 58 | UILabel.mockMultiline(text: "Layout!", textColor: .white) 59 | .viewBlock 60 | .padding(8) 61 | .background(UIView.mock(backgroundColor: .layeringColor)) 62 | } 63 | } 64 | } 65 | 66 | assertSnapshot(matching: view, as: .image, record: _record) 67 | 68 | } 69 | 70 | func test_leading() { 71 | 72 | let view = ExampleView(width: nil, height: nil) { (view: UIView) in 73 | Mondrian.buildSubviews(on: view) { 74 | VStackBlock(spacing: 4, alignment: .leading) { 75 | UILabel.mockMultiline(text: "Hello", textColor: .white) 76 | .viewBlock 77 | .padding(8) 78 | .background(UIView.mock(backgroundColor: .layeringColor)) 79 | UILabel.mockMultiline(text: "Mondrian", textColor: .white) 80 | .viewBlock 81 | .padding(8) 82 | .background(UIView.mock(backgroundColor: .layeringColor)) 83 | UILabel.mockMultiline(text: "Layout!", textColor: .white) 84 | .viewBlock 85 | .padding(8) 86 | .background(UIView.mock(backgroundColor: .layeringColor)) 87 | } 88 | } 89 | } 90 | 91 | assertSnapshot(matching: view, as: .image, record: _record) 92 | 93 | } 94 | 95 | func test_trailing() { 96 | 97 | let view = ExampleView(width: nil, height: nil) { (view: UIView) in 98 | Mondrian.buildSubviews(on: view) { 99 | VStackBlock(spacing: 4, alignment: .trailing) { 100 | UILabel.mockMultiline(text: "Hello", textColor: .white) 101 | .viewBlock 102 | .padding(8) 103 | .background(UIView.mock(backgroundColor: .layeringColor)) 104 | UILabel.mockMultiline(text: "Mondrian", textColor: .white) 105 | .viewBlock 106 | .padding(8) 107 | .background(UIView.mock(backgroundColor: .layeringColor)) 108 | UILabel.mockMultiline(text: "Layout!", textColor: .white) 109 | .viewBlock 110 | .padding(8) 111 | .background(UIView.mock(backgroundColor: .layeringColor)) 112 | } 113 | } 114 | } 115 | 116 | assertSnapshot(matching: view, as: .image, record: _record) 117 | 118 | } 119 | 120 | func test_additional_spacing() { 121 | 122 | let view = ExampleView(width: nil, height: nil) { (view: UIView) in 123 | Mondrian.buildSubviews(on: view) { 124 | VStackBlock(spacing: 4) { 125 | UIView.mock( 126 | preferredSize: .smallSquare 127 | ) 128 | 129 | StackingSpacer(minLength: 4) 130 | 131 | UIView.mock( 132 | preferredSize: .smallSquare 133 | ) 134 | 135 | UIView.mock( 136 | preferredSize: .smallSquare 137 | ) 138 | } 139 | } 140 | } 141 | 142 | assertSnapshot(matching: view, as: .image, record: _record) 143 | } 144 | 145 | func test_label_alignment() { 146 | 147 | let view = ExampleView(width: 200, height: 200) { (view: UIView) in 148 | Mondrian.buildSubviews(on: view) { 149 | VStackBlock(alignment: .leading) { 150 | UILabel.mockMultiline(text: "Hello") 151 | .viewBlock 152 | .padding(.horizontal, 10) 153 | UIView.mock(backgroundColor: .layeringColor) 154 | .viewBlock 155 | .alignSelf(.fill) 156 | } 157 | .background(UIView.mock(backgroundColor: .layeringColor)) 158 | } 159 | } 160 | 161 | assertSnapshot(matching: view, as: .image, record: _record) 162 | 163 | } 164 | 165 | func test_including_layoutGuide() { 166 | 167 | let view = ExampleView(width: 200, height: 200) { (view: UIView) in 168 | 169 | let boxes = (0..<3).map { _ in UIView.mock(backgroundColor: .layeringColor) } 170 | let guides = (0..<2).map { _ in UILayoutGuide() } 171 | 172 | Mondrian.buildSubviews(on: view) { 173 | VStackBlock(alignment: .leading) { 174 | 175 | boxes[0] 176 | 177 | guides[0] 178 | 179 | boxes[1] 180 | 181 | guides[1] 182 | 183 | boxes[2] 184 | 185 | StackingSpacer(minLength: 0) 186 | 187 | } 188 | .background(UIView.mock(backgroundColor: .layeringColor)) 189 | } 190 | 191 | mondrianBatchLayout { 192 | 193 | boxes.map { $0.mondrian.layout.height(20) } 194 | 195 | guides[0].mondrian.layout.height(.to(boxes[0])) 196 | guides[1].mondrian.layout.height(.to(boxes[2]), multiplier: 2) 197 | } 198 | } 199 | 200 | assertSnapshot(matching: view, as: .image, record: _record) 201 | } 202 | 203 | } 204 | -------------------------------------------------------------------------------- /Tests/ZStackTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ZStackTests.swift 3 | // Tests 4 | // 5 | // Created by Muukii on 2021/06/18. 6 | // 7 | 8 | import Foundation 9 | import MondrianLayout 10 | import SnapshotTesting 11 | import XCTest 12 | 13 | final class ZStackTests: XCTestCase { 14 | 15 | func test_minimum_padding() { 16 | 17 | let view = ExampleView(width: 100, height: 100) { view in 18 | Mondrian.buildSubviews(on: view) { 19 | ZStackBlock { 20 | ZStackBlock { 21 | UILabel.mockSingleline(text: "Hello Hello Hello") 22 | .viewBlock 23 | .background(UIView.mock()) 24 | } 25 | .padding(20) /// a minimum padding for the label in the container 26 | } 27 | .background(UIView.mock()) 28 | } 29 | } 30 | 31 | assertSnapshot(matching: view, as: .image, record: _record) 32 | 33 | } 34 | 35 | func test_expandsElementIfCanBeExpanding() { 36 | 37 | let view = ExampleView(width: 100, height: 100) { view in 38 | Mondrian.buildSubviews(on: view) { 39 | LayoutContainer(attachedSafeAreaEdges: .vertical) { 40 | ZStackBlock { 41 | 42 | /// this view must be expanded to parent view 43 | UIView.mock( 44 | backgroundColor: .layeringColor 45 | ) 46 | 47 | /// this view must be sized with intrinsic content size. 48 | UIView.mock( 49 | backgroundColor: .layeringColor, 50 | preferredSize: .smallSquare 51 | ) 52 | } 53 | } 54 | } 55 | } 56 | 57 | assertSnapshot(matching: view, as: .image, record: _record) 58 | 59 | } 60 | 61 | func test_expandsElementWithRelative() { 62 | 63 | let view = ExampleView(width: 100, height: 100) { view in 64 | Mondrian.buildSubviews(on: view) { 65 | LayoutContainer(attachedSafeAreaEdges: .vertical) { 66 | ZStackBlock { 67 | UIView.mock( 68 | backgroundColor: .layeringColor, 69 | preferredSize: .smallSquare 70 | ) 71 | .viewBlock 72 | .relative(0) 73 | } 74 | } 75 | } 76 | } 77 | 78 | assertSnapshot(matching: view, as: .image, record: _record) 79 | } 80 | 81 | func test_alignSelf() { 82 | 83 | let view = ExampleView(width: 100, height: 100) { view in 84 | Mondrian.buildSubviews(on: view) { 85 | LayoutContainer(attachedSafeAreaEdges: .vertical) { 86 | ZStackBlock { 87 | 88 | UIView.mock( 89 | backgroundColor: .layeringColor, 90 | preferredSize: .smallSquare 91 | ) 92 | .viewBlock 93 | .alignSelf(.attach(.all)) 94 | 95 | UIView.mock( 96 | backgroundColor: .layeringColor, 97 | preferredSize: .smallSquare 98 | ) 99 | .viewBlock 100 | .alignSelf(.attach(.top)) 101 | 102 | UIView.mock( 103 | backgroundColor: .layeringColor, 104 | preferredSize: .smallSquare 105 | ) 106 | .viewBlock 107 | .alignSelf(.attach(.bottom)) 108 | 109 | UIView.mock( 110 | backgroundColor: .layeringColor, 111 | preferredSize: .smallSquare 112 | ) 113 | .viewBlock 114 | .alignSelf(.attach(.leading)) 115 | 116 | UIView.mock( 117 | backgroundColor: .layeringColor, 118 | preferredSize: .smallSquare 119 | ) 120 | .viewBlock 121 | .alignSelf(.attach(.trailing)) 122 | 123 | UIView.mock( 124 | backgroundColor: .layeringColor, 125 | preferredSize: .smallSquare 126 | ) 127 | .viewBlock 128 | .alignSelf(.attach([.top, .leading])) 129 | 130 | UIView.mock( 131 | backgroundColor: .layeringColor, 132 | preferredSize: .smallSquare 133 | ) 134 | .viewBlock 135 | .alignSelf(.attach([.top, .trailing])) 136 | 137 | UIView.mock( 138 | backgroundColor: .layeringColor, 139 | preferredSize: .smallSquare 140 | ) 141 | .viewBlock 142 | .alignSelf(.attach([.bottom, .leading])) 143 | 144 | UIView.mock( 145 | backgroundColor: .layeringColor, 146 | preferredSize: .smallSquare 147 | ) 148 | .viewBlock 149 | .alignSelf(.attach([.bottom, .trailing])) 150 | 151 | 152 | UIView.mock( 153 | backgroundColor: .layeringColor, 154 | preferredSize: .smallSquare 155 | ) 156 | .viewBlock 157 | .alignSelf(.attach([.top, .bottom])) 158 | 159 | UIView.mock( 160 | backgroundColor: .layeringColor, 161 | preferredSize: .smallSquare 162 | ) 163 | .viewBlock 164 | .alignSelf(.attach([.leading, .trailing])) 165 | 166 | UIView.mock( 167 | backgroundColor: .layeringColor, 168 | preferredSize: .smallSquare 169 | ) 170 | .viewBlock 171 | .alignSelf(.attach([.top, .leading, .trailing])) 172 | 173 | UIView.mock( 174 | backgroundColor: .layeringColor, 175 | preferredSize: .smallSquare 176 | ) 177 | .viewBlock 178 | .alignSelf(.attach([.bottom, .leading, .trailing])) 179 | 180 | UIView.mock( 181 | backgroundColor: .layeringColor, 182 | preferredSize: .smallSquare 183 | ) 184 | .viewBlock 185 | .alignSelf(.attach([.top, .bottom, .leading])) 186 | 187 | UIView.mock( 188 | backgroundColor: .layeringColor, 189 | preferredSize: .smallSquare 190 | ) 191 | .viewBlock 192 | .alignSelf(.attach([.top, .bottom, .trailing])) 193 | 194 | UIView.mock( 195 | backgroundColor: .layeringColor, 196 | preferredSize: .smallSquare 197 | ) 198 | .viewBlock 199 | .alignSelf(.attach([])) 200 | 201 | UIView.mock( 202 | backgroundColor: .layeringColor, 203 | preferredSize: .smallSquare 204 | ) 205 | .viewBlock 206 | .alignSelf(.center) 207 | 208 | } 209 | } 210 | } 211 | } 212 | 213 | assertSnapshot(matching: view, as: .image, record: _record) 214 | 215 | } 216 | } 217 | -------------------------------------------------------------------------------- /Tests/__Snapshots__/ClassicTests/test_multiplier_constraints.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluidGroup/MondrianLayout/b26edaa5a469bc5fd58f9afe37d257ca1b3771e0/Tests/__Snapshots__/ClassicTests/test_multiplier_constraints.1.png -------------------------------------------------------------------------------- /Tests/__Snapshots__/HStackTests/test_additional_spacing.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluidGroup/MondrianLayout/b26edaa5a469bc5fd58f9afe37d257ca1b3771e0/Tests/__Snapshots__/HStackTests/test_additional_spacing.1.png -------------------------------------------------------------------------------- /Tests/__Snapshots__/HStackTests/test_mixing_spacer.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluidGroup/MondrianLayout/b26edaa5a469bc5fd58f9afe37d257ca1b3771e0/Tests/__Snapshots__/HStackTests/test_mixing_spacer.1.png -------------------------------------------------------------------------------- /Tests/__Snapshots__/LayoutDescriptorTests/test_layout_0.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluidGroup/MondrianLayout/b26edaa5a469bc5fd58f9afe37d257ca1b3771e0/Tests/__Snapshots__/LayoutDescriptorTests/test_layout_0.1.png -------------------------------------------------------------------------------- /Tests/__Snapshots__/LayoutDescriptorTests/test_layout_center.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluidGroup/MondrianLayout/b26edaa5a469bc5fd58f9afe37d257ca1b3771e0/Tests/__Snapshots__/LayoutDescriptorTests/test_layout_center.1.png -------------------------------------------------------------------------------- /Tests/__Snapshots__/LayoutDescriptorTests/test_layout_edge.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluidGroup/MondrianLayout/b26edaa5a469bc5fd58f9afe37d257ca1b3771e0/Tests/__Snapshots__/LayoutDescriptorTests/test_layout_edge.1.png -------------------------------------------------------------------------------- /Tests/__Snapshots__/OverlayTests/test_1.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluidGroup/MondrianLayout/b26edaa5a469bc5fd58f9afe37d257ca1b3771e0/Tests/__Snapshots__/OverlayTests/test_1.1.png -------------------------------------------------------------------------------- /Tests/__Snapshots__/OverlayTests/test_2.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluidGroup/MondrianLayout/b26edaa5a469bc5fd58f9afe37d257ca1b3771e0/Tests/__Snapshots__/OverlayTests/test_2.1.png -------------------------------------------------------------------------------- /Tests/__Snapshots__/RelativeTests/test_accumulate_padding.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluidGroup/MondrianLayout/b26edaa5a469bc5fd58f9afe37d257ca1b3771e0/Tests/__Snapshots__/RelativeTests/test_accumulate_padding.1.png -------------------------------------------------------------------------------- /Tests/__Snapshots__/RelativeTests/test_accumulate_relative.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluidGroup/MondrianLayout/b26edaa5a469bc5fd58f9afe37d257ca1b3771e0/Tests/__Snapshots__/RelativeTests/test_accumulate_relative.1.png -------------------------------------------------------------------------------- /Tests/__Snapshots__/RelativeTests/test_centering_in_ambiguous.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluidGroup/MondrianLayout/b26edaa5a469bc5fd58f9afe37d257ca1b3771e0/Tests/__Snapshots__/RelativeTests/test_centering_in_ambiguous.1.png -------------------------------------------------------------------------------- /Tests/__Snapshots__/SizingTests/test_sizing.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluidGroup/MondrianLayout/b26edaa5a469bc5fd58f9afe37d257ca1b3771e0/Tests/__Snapshots__/SizingTests/test_sizing.1.png -------------------------------------------------------------------------------- /Tests/__Snapshots__/SyntaxTests/test_hStack.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluidGroup/MondrianLayout/b26edaa5a469bc5fd58f9afe37d257ca1b3771e0/Tests/__Snapshots__/SyntaxTests/test_hStack.1.png -------------------------------------------------------------------------------- /Tests/__Snapshots__/SyntaxTests/test_vStack.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluidGroup/MondrianLayout/b26edaa5a469bc5fd58f9afe37d257ca1b3771e0/Tests/__Snapshots__/SyntaxTests/test_vStack.1.png -------------------------------------------------------------------------------- /Tests/__Snapshots__/Tests/test_1.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluidGroup/MondrianLayout/b26edaa5a469bc5fd58f9afe37d257ca1b3771e0/Tests/__Snapshots__/Tests/test_1.1.png -------------------------------------------------------------------------------- /Tests/__Snapshots__/Tests/test_mondrian.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluidGroup/MondrianLayout/b26edaa5a469bc5fd58f9afe37d257ca1b3771e0/Tests/__Snapshots__/Tests/test_mondrian.1.png -------------------------------------------------------------------------------- /Tests/__Snapshots__/VGridTests/test_basic.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluidGroup/MondrianLayout/b26edaa5a469bc5fd58f9afe37d257ca1b3771e0/Tests/__Snapshots__/VGridTests/test_basic.1.png -------------------------------------------------------------------------------- /Tests/__Snapshots__/VStackTests/test_additional_spacing.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluidGroup/MondrianLayout/b26edaa5a469bc5fd58f9afe37d257ca1b3771e0/Tests/__Snapshots__/VStackTests/test_additional_spacing.1.png -------------------------------------------------------------------------------- /Tests/__Snapshots__/VStackTests/test_enter.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluidGroup/MondrianLayout/b26edaa5a469bc5fd58f9afe37d257ca1b3771e0/Tests/__Snapshots__/VStackTests/test_enter.1.png -------------------------------------------------------------------------------- /Tests/__Snapshots__/VStackTests/test_including_layoutGuide.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluidGroup/MondrianLayout/b26edaa5a469bc5fd58f9afe37d257ca1b3771e0/Tests/__Snapshots__/VStackTests/test_including_layoutGuide.1.png -------------------------------------------------------------------------------- /Tests/__Snapshots__/VStackTests/test_label_alignment.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluidGroup/MondrianLayout/b26edaa5a469bc5fd58f9afe37d257ca1b3771e0/Tests/__Snapshots__/VStackTests/test_label_alignment.1.png -------------------------------------------------------------------------------- /Tests/__Snapshots__/VStackTests/test_leading.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluidGroup/MondrianLayout/b26edaa5a469bc5fd58f9afe37d257ca1b3771e0/Tests/__Snapshots__/VStackTests/test_leading.1.png -------------------------------------------------------------------------------- /Tests/__Snapshots__/VStackTests/test_mixing_spacer.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluidGroup/MondrianLayout/b26edaa5a469bc5fd58f9afe37d257ca1b3771e0/Tests/__Snapshots__/VStackTests/test_mixing_spacer.1.png -------------------------------------------------------------------------------- /Tests/__Snapshots__/VStackTests/test_trailing.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluidGroup/MondrianLayout/b26edaa5a469bc5fd58f9afe37d257ca1b3771e0/Tests/__Snapshots__/VStackTests/test_trailing.1.png -------------------------------------------------------------------------------- /Tests/__Snapshots__/ZStackTests/test_alignSelf.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluidGroup/MondrianLayout/b26edaa5a469bc5fd58f9afe37d257ca1b3771e0/Tests/__Snapshots__/ZStackTests/test_alignSelf.1.png -------------------------------------------------------------------------------- /Tests/__Snapshots__/ZStackTests/test_expandsElementIfCanBeExpanding.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluidGroup/MondrianLayout/b26edaa5a469bc5fd58f9afe37d257ca1b3771e0/Tests/__Snapshots__/ZStackTests/test_expandsElementIfCanBeExpanding.1.png -------------------------------------------------------------------------------- /Tests/__Snapshots__/ZStackTests/test_expandsElementWithRelative.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluidGroup/MondrianLayout/b26edaa5a469bc5fd58f9afe37d257ca1b3771e0/Tests/__Snapshots__/ZStackTests/test_expandsElementWithRelative.1.png -------------------------------------------------------------------------------- /Tests/__Snapshots__/ZStackTests/test_minimum_padding.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluidGroup/MondrianLayout/b26edaa5a469bc5fd58f9afe37d257ca1b3771e0/Tests/__Snapshots__/ZStackTests/test_minimum_padding.1.png -------------------------------------------------------------------------------- /fastlane/Appfile: -------------------------------------------------------------------------------- 1 | # app_identifier("[[APP_IDENTIFIER]]") # The bundle identifier of your app 2 | # apple_id("[[APPLE_ID]]") # Your Apple email address 3 | 4 | 5 | # For more information about the Appfile, see: 6 | # https://docs.fastlane.tools/advanced/#appfile 7 | -------------------------------------------------------------------------------- /fastlane/Fastfile: -------------------------------------------------------------------------------- 1 | # This file contains the fastlane.tools configuration 2 | # You can find the documentation at https://docs.fastlane.tools 3 | # 4 | # For a list of all available actions, check out 5 | # 6 | # https://docs.fastlane.tools/actions 7 | # 8 | # For a list of all available plugins, check out 9 | # 10 | # https://docs.fastlane.tools/plugins/available-plugins 11 | # 12 | 13 | # Uncomment the line if you want fastlane to automatically update itself 14 | # update_fastlane 15 | 16 | default_platform(:ios) 17 | 18 | platform :ios do 19 | desc "Description of what the lane does" 20 | lane :custom_lane do 21 | # add actions here: https://docs.fastlane.tools/actions 22 | end 23 | 24 | lane :test_project do 25 | scan(scheme: "Tests") 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /fastlane/README.md: -------------------------------------------------------------------------------- 1 | fastlane documentation 2 | ================ 3 | # Installation 4 | 5 | Make sure you have the latest version of the Xcode command line tools installed: 6 | 7 | ``` 8 | xcode-select --install 9 | ``` 10 | 11 | Install _fastlane_ using 12 | ``` 13 | [sudo] gem install fastlane -NV 14 | ``` 15 | or alternatively using `brew install fastlane` 16 | 17 | # Available Actions 18 | ## iOS 19 | ### ios custom_lane 20 | ``` 21 | fastlane ios custom_lane 22 | ``` 23 | Description of what the lane does 24 | ### ios test_project 25 | ``` 26 | fastlane ios test_project 27 | ``` 28 | 29 | 30 | ---- 31 | 32 | This README.md is auto-generated and will be re-generated every time [fastlane](https://fastlane.tools) is run. 33 | More information about fastlane can be found on [fastlane.tools](https://fastlane.tools). 34 | The documentation of fastlane can be found on [docs.fastlane.tools](https://docs.fastlane.tools). 35 | --------------------------------------------------------------------------------