├── .gitignore ├── Cartfile ├── Cartfile.resolved ├── Classes ├── Enums │ ├── Padding.swift │ ├── Position.swift │ └── Vertical.swift ├── Extensions │ ├── GridSrollView+Subviews.swift │ ├── GridView+Calculations.swift │ ├── GridView+Drawing.swift │ ├── GridView+Layout.swift │ ├── GridView+Subviews.swift │ └── UIColor+Tools.swift ├── Libs │ ├── Compatibility.swift │ ├── Properties.swift │ └── Subview.swift ├── Protocols │ ├── GridViewInterface.swift │ └── ScrollViewForwarder.swift ├── View controllers │ ├── GridScrollViewController.swift │ └── GridViewController.swift └── Views │ ├── GridCollectionReusableView.swift │ ├── GridCollectionViewCell.swift │ ├── GridScrollView.swift │ ├── GridTableViewCell.swift │ ├── GridTableViewHeaderFooterView.swift │ └── GridView.swift ├── Hagrid Demo ├── AppDelegate.swift ├── Extensions │ └── UIColor+Tools.swift ├── Supporting files │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── Icon-60@2x.png │ │ │ ├── Icon-60@3x.png │ │ │ ├── Icon-76.png │ │ │ ├── Icon-76@2x.png │ │ │ ├── Icon-83.5@2x.png │ │ │ ├── Icon-Notification.png │ │ │ ├── Icon-Notification@2x.png │ │ │ ├── Icon-Notification@3x.png │ │ │ ├── Icon-Small-40.png │ │ │ ├── Icon-Small-40@2x.png │ │ │ ├── Icon-Small-40@3x.png │ │ │ ├── Icon-Small.png │ │ │ ├── Icon-Small@2x.png │ │ │ ├── Icon-Small@3x.png │ │ │ └── Icon1024.png │ │ ├── Contents.json │ │ └── TestData │ │ │ ├── AlbumCover.imageset │ │ │ ├── Contents.json │ │ │ └── Pearl-Jam-Ten.jpg │ │ │ └── Contents.json │ ├── Base.lproj │ │ └── LaunchScreen.storyboard │ └── Info.plist └── View controllers │ └── ViewController.swift ├── Hagrid-iOS └── Supporting files │ ├── Info.plist │ └── TheGrid.h ├── Hagrid-macOS └── Supporting files │ ├── Hagrid_macOS.h │ └── Info.plist ├── Hagrid-tvOS └── Supporting files │ ├── Hagrid_appleTV.h │ └── Info.plist ├── Hagrid.podspec ├── Hagrid.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── xcshareddata │ └── xcschemes │ │ ├── Hagrid iOS.xcscheme │ │ ├── Hagrid macOS.xcscheme │ │ └── Hagrid tvOS.xcscheme └── xcuserdata │ └── pro.xcuserdatad │ └── xcschemes │ ├── Demo App.xcscheme │ └── xcschememanagement.plist ├── LICENSE ├── Other ├── logo.png ├── screen1.png ├── screen2.png ├── screen3.png ├── screen4.png └── screen5.png ├── Podfile ├── Podfile.lock └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /Carthage 3 | 4 | # Xcode 5 | # 6 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 7 | 8 | ## Build generated 9 | build/ 10 | DerivedData/ 11 | 12 | ## Various settings 13 | *.pbxuser 14 | !default.pbxuser 15 | *.mode1v3 16 | !default.mode1v3 17 | *.mode2v3 18 | !default.mode2v3 19 | *.perspectivev3 20 | !default.perspectivev3 21 | xcuserdata/ 22 | 23 | ## Other 24 | *.moved-aside 25 | *.xccheckout 26 | *.xcscmblueprint 27 | 28 | ## Obj-C/Swift specific 29 | *.hmap 30 | *.ipa 31 | *.dSYM.zip 32 | *.dSYM 33 | 34 | ## Playgrounds 35 | timeline.xctimeline 36 | playground.xcworkspace 37 | 38 | # Swift Package Manager 39 | # 40 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 41 | # Packages/ 42 | # Package.pins 43 | # Package.resolved 44 | .build/ 45 | 46 | # CocoaPods 47 | # 48 | # We recommend against adding the Pods directory to your .gitignore. However 49 | # you should judge for yourself, the pros and cons are mentioned at: 50 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 51 | # 52 | # Pods/ 53 | 54 | # Carthage 55 | # 56 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 57 | # Carthage/Checkouts 58 | 59 | Carthage/Checkouts 60 | Carthage/Build 61 | 62 | # fastlane 63 | # 64 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 65 | # screenshots whenever they are needed. 66 | # For more information about the recommended setup visit: 67 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 68 | 69 | fastlane/report.xml 70 | fastlane/Preview.html 71 | fastlane/screenshots/**/*.png 72 | fastlane/test_output 73 | .DS_Store 74 | *.pbxproj-e 75 | -------------------------------------------------------------------------------- /Cartfile: -------------------------------------------------------------------------------- 1 | github "SnapKit/SnapKit" "4.0.0" 2 | -------------------------------------------------------------------------------- /Cartfile.resolved: -------------------------------------------------------------------------------- 1 | github "SnapKit/SnapKit" "4.0.0" 2 | -------------------------------------------------------------------------------- /Classes/Enums/Padding.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Padding.swift 3 | // Hagrid 4 | // 5 | // Created by Ondrej Rafaj on 29/05/2018. 6 | // Copyright © 2018 LiveUI. All rights reserved. 7 | // 8 | 9 | @_exported import Foundation 10 | 11 | 12 | /// Element padding 13 | public enum Padding { 14 | 15 | /// None 16 | case none 17 | 18 | /// Top 19 | case top(CGFloat) 20 | 21 | /// Bottom 22 | case bottom(CGFloat) 23 | 24 | /// Left 25 | case left(CGFloat) 26 | 27 | /// Right 28 | case right(CGFloat) 29 | 30 | /// Sides (left, right) 31 | case horizontal(left: CGFloat, right: CGFloat) 32 | 33 | /// Top/bottom (top, bottom) 34 | case vertical(top: CGFloat, bottom: CGFloat) 35 | 36 | /// Full (top, left, right, bottom) 37 | case full(top: CGFloat, left: CGFloat, right: CGFloat, bottom: CGFloat) 38 | 39 | } 40 | 41 | 42 | extension Padding { 43 | 44 | #if os(iOS) || os(tvOS) 45 | typealias EdgeInsetsAlias = UIEdgeInsets 46 | #elseif os(OSX) 47 | typealias EdgeInsetsAlias = NSEdgeInsets 48 | #endif 49 | 50 | /// UIEdgeInsets representation 51 | var value: EdgeInsetsAlias { 52 | switch self { 53 | case .top(let top): 54 | return EdgeInsetsAlias(top: top, left: 0, bottom: 0, right: 0) 55 | case .bottom(let bottom): 56 | return EdgeInsetsAlias(top: 0, left: 0, bottom: bottom, right: 0) 57 | case .left(let left): 58 | return EdgeInsetsAlias(top: 0, left: left, bottom: 0, right: 0) 59 | case .right(let right): 60 | return EdgeInsetsAlias(top: 0, left: 0, bottom: 0, right: right) 61 | case .horizontal(let left, let right): 62 | return EdgeInsetsAlias(top: 0, left: left, bottom: 0, right: right) 63 | case .vertical(let top, let bottom): 64 | return EdgeInsetsAlias(top: top, left: 0, bottom: bottom, right: 0) 65 | case .full(let top, let left, let right, let bottom): 66 | return EdgeInsetsAlias(top: top, left: left, bottom: bottom, right: right) 67 | case .none: 68 | return EdgeInsetsAlias(top: 0, left: 0, bottom: 0, right: 0) 69 | } 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /Classes/Enums/Position.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Position.swift 3 | // Hagrid 4 | // 5 | // Created by Ondrej Rafaj on 29/05/2018. 6 | // Copyright © 2018 LiveUI. All rights reserved. 7 | // 8 | 9 | @_exported import Foundation 10 | 11 | 12 | /// Basic position value object 13 | public struct Position: ExpressibleByIntegerLiteral { 14 | 15 | /// Internal storage enum 16 | enum Storage { 17 | 18 | /// Specific column on the grid 19 | case col(Int) 20 | 21 | /// Last column on a grid view 22 | case last 23 | 24 | /// N-th column from the end 25 | case reversed(Int) 26 | 27 | /// Up to another element 28 | case relation(ViewAlias, margin: CGFloat) 29 | 30 | /// Match position of another view 31 | case match(ViewAlias, margin: CGFloat) 32 | 33 | /// Dynamic position 34 | case dynamic 35 | 36 | #if os(iOS) || os(tvOS) 37 | /// Custom position for a given size class (trait collection) 38 | case custom(((_ traitCollection: UITraitCollection) -> Position)) 39 | #endif 40 | 41 | } 42 | 43 | /// Storage 44 | var storage: Storage 45 | 46 | /// Initializer 47 | init(_ value: Storage) { 48 | storage = value 49 | } 50 | 51 | /// First column 52 | public static var first: Position { return .col(0) } 53 | 54 | /// Specific column on the grid 55 | public static func col(_ value: Int) -> Position { return .init(.col(value)) } 56 | 57 | /// Last column on a grid view 58 | public static var last: Position { return .init(.last) } 59 | 60 | /// N-th column from the end 61 | public static func reversed(_ value: Int) -> Position { return .init(.reversed(value)) } 62 | 63 | /// Up to another element 64 | public static func relation(_ view: ViewAlias, margin: CGFloat = 0) -> Position { return .init(.relation(view, margin: margin)) } 65 | 66 | /// Match position of another view 67 | public static func match(_ view: ViewAlias, margin: CGFloat = 0) -> Position { return .init(.match(view, margin: margin)) } 68 | 69 | /// Dynamic position 70 | public static var dynamic: Position { return .init(.dynamic) } 71 | 72 | #if os(iOS) || os(tvOS) 73 | /// Custom position for a given size class (trait collection) 74 | public static func custom(_ closure: @escaping ((_ traitCollection: UITraitCollection) -> Position)) -> Position { return .init(.custom(closure)) } 75 | #endif 76 | 77 | /// Initialize with a number 78 | public init(integerLiteral value: Int) { 79 | storage = .col(value) 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /Classes/Enums/Vertical.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Vertical.swift 3 | // Hagrid 4 | // 5 | // Created by Ondrej Rafaj on 30/05/2018. 6 | // Copyright © 2018 LiveUI. All rights reserved. 7 | // 8 | 9 | @_exported import Foundation 10 | 11 | 12 | /// Vertical positioning 13 | public struct Vertical: ExpressibleByFloatLiteral, ExpressibleByIntegerLiteral { 14 | 15 | /// Internal storage enum 16 | enum Storage { 17 | 18 | /// Don't set vertical position 19 | case none 20 | 21 | /// Center horizontally in superview 22 | case centerY 23 | 24 | /// Exact value from the top 25 | case exactly(fromTop: CGFloat) 26 | 27 | /// Match top of another view 28 | case match(ViewAlias, margin: CGFloat) 29 | 30 | /// Below another view (view, margin) 31 | case below(ViewAlias, margin: CGFloat) 32 | 33 | /// Above another view (view, margin) 34 | case above(ViewAlias, margin: CGFloat) 35 | 36 | /// Maintains a position (row) under a set of elements 37 | case row([ViewAlias], margin: CGFloat) 38 | 39 | #if os(iOS) || os(tvOS) 40 | /// Custom vertical position for a given size class (trait collection) 41 | case custom(((_ traitCollection: UITraitCollection) -> Vertical)) 42 | #endif 43 | 44 | } 45 | 46 | /// Storage 47 | var storage: Storage 48 | 49 | /// Initializer 50 | init(_ value: Storage) { 51 | storage = value 52 | } 53 | 54 | /// Don't set vertical position 55 | public static var none: Vertical { return .init(.none) } 56 | 57 | /// Center horizontally in superview 58 | public static var centerY: Vertical { return .init(.centerY) } 59 | 60 | /// Top of the grid 61 | public static var toTop: Vertical { return .exactly(fromTop: 0) } 62 | 63 | /// Exact value from the top 64 | public static func exactly(fromTop: CGFloat) -> Vertical { return .init(.exactly(fromTop: fromTop)) } 65 | 66 | /// Match top of another view 67 | public static func match(_ view: ViewAlias, margin: CGFloat = 0) -> Vertical { return .init(.match(view, margin: margin)) } 68 | 69 | /// Maintains a position under a set of elements 70 | public static func below(_ views: [ViewAlias], margin: CGFloat = 0) -> Vertical { return .init(.row(views, margin: margin)) } 71 | 72 | /// Below another view (view, margin) 73 | public static func below(_ view: ViewAlias, margin: CGFloat = 0) -> Vertical { return .init(.below(view, margin: margin)) } 74 | 75 | /// Above another view (view, margin) 76 | public static func above(_ view: ViewAlias, margin: CGFloat = 0) -> Vertical { return .init(.above(view, margin: margin)) } 77 | 78 | #if os(iOS) || os(tvOS) 79 | /// Custom vertical position for a given size class (trait collection) 80 | public static func custom(_ closure: @escaping ((_ traitCollection: UITraitCollection) -> Vertical)) -> Vertical { return .init(.custom(closure)) } 81 | #endif 82 | 83 | /// Initialize with a Float 84 | public init(floatLiteral value: Float) { 85 | storage = .exactly(fromTop: CGFloat(value)) 86 | } 87 | 88 | /// Initialize with an Int 89 | public init(integerLiteral value: Int) { 90 | storage = .exactly(fromTop: CGFloat(value)) 91 | } 92 | 93 | } 94 | -------------------------------------------------------------------------------- /Classes/Extensions/GridSrollView+Subviews.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GridSrollView+Subviews.swift 3 | // Hagrid 4 | // 5 | // Created by Ondrej Rafaj on 31/05/2018. 6 | // Copyright © 2018 LiveUI. All rights reserved. 7 | // 8 | 9 | #if os(iOS) || os(tvOS) 10 | @_exported import Foundation 11 | @_exported import SnapKit 12 | @_exported import UIKit 13 | 14 | 15 | extension GridScrollView { 16 | 17 | /** 18 | Add subview 19 | 20 | - parameters: 21 | - view: View to be added 22 | - top: Top margin 23 | - pos: Position on the grid 24 | - space: Number of columns to fill 25 | - padding: Subview padding on the grid 26 | - redraw: SnapKit make closure for that fine tunning we all need 27 | */ 28 | public func add(subview view: ViewAlias, _ vertical: Vertical? = nil, from: Position = .first, space: Position = .last, padding: Padding = .none, redraw: ((_ make: ConstraintMaker) -> Void)? = nil) { 29 | let subview = Subview( 30 | properties: Properties( 31 | vertical: vertical, 32 | from: from, 33 | space: space, 34 | redraw: redraw, 35 | padding: padding 36 | ), 37 | view: view 38 | ) 39 | gridView.addSubview(subview) 40 | } 41 | 42 | @available(*, unavailable, message: "This method is unavailable, please use add(subview:_:from:space:padding:redraw:) instead") 43 | open override func addSubview(_ view: ViewAlias) { 44 | super.addSubview(view) 45 | } 46 | 47 | } 48 | 49 | #endif 50 | -------------------------------------------------------------------------------- /Classes/Extensions/GridView+Calculations.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GridView+Calculations.swift 3 | // Hagrid 4 | // 5 | // Created by Ondrej Rafaj on 29/05/2018. 6 | // Copyright © 2018 LiveUI. All rights reserved. 7 | // 8 | 9 | @_exported import Foundation 10 | 11 | 12 | extension GridView { 13 | 14 | /// Pixel position for column 15 | func x(_ column: Int) -> CGFloat { 16 | return (CGFloat(column) * config.columnWidth) + config.padding.value.left 17 | } 18 | 19 | } 20 | 21 | extension GridView.Config { 22 | 23 | /// Recalculate cache 24 | func recalculate() { 25 | columnWidth = calculatedColumnWidth() 26 | } 27 | 28 | /// Column width 29 | private func calculatedColumnWidth() -> CGFloat { 30 | let availableWidth = (element.bounds.size.width - (padding.value.left + padding.value.right)) 31 | let columnWidth = (availableWidth / CGFloat(numberOfColumns)) 32 | return columnWidth 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /Classes/Extensions/GridView+Drawing.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GridView+Drawing.swift 3 | // Hagrid 4 | // 5 | // Created by Ondrej Rafaj on 29/05/2018. 6 | // Copyright © 2018 LiveUI. All rights reserved. 7 | // 8 | 9 | #if os(iOS) || os(tvOS) 10 | @_exported import Foundation 11 | @_exported import UIKit 12 | 13 | 14 | extension GridView { 15 | 16 | /// Draw rect 17 | open override func draw(_ rect: CGRect) { 18 | drawGrid(on: rect) 19 | } 20 | 21 | /// Draw grid view if needed 22 | func drawGrid(on rect: CGRect) { 23 | if config.displayGrid { 24 | // Get the right color co the views background color 25 | let color = (((backgroundColor ?? .white).isDark ? .white : .black) as UIColor).withAlphaComponent(0.1) 26 | 27 | // Draw vertical lines 28 | for i in 0...config.numberOfColumns { 29 | let x = self.x(i) 30 | let bezierPath = UIBezierPath() 31 | bezierPath.move(to: CGPoint(x: x, y: config.padding.value.top)) 32 | bezierPath.addLine(to: CGPoint(x: x, y: bounds.size.height)) 33 | color.setStroke() 34 | bezierPath.lineWidth = 1 35 | bezierPath.stroke() 36 | 37 | if i > 0 && i <= config.numberOfColumns { 38 | drawColumnNumber(in: i, color: color) 39 | } 40 | } 41 | } 42 | } 43 | 44 | func drawColumnNumber(in column: Int, color: UIColor) { 45 | let context = UIGraphicsGetCurrentContext()! 46 | 47 | let x = self.x((column - 1)) 48 | let font = (config.columnWidth > 20) ? UIFont.boldSystemFont(ofSize: (config.columnWidth / 2)) : UIFont.systemFont(ofSize: (config.columnWidth / 2)) 49 | 50 | if bounds.size.height < 200 { 51 | let y = ((bounds.size.height / 2) - (font.pointSize / 2)) 52 | drawColumnNumber(at: y, column: column, x: x, font: font, color: color, context: context) 53 | } else { 54 | var y: CGFloat = 100 55 | while y < bounds.size.height { 56 | drawColumnNumber(at: y, column: column, x: x, font: font, color: color, context: context) 57 | y += 100 58 | } 59 | } 60 | } 61 | 62 | func drawColumnNumber(at y: CGFloat, column: Int, x: CGFloat, font: UIFont, color: UIColor, context: CGContext) { 63 | let textRect = CGRect(x: x, y: y, width: config.columnWidth, height: 44) 64 | let textTextContent = String(column) 65 | let textStyle = NSMutableParagraphStyle() 66 | textStyle.alignment = .center 67 | let textFontAttributes = [ 68 | .font: font, 69 | .foregroundColor: color, 70 | .paragraphStyle: textStyle, 71 | ] as [NSAttributedStringKey: Any] 72 | 73 | let textTextHeight: CGFloat = textTextContent.boundingRect(with: CGSize(width: textRect.width, height: CGFloat.infinity), options: .usesLineFragmentOrigin, attributes: textFontAttributes, context: nil).height 74 | context.saveGState() 75 | context.clip(to: textRect) 76 | textTextContent.draw(in: CGRect(x: textRect.minX, y: textRect.minY + (textRect.height - textTextHeight) / 2, width: textRect.width, height: textTextHeight), withAttributes: textFontAttributes) 77 | context.restoreGState() 78 | } 79 | 80 | } 81 | 82 | #endif 83 | -------------------------------------------------------------------------------- /Classes/Extensions/GridView+Layout.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GridView+Layout.swift 3 | // Hagrid 4 | // 5 | // Created by Ondrej Rafaj on 29/05/2018. 6 | // Copyright © 2018 LiveUI. All rights reserved. 7 | // 8 | 9 | @_exported import Foundation 10 | #if os(iOS) || os(tvOS) 11 | @_exported import UIKit 12 | #elseif os(OSX) 13 | @_exported import Cocoa 14 | #endif 15 | 16 | 17 | extension GridView { 18 | 19 | #if os(iOS) || os(tvOS) 20 | /// Layout subviews override 21 | @available(*, unavailable, message: "This method is unavailable") 22 | open override func layoutSubviews() { 23 | super.layoutSubviews() 24 | 25 | executeLayout() 26 | } 27 | #elseif os(OSX) 28 | /// Layout subviews override 29 | @available(*, unavailable, message: "This method is unavailable") 30 | open override func layout() { 31 | macLayout() 32 | } 33 | 34 | /// Mac layout 35 | func macLayout() { 36 | super.layout() 37 | 38 | executeLayout() 39 | } 40 | #endif 41 | 42 | /// Execute layout 43 | private func executeLayout() { 44 | if self.superview != nil { 45 | config.recalculate() 46 | config.reDraw() 47 | 48 | for subview in gridSubviews { 49 | layout(subview) 50 | } 51 | } 52 | } 53 | 54 | /// Layout a single Subview 55 | func layout(_ subview: Subview) { 56 | subview.view.snp.remakeConstraints { (make) in 57 | // Top 58 | self.make(subview, top: make) 59 | 60 | // Left 61 | self.make(subview, left: make) 62 | 63 | // Right 64 | self.make(subview, right: make) 65 | 66 | // Dynamic size 67 | if config.automaticVerticalSizing { 68 | make.bottom.lessThanOrEqualTo(self.snp.bottom).offset(-config.padding.value.bottom) 69 | } 70 | 71 | // Run custom constraints 72 | subview.properties.redraw?(make) 73 | } 74 | } 75 | 76 | /// Build top constraints 77 | private func make(_ subview: Subview, top make: ConstraintMaker) { 78 | if let vertical = subview.properties.vertical { 79 | func set(vertical: Vertical) { 80 | switch vertical.storage { 81 | case .none: 82 | break 83 | case .centerY: 84 | make.centerY.equalToSuperview() 85 | case .exactly(fromTop: let top): 86 | make.top.equalTo(top + config.padding.value.top) 87 | case .match(let view, margin: let margin): 88 | make.top.equalTo(view).offset(margin) 89 | case .below(let view, margin: let margin): 90 | make.top.equalTo(view.snp.bottom).offset(margin) 91 | case .above(let view, margin: let margin): 92 | make.bottom.equalTo(view.snp.top).offset(-margin) 93 | case .row(let views, margin: let margin): 94 | for view in views { 95 | make.top.greaterThanOrEqualTo(view.snp.bottom).offset(margin) 96 | } 97 | #if os(iOS) || os(tvOS) 98 | case .custom(let closure): 99 | set(vertical: closure(traitCollection)) 100 | #endif 101 | } 102 | } 103 | set(vertical: vertical) 104 | } else { 105 | make.top.greaterThanOrEqualTo(config.padding.value.top) 106 | } 107 | } 108 | 109 | /// Build left constraints 110 | private func make(_ subview: Subview, left make: ConstraintMaker) { 111 | func setLeft(position: Position) { 112 | let leftPadding = subview.properties.padding.value.left 113 | switch position.storage { 114 | case .col(let column): 115 | let left = (self.x(column) + leftPadding) 116 | make.left.equalTo(left) 117 | case .reversed(let column): 118 | let left = (self.x((config.numberOfColumns - column)) + leftPadding) 119 | make.left.equalTo(left) 120 | case .last: 121 | make.width.equalTo(self.x(config.numberOfColumns - 1)) 122 | case .dynamic: 123 | break 124 | case .match(let view, margin: let margin): 125 | make.left.equalTo(view.snp.left).offset(margin + leftPadding) 126 | case .relation(let view, margin: let margin): 127 | make.left.equalTo(view.snp.right).offset(margin + leftPadding) 128 | #if os(iOS) || os(tvOS) 129 | case .custom(let closure): 130 | setLeft(position: closure(traitCollection)) 131 | #endif 132 | } 133 | } 134 | setLeft(position: subview.properties.from) 135 | } 136 | 137 | /// Build right constraints 138 | private func make(_ subview: Subview, right make: ConstraintMaker) { 139 | func setRight(position: Position) { 140 | let rightPadding = subview.properties.padding.value.right 141 | let fullRightPadding = -(rightPadding + config.padding.value.right) 142 | switch position.storage { 143 | case .col(let column): 144 | if column >= 0 && column < config.numberOfColumns { // Defined column 145 | let col = config.numberOfColumns - (config.numberOfColumns - column) 146 | make.width.equalTo((CGFloat(col) * config.columnWidth) - rightPadding) 147 | } else { 148 | make.right.equalTo(fullRightPadding) 149 | } 150 | case .reversed(let column): 151 | make.right.equalTo(fullRightPadding - (CGFloat(column) * config.columnWidth)) 152 | case .last: 153 | make.right.equalTo(fullRightPadding) 154 | case .dynamic: 155 | break 156 | case .match(let view, margin: let margin): 157 | make.right.equalTo(view).offset(margin - rightPadding) 158 | case .relation(let view, margin: let margin): 159 | make.right.equalTo(view.snp.left).offset(margin - rightPadding) 160 | #if os(iOS) || os(tvOS) 161 | case .custom(let closure): 162 | setRight(position: closure(traitCollection)) 163 | #endif 164 | } 165 | } 166 | setRight(position: subview.properties.space) 167 | } 168 | 169 | } 170 | -------------------------------------------------------------------------------- /Classes/Extensions/GridView+Subviews.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GridView+Subviews.swift 3 | // Hagrid 4 | // 5 | // Created by Ondrej Rafaj on 29/05/2018. 6 | // Copyright © 2018 LiveUI. All rights reserved. 7 | // 8 | 9 | @_exported import Foundation 10 | @_exported import SnapKit 11 | #if os(iOS) || os(tvOS) 12 | @_exported import UIKit 13 | #elseif os(OSX) 14 | @_exported import Cocoa 15 | #endif 16 | 17 | 18 | extension GridView { 19 | 20 | /** 21 | Add subview 22 | 23 | - parameters: 24 | - view: View to be added 25 | - top: Top margin 26 | - pos: Position on the grid 27 | - space: Number of columns to fill 28 | - padding: Subview padding on the grid 29 | - redraw: SnapKit make closure for that fine tunning we all need 30 | */ 31 | public func add(subview view: ViewAlias, _ vertical: Vertical? = nil, from: Position = .first, space: Position = .last, padding: Padding = .none, redraw: ((_ make: ConstraintMaker) -> Void)? = nil) { 32 | let subview = Subview( 33 | properties: Properties( 34 | vertical: vertical, 35 | from: from, 36 | space: space, 37 | redraw: redraw, 38 | padding: padding 39 | ), 40 | view: view 41 | ) 42 | addSubview(subview) 43 | } 44 | 45 | @available(*, unavailable, message: "This method is unavailable", renamed: "add(subview:_:from:space:padding:redraw:)") 46 | open override func addSubview(_ view: ViewAlias) { 47 | fatalError("Add subview is not supported on a GridView") 48 | } 49 | 50 | } 51 | 52 | extension GridView { 53 | 54 | /// Add a real! subview 55 | func addSubview(_ subview: Subview) { 56 | gridSubviews.append(subview) 57 | super.addSubview(subview.view) 58 | } 59 | 60 | } 61 | 62 | extension GridView { 63 | 64 | /// Will remove subview from grid view 65 | open override func willRemoveSubview(_ subview: ViewAlias) { 66 | super.willRemoveSubview(subview) 67 | 68 | cleanGridSubviews() 69 | } 70 | 71 | /// Clean grid subviews 72 | func cleanGridSubviews() { 73 | gridSubviews.filter({ $0.view.superview == nil }).map({ gridSubviews.index(of: $0) }).forEach({ 74 | guard let index = $0 else { 75 | return 76 | } 77 | gridSubviews.remove(at: index) 78 | }) 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /Classes/Extensions/UIColor+Tools.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIColor+Tools.swift 3 | // Hagrid 4 | // 5 | // Created by Ondrej Rafaj on 29/05/2018. 6 | // Copyright © 2018 LiveUI. All rights reserved. 7 | // 8 | 9 | #if os(iOS) || os(tvOS) 10 | @_exported import Foundation 11 | @_exported import UIKit 12 | 13 | 14 | extension UIColor { 15 | 16 | /// Is color dark? 17 | var isDark: Bool { 18 | let rgb = rgba() 19 | if rgb[3] < 0.2 { 20 | return false 21 | } 22 | return (0.2126 * rgb[0] + 0.7152 * rgb[1] + 0.0722 * rgb[2]) < 0.5 23 | } 24 | 25 | /// GRB components 26 | func rgba() -> [CGFloat] { 27 | var (r, g, b, a): (CGFloat, CGFloat, CGFloat, CGFloat) = (0.0, 0.0, 0.0, 0.0) 28 | getRed(&r, green: &g, blue: &b, alpha: &a) 29 | return [r, g, b, a] 30 | } 31 | 32 | } 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /Classes/Libs/Compatibility.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Compatibility.swift 3 | // Hagrid 4 | // 5 | // Created by Ondrej Rafaj on 31/05/2018. 6 | // Copyright © 2018 LiveUI. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | #if os(iOS) || os(tvOS) 12 | 13 | @_exported import UIKit 14 | 15 | public typealias ViewAlias = UIView 16 | 17 | #elseif os(OSX) 18 | 19 | @_exported import Cocoa 20 | 21 | public typealias ViewAlias = NSView 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /Classes/Libs/Properties.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Properties.swift 3 | // Hagrid 4 | // 5 | // Created by Ondrej Rafaj on 29/05/2018. 6 | // Copyright © 2018 LiveUI. All rights reserved. 7 | // 8 | 9 | @_exported import Foundation 10 | @_exported import SnapKit 11 | 12 | 13 | /// Element properties 14 | public struct Properties { 15 | 16 | /// Vertical positioning 17 | public let vertical: Vertical? 18 | 19 | /// Starting position 20 | public let from: Position 21 | 22 | /// Horizontal space taken by the element 23 | public let space: Position 24 | 25 | /// Redraw SnapKit closure 26 | let redraw: ((_ make: ConstraintMaker) -> Void)? 27 | 28 | /// View padding 29 | let padding: Padding 30 | 31 | } 32 | -------------------------------------------------------------------------------- /Classes/Libs/Subview.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Subview.swift 3 | // Hagrid 4 | // 5 | // Created by Ondrej Rafaj on 29/05/2018. 6 | // Copyright © 2018 LiveUI. All rights reserved. 7 | // 8 | 9 | @_exported import Foundation 10 | 11 | 12 | /// Subview 13 | public struct Subview { 14 | 15 | /// Subview properties 16 | public let properties: Properties 17 | 18 | /// View 19 | public let view: ViewAlias 20 | 21 | } 22 | 23 | extension Subview: Hashable { 24 | 25 | /// Subviews hash value (equals hash value of it's view) 26 | public var hashValue: Int { 27 | return view.hashValue 28 | } 29 | 30 | } 31 | 32 | extension Subview: Equatable { 33 | 34 | public static func ==(lhs: Subview, rhs: Subview) -> Bool { 35 | return lhs.hashValue == rhs.hashValue 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /Classes/Protocols/GridViewInterface.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GridViewInterface.swift 3 | // Hagrid 4 | // 5 | // Created by Ondrej Rafaj on 31/05/2018. 6 | // Copyright © 2018 LiveUI. All rights reserved. 7 | // 8 | 9 | @_exported import Foundation 10 | 11 | 12 | /// Public interface for any grid view type 13 | public protocol GridViewInterface { 14 | 15 | /// Grid view configuration 16 | var config: GridView.Config { get } 17 | 18 | /// Grid subviews 19 | var gridSubviews: [Subview] { get } 20 | 21 | /** 22 | Add subview 23 | 24 | - parameters: 25 | - view: View to be added 26 | - top: Top margin 27 | - pos: Position on the grid 28 | - space: Number of columns to fill 29 | - padding: Subview padding on the grid 30 | - redraw: SnapKit make closure for that fine tunning we all need 31 | */ 32 | func add(subview view: ViewAlias, _ vertical: Vertical?, from: Position, space: Position, padding: Padding, redraw: ((_ make: ConstraintMaker) -> Void)?) 33 | 34 | } 35 | -------------------------------------------------------------------------------- /Classes/Protocols/ScrollViewForwarder.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ScrollViewForwarder.swift 3 | // Hagrid 4 | // 5 | // Created by Ondrej Rafaj on 31/05/2018. 6 | // Copyright © 2018 LiveUI. All rights reserved. 7 | // 8 | 9 | #if os(iOS) || os(tvOS) 10 | @_exported import Foundation 11 | @_exported import UIKit 12 | 13 | 14 | public protocol ScrollViewForwarder { 15 | 16 | var scrollView: UIScrollView { get } 17 | 18 | } 19 | 20 | extension GridScrollView { 21 | 22 | open var contentOffset: CGPoint { 23 | get { return scrollView.contentOffset } 24 | set { scrollView.contentOffset = newValue } 25 | } 26 | 27 | open var contentSize: CGSize { 28 | get { return scrollView.contentSize } 29 | set { scrollView.contentSize = newValue } 30 | } 31 | 32 | open var contentInset: UIEdgeInsets { 33 | get { return scrollView.contentInset } 34 | set { scrollView.contentInset = newValue } 35 | } 36 | 37 | #if os(iOS) 38 | 39 | @available(iOS 5.0, *) 40 | open var pinchGestureRecognizer: UIPinchGestureRecognizer? { 41 | return scrollView.pinchGestureRecognizer 42 | } 43 | 44 | open var scrollsToTop: Bool { 45 | get { return scrollView.scrollsToTop } 46 | set { scrollView.scrollsToTop = newValue } 47 | } 48 | 49 | @available(iOS 10.0, *) 50 | open var refreshControl: UIRefreshControl? { 51 | get { return scrollView.refreshControl } 52 | set { scrollView.refreshControl = newValue } 53 | } 54 | 55 | open var isPagingEnabled: Bool { 56 | get { return scrollView.isPagingEnabled } 57 | set { scrollView.isPagingEnabled = newValue } 58 | } 59 | 60 | @available(iOS 11.0, *) 61 | open var adjustedContentInset: UIEdgeInsets { 62 | return scrollView.adjustedContentInset 63 | } 64 | 65 | 66 | @available(iOS 11.0, *) 67 | open func adjustedContentInsetDidChange() { 68 | scrollView.adjustedContentInsetDidChange() 69 | } 70 | 71 | @available(iOS 11.0, *) 72 | open var contentInsetAdjustmentBehavior: UIScrollViewContentInsetAdjustmentBehavior { 73 | get { return scrollView.contentInsetAdjustmentBehavior } 74 | set { scrollView.contentInsetAdjustmentBehavior = newValue } 75 | } 76 | 77 | @available(iOS 11.0, *) 78 | open var contentLayoutGuide: UILayoutGuide { 79 | return scrollView.contentLayoutGuide 80 | } 81 | 82 | @available(iOS 11.0, *) 83 | open var frameLayoutGuide: UILayoutGuide { 84 | return scrollView.frameLayoutGuide 85 | } 86 | 87 | #elseif os(tvOS) 88 | 89 | @available(tvOS 11.0, *) 90 | open var adjustedContentInset: UIEdgeInsets { 91 | return scrollView.adjustedContentInset 92 | } 93 | 94 | 95 | @available(tvOS 11.0, *) 96 | open func adjustedContentInsetDidChange() { 97 | scrollView.adjustedContentInsetDidChange() 98 | } 99 | 100 | @available(tvOS 11.0, *) 101 | open var contentInsetAdjustmentBehavior: UIScrollViewContentInsetAdjustmentBehavior { 102 | get { return scrollView.contentInsetAdjustmentBehavior } 103 | set { scrollView.contentInsetAdjustmentBehavior = newValue } 104 | } 105 | 106 | @available(tvOS 11.0, *) 107 | open var contentLayoutGuide: UILayoutGuide { 108 | return scrollView.contentLayoutGuide 109 | } 110 | 111 | @available(tvOS 11.0, *) 112 | open var frameLayoutGuide: UILayoutGuide { 113 | return scrollView.frameLayoutGuide 114 | } 115 | 116 | #endif 117 | 118 | weak open var delegate: UIScrollViewDelegate? { 119 | get { return scrollView.delegate } 120 | set { scrollView.delegate = newValue } 121 | } 122 | 123 | open var isDirectionalLockEnabled: Bool { 124 | get { return scrollView.isDirectionalLockEnabled } 125 | set { scrollView.isDirectionalLockEnabled = newValue } 126 | } 127 | 128 | open var bounces: Bool { 129 | get { return scrollView.bounces } 130 | set { scrollView.bounces = newValue } 131 | } 132 | 133 | open var alwaysBounceVertical: Bool { 134 | get { return scrollView.alwaysBounceVertical } 135 | set { scrollView.alwaysBounceVertical = newValue } 136 | } 137 | 138 | open var alwaysBounceHorizontal: Bool { 139 | get { return scrollView.alwaysBounceHorizontal } 140 | set { scrollView.alwaysBounceHorizontal = newValue } 141 | } 142 | 143 | open var isScrollEnabled: Bool { 144 | get { return scrollView.isScrollEnabled } 145 | set { scrollView.isScrollEnabled = newValue } 146 | } 147 | 148 | open var showsHorizontalScrollIndicator: Bool { 149 | get { return scrollView.showsHorizontalScrollIndicator } 150 | set { scrollView.showsHorizontalScrollIndicator = newValue } 151 | } 152 | 153 | open var showsVerticalScrollIndicator: Bool { 154 | get { return scrollView.showsVerticalScrollIndicator } 155 | set { scrollView.showsVerticalScrollIndicator = newValue } 156 | } 157 | 158 | open var scrollIndicatorInsets: UIEdgeInsets { 159 | get { return scrollView.contentInset } 160 | set { scrollView.contentInset = newValue } 161 | } 162 | 163 | open var indicatorStyle: UIScrollViewIndicatorStyle { 164 | get { return scrollView.indicatorStyle } 165 | set { scrollView.indicatorStyle = newValue } 166 | } 167 | 168 | @available(iOS 3.0, *) 169 | open var decelerationRate: CGFloat { 170 | get { return scrollView.decelerationRate } 171 | set { scrollView.decelerationRate = newValue } 172 | } 173 | 174 | open var indexDisplayMode: UIScrollViewIndexDisplayMode { 175 | get { return scrollView.indexDisplayMode } 176 | set { scrollView.indexDisplayMode = newValue } 177 | } 178 | 179 | open func setContentOffset(_ contentOffset: CGPoint, animated: Bool) { 180 | scrollView.setContentOffset(contentOffset, animated: animated) 181 | } 182 | 183 | open func scrollRectToVisible(_ rect: CGRect, animated: Bool) { 184 | scrollView.scrollRectToVisible(rect, animated: animated) 185 | } 186 | 187 | 188 | open func flashScrollIndicators() { 189 | scrollView.flashScrollIndicators() 190 | } 191 | 192 | open var isTracking: Bool { 193 | return scrollView.isTracking 194 | } 195 | 196 | open var isDragging: Bool { 197 | return scrollView.isDragging 198 | } 199 | 200 | open var isDecelerating: Bool { 201 | return scrollView.isDecelerating 202 | } 203 | 204 | 205 | open var delaysContentTouches: Bool { 206 | get { return scrollView.delaysContentTouches } 207 | set { scrollView.delaysContentTouches = newValue } 208 | } 209 | 210 | open var canCancelContentTouches: Bool { 211 | get { return scrollView.canCancelContentTouches } 212 | set { scrollView.canCancelContentTouches = newValue } 213 | } 214 | 215 | open func touchesShouldBegin(_ touches: Set, with event: UIEvent?, in view: ViewAlias) -> Bool { 216 | return scrollView.touchesShouldBegin(touches, with: event, in: view) 217 | } 218 | 219 | open func touchesShouldCancel(in view: ViewAlias) -> Bool { 220 | return scrollView.touchesShouldCancel(in: view) 221 | } 222 | 223 | open var minimumZoomScale: CGFloat { 224 | get { return scrollView.minimumZoomScale } 225 | set { scrollView.minimumZoomScale = newValue } 226 | } 227 | 228 | open var maximumZoomScale: CGFloat { 229 | get { return scrollView.maximumZoomScale } 230 | set { scrollView.maximumZoomScale = newValue } 231 | } 232 | 233 | @available(iOS 3.0, *) 234 | open var zoomScale: CGFloat { 235 | get { return scrollView.zoomScale } 236 | set { scrollView.zoomScale = newValue } 237 | } 238 | 239 | @available(iOS 3.0, *) 240 | open func setZoomScale(_ scale: CGFloat, animated: Bool) { 241 | scrollView.setZoomScale(scale, animated: animated) 242 | } 243 | 244 | @available(iOS 3.0, *) 245 | open func zoom(to rect: CGRect, animated: Bool) { 246 | scrollView.zoom(to: rect, animated: animated) 247 | } 248 | 249 | open var bouncesZoom: Bool { 250 | get { return scrollView.bouncesZoom } 251 | set { scrollView.bouncesZoom = newValue } 252 | } 253 | 254 | open var isZooming: Bool { 255 | return scrollView.isZooming 256 | } 257 | 258 | open var isZoomBouncing: Bool { 259 | return scrollView.isZoomBouncing 260 | } 261 | 262 | @available(iOS 5.0, *) 263 | open var panGestureRecognizer: UIPanGestureRecognizer { 264 | return scrollView.panGestureRecognizer 265 | } 266 | 267 | open var directionalPressGestureRecognizer: UIGestureRecognizer { 268 | return scrollView.directionalPressGestureRecognizer 269 | } 270 | 271 | @available(iOS 7.0, *) 272 | open var keyboardDismissMode: UIScrollViewKeyboardDismissMode { 273 | get { return scrollView.keyboardDismissMode } 274 | set { scrollView.keyboardDismissMode = newValue } 275 | } 276 | 277 | } 278 | 279 | #endif 280 | -------------------------------------------------------------------------------- /Classes/View controllers/GridScrollViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GridScrollViewController.swift 3 | // Hagrid 4 | // 5 | // Created by Ondrej Rafaj on 31/05/2018. 6 | // Copyright © 2018 LiveUI. All rights reserved. 7 | // 8 | 9 | #if os(iOS) || os(tvOS) 10 | @_exported import Foundation 11 | 12 | 13 | /// Grid view view controller 14 | open class GridScrollViewController: UIViewController { 15 | 16 | /** 17 | Grid scroll view 18 | 19 | - important: Replaces default `view` 20 | */ 21 | public internal(set) var gridView: GridScrollView! 22 | 23 | /// Scrolling direction 24 | var scrollingDirection: GridScrollView.Direction 25 | 26 | /// Original view (removed) 27 | @available(*, unavailable, message: "This property is unavailable", renamed: "gridView") 28 | open override var view: ViewAlias! { 29 | get { return super.view } 30 | set { super.view = newValue } 31 | } 32 | 33 | // MARK: View lifecycle 34 | 35 | @available(*, unavailable, message: "This method is unavailable") 36 | open override func loadView() { 37 | // Replacing view with gridView 38 | gridView = GridScrollView(scrollingDirection: scrollingDirection) 39 | super.view = gridView 40 | } 41 | 42 | // MARK: Initialization 43 | 44 | /// Initializer 45 | public init() { 46 | self.scrollingDirection = .vertical 47 | 48 | super.init(nibName: nil, bundle: nil) 49 | } 50 | 51 | /// Initializer (scrolling direction set to `.vertical` by default) 52 | public init(scrollingDirection: GridScrollView.Direction = .vertical) { 53 | self.scrollingDirection = scrollingDirection 54 | 55 | super.init(nibName: nil, bundle: nil) 56 | } 57 | 58 | /// Initializer (scrolling direction set to `.vertical` by default) 59 | public override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { 60 | self.scrollingDirection = .vertical 61 | 62 | super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) 63 | } 64 | 65 | @available(*, unavailable, message: "This method is unavailable") 66 | required public init?(coder aDecoder: NSCoder) { 67 | fatalError("init(coder:) has not been implemented") 68 | } 69 | 70 | } 71 | 72 | #endif 73 | -------------------------------------------------------------------------------- /Classes/View controllers/GridViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GridViewController.swift 3 | // Hagrid 4 | // 5 | // Created by Ondrej Rafaj on 29/05/2018. 6 | // Copyright © 2018 LiveUI. All rights reserved. 7 | // 8 | 9 | #if os(iOS) || os(tvOS) 10 | @_exported import Foundation 11 | @_exported import UIKit 12 | 13 | 14 | /// Grid view view controller 15 | open class GridViewController: UIViewController { 16 | 17 | /** 18 | Grid view 19 | 20 | - important: Replaces default `view` 21 | */ 22 | public internal(set) var gridView: GridView! 23 | 24 | /// Original view (removed) 25 | @available(*, unavailable, message: "This property is unavailable", renamed: "gridView") 26 | open override var view: ViewAlias! { 27 | get { return super.view } 28 | set { super.view = newValue } 29 | } 30 | 31 | // MARK: View lifecycle 32 | 33 | @available(*, unavailable, message: "This method is unavailable") 34 | open override func loadView() { 35 | gridView = GridView() 36 | super.view = gridView 37 | } 38 | 39 | } 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /Classes/Views/GridCollectionReusableView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GridCollectionReusableView.swift 3 | // Hagrid 4 | // 5 | // Created by Ondrej Rafaj on 20/06/2018. 6 | // Copyright © 2018 LiveUI. All rights reserved. 7 | // 8 | 9 | #if os(iOS) || os(tvOS) 10 | @_exported import Foundation 11 | @_exported import UIKit 12 | @_exported import SnapKit 13 | 14 | 15 | /// Grid view enabled collection reusable view 16 | open class GridCollectionReusableView: UICollectionReusableView { 17 | 18 | /// Grid view 19 | public let gridView = GridView() 20 | 21 | // MARK: Initialization 22 | 23 | /// Initializer 24 | override public init(frame rect: CGRect) { 25 | super.init(frame: rect) 26 | 27 | addSubview(gridView) 28 | gridView.snp.makeConstraints { make in 29 | make.edges.equalToSuperview() 30 | } 31 | } 32 | 33 | @available(*, unavailable, message: "This method is unavailable") 34 | required public init?(coder aDecoder: NSCoder) { 35 | fatalError("init(coder:) has not been implemented") 36 | } 37 | 38 | } 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /Classes/Views/GridCollectionViewCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GridCollectionViewCell.swift 3 | // Hagrid 4 | // 5 | // Created by Ondrej Rafaj on 20/06/2018. 6 | // Copyright © 2018 LiveUI. All rights reserved. 7 | // 8 | 9 | #if os(iOS) || os(tvOS) 10 | @_exported import Foundation 11 | @_exported import UIKit 12 | @_exported import SnapKit 13 | 14 | 15 | /// Grid view enabled collection view cell 16 | open class GridCollectionViewCell: UICollectionViewCell { 17 | 18 | /// Grid view 19 | public let gridView = GridView() 20 | 21 | @available(*, unavailable, message: "This method is unavailable", renamed: "gridView") 22 | override open var contentView: UIView { 23 | get { return super.contentView } 24 | } 25 | 26 | // MARK: Initialization 27 | 28 | /// Initializer 29 | override public init(frame rect: CGRect) { 30 | super.init(frame: rect) 31 | 32 | super.contentView.addSubview(gridView) 33 | gridView.snp.makeConstraints { make in 34 | make.edges.equalToSuperview() 35 | } 36 | } 37 | 38 | @available(*, unavailable, message: "This method is unavailable") 39 | required public init?(coder aDecoder: NSCoder) { 40 | fatalError("init(coder:) has not been implemented") 41 | } 42 | 43 | } 44 | 45 | #endif 46 | -------------------------------------------------------------------------------- /Classes/Views/GridScrollView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GridScrollView.swift 3 | // Hagrid 4 | // 5 | // Created by Ondrej Rafaj on 31/05/2018. 6 | // Copyright © 2018 LiveUI. All rights reserved. 7 | // 8 | 9 | #if os(iOS) || os(tvOS) 10 | @_exported import Foundation 11 | @_exported import SnapKit 12 | 13 | 14 | /// GridView enabled scroll view 15 | open class GridScrollView: ViewAlias, GridViewInterface, ScrollViewForwarder { 16 | 17 | /// Scrolling direction 18 | public enum Direction { 19 | 20 | /// Vertical 21 | case vertical 22 | 23 | } 24 | 25 | /// Main scroll view 26 | public let scrollView = UIScrollView() 27 | 28 | /// Main grid view 29 | public let gridView = GridView() 30 | 31 | /// Scrolling direction 32 | public internal(set) var scrollingDirection: GridScrollView.Direction 33 | 34 | /// Grid view configuration 35 | open internal(set) var config: GridView.Config { 36 | get { return gridView.config } 37 | set { gridView.config = newValue } 38 | } 39 | 40 | /// Subviews 41 | public internal(set) var gridSubviews: [Subview] { 42 | get { return gridView.gridSubviews } 43 | set { gridView.gridSubviews = newValue } 44 | } 45 | 46 | // MARK: Initialization 47 | 48 | private func setup(direction: Direction = .vertical) { 49 | super.addSubview(scrollView) 50 | scrollView.snp.makeConstraints { make in 51 | make.edges.equalToSuperview() 52 | } 53 | 54 | switch scrollingDirection { 55 | case .vertical: 56 | gridView.config.automaticVerticalSizing = true 57 | } 58 | scrollView.addSubview(gridView) 59 | gridView.snp.makeConstraints { make in 60 | make.edges.equalToSuperview() 61 | if direction == .vertical { 62 | make.width.equalTo(scrollView) 63 | } 64 | } 65 | } 66 | 67 | /// Init with scrolling direction (.vertical by default) 68 | public init(scrollingDirection: Direction = .vertical) { 69 | self.scrollingDirection = scrollingDirection 70 | super.init(frame: .zero) 71 | setup() 72 | } 73 | 74 | /// Init with frame (defaults to vertical scrolling) 75 | public override init(frame: CGRect) { 76 | scrollingDirection = .vertical 77 | super.init(frame: frame) 78 | setup() 79 | } 80 | 81 | /// Init with coder (defaults to vertical scrolling) 82 | required public init?(coder aDecoder: NSCoder) { 83 | scrollingDirection = .vertical 84 | super.init(coder: aDecoder) 85 | setup() 86 | } 87 | 88 | } 89 | 90 | #endif 91 | -------------------------------------------------------------------------------- /Classes/Views/GridTableViewCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GridTableViewCell.swift 3 | // Hagrid 4 | // 5 | // Created by Ondrej Rafaj on 01/06/2018. 6 | // Copyright © 2018 LiveUI. All rights reserved. 7 | // 8 | 9 | #if os(iOS) || os(tvOS) 10 | @_exported import Foundation 11 | @_exported import UIKit 12 | @_exported import SnapKit 13 | 14 | 15 | /// Grid view enabled table view cell 16 | open class GridTableViewCell: UITableViewCell { 17 | 18 | /// Grid view 19 | public let gridView = GridView() 20 | 21 | @available(*, unavailable, message: "This method is unavailable", renamed: "gridView") 22 | override open var contentView: UIView { 23 | get { return super.contentView } 24 | } 25 | 26 | // MARK: Initialization 27 | 28 | /// Initializer 29 | override public init(style: UITableViewCellStyle, reuseIdentifier: String?) { 30 | super.init(style: style, reuseIdentifier: reuseIdentifier) 31 | 32 | super.contentView.addSubview(gridView) 33 | gridView.snp.makeConstraints { make in 34 | make.edges.equalToSuperview() 35 | } 36 | } 37 | 38 | @available(*, unavailable, message: "This method is unavailable") 39 | required public init?(coder aDecoder: NSCoder) { 40 | fatalError("init(coder:) has not been implemented") 41 | } 42 | 43 | } 44 | 45 | #endif 46 | -------------------------------------------------------------------------------- /Classes/Views/GridTableViewHeaderFooterView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GridTableViewHeaderFooterView.swift 3 | // Hagrid 4 | // 5 | // Created by Ondrej Rafaj on 11/06/2018. 6 | // 7 | 8 | #if os(iOS) || os(tvOS) 9 | @_exported import Foundation 10 | @_exported import UIKit 11 | @_exported import SnapKit 12 | 13 | 14 | open class GridTableViewHeaderFooterView: UITableViewHeaderFooterView { 15 | 16 | /// Grid view 17 | public let gridView = GridView() 18 | 19 | // MARK: Initialization 20 | 21 | /// Initializer 22 | override public init(reuseIdentifier: String?) { 23 | super.init(reuseIdentifier: reuseIdentifier) 24 | 25 | addSubview(gridView) 26 | gridView.snp.makeConstraints { make in 27 | make.edges.equalToSuperview() 28 | } 29 | } 30 | 31 | @available(*, unavailable, message: "This method is unavailable") 32 | required public init?(coder aDecoder: NSCoder) { 33 | fatalError("init(coder:) has not been implemented") 34 | } 35 | 36 | 37 | } 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /Classes/Views/GridView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GridView.swift 3 | // Hagrid 4 | // 5 | // Created by Ondrej Rafaj on 29/05/2018. 6 | // Copyright © 2018 LiveUI. All rights reserved. 7 | // 8 | 9 | @_exported import Foundation 10 | #if os(iOS) || os(tvOS) 11 | @_exported import UIKit 12 | #elseif os(OSX) 13 | @_exported import Cocoa 14 | #endif 15 | 16 | 17 | /// Grid view 18 | open class GridView: ViewAlias, GridViewInterface { 19 | 20 | /// Grid view config object 21 | public final class Config { 22 | 23 | /** 24 | Display grid outlines 25 | 26 | - important: Redraws gridview on set 27 | */ 28 | public var displayGrid: Bool = false { didSet { reDraw() } } 29 | 30 | /** 31 | Number of columns 32 | 33 | - important: Redraws gridview on set 34 | */ 35 | public var numberOfColumns: Int = 12 { didSet { reLayout() } } 36 | 37 | /** 38 | Column width 39 | 40 | - important: Will be 0 until grid view is drawn for the first time 41 | */ 42 | public internal(set) var columnWidth: CGFloat = 0 43 | 44 | /** 45 | Grid view padding 46 | 47 | - important: Bottom padding not implemented 48 | */ 49 | public var padding: Padding = .none { 50 | didSet { 51 | reLayout() 52 | } 53 | } 54 | 55 | /** 56 | View is dynamically extending vertically with each added element 57 | */ 58 | public var automaticVerticalSizing: Bool = false { 59 | didSet { 60 | reLayout() 61 | } 62 | } 63 | 64 | // MARK: Private interface 65 | 66 | /// Holds reference to the grid view 67 | let element: GridView 68 | 69 | /// Initializer 70 | init(_ element: GridView) { 71 | self.element = element 72 | } 73 | 74 | /// Re-draw the element 75 | func reDraw() { 76 | #if os(iOS) || os(tvOS) 77 | element.setNeedsDisplay() 78 | #elseif os(OSX) 79 | element.setNeedsDisplay(element.bounds) 80 | #endif 81 | } 82 | 83 | /// Re-layout 84 | private func reLayout() { 85 | if element.superview != nil { 86 | #if os(iOS) || os(tvOS) 87 | element.setNeedsLayout() 88 | element.layoutIfNeeded() 89 | #elseif os(OSX) 90 | element.needsLayout = true 91 | element.macLayout() 92 | #endif 93 | } 94 | } 95 | 96 | } 97 | 98 | /// Grid view configuration 99 | open internal(set) lazy var config: Config = { 100 | return Config(self) 101 | }() 102 | 103 | /// Subviews 104 | public internal(set) var gridSubviews: [Subview] = [] 105 | 106 | // MARK: Initialization 107 | 108 | /// Initializer 109 | public override init(frame: CGRect) { 110 | super.init(frame: frame) 111 | 112 | setup() 113 | } 114 | 115 | /// Initializer 116 | required public init?(coder aDecoder: NSCoder) { 117 | super.init(coder: aDecoder) 118 | 119 | setup() 120 | } 121 | 122 | } 123 | 124 | // MARK: Initialization 125 | 126 | extension GridView { 127 | 128 | func setup() { 129 | #if os(iOS) || os(tvOS) 130 | backgroundColor = .clear 131 | #endif 132 | } 133 | 134 | /// Initializer 135 | public convenience init() { 136 | self.init(frame: CGRect.zero) 137 | } 138 | 139 | } 140 | 141 | -------------------------------------------------------------------------------- /Hagrid Demo/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // Hagrid 4 | // 5 | // Created by Ondrej Rafaj on 29/05/2018. 6 | // Copyright © 2018 LiveUI. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 18 | window = UIWindow(frame: UIScreen.main.bounds) 19 | if let window = window { 20 | window.backgroundColor = .white 21 | window.rootViewController = UINavigationController(rootViewController: ViewController()) 22 | window.makeKeyAndVisible() 23 | } 24 | UIApplication.shared.statusBarStyle = .lightContent 25 | return true 26 | } 27 | 28 | func applicationWillResignActive(_ application: UIApplication) { 29 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 30 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 31 | } 32 | 33 | func applicationDidEnterBackground(_ application: UIApplication) { 34 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 35 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 36 | } 37 | 38 | func applicationWillEnterForeground(_ application: UIApplication) { 39 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 40 | } 41 | 42 | func applicationDidBecomeActive(_ application: UIApplication) { 43 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 44 | } 45 | 46 | func applicationWillTerminate(_ application: UIApplication) { 47 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 48 | } 49 | 50 | 51 | } 52 | 53 | -------------------------------------------------------------------------------- /Hagrid Demo/Extensions/UIColor+Tools.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIColor+Tools.swift 3 | // Hagrid 4 | // 5 | // Created by Ondrej Rafaj on 29/05/2018. 6 | // Copyright © 2018 LiveUI. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | 13 | extension UIColor { 14 | 15 | /// Random color 16 | static var random: UIColor { 17 | let hue : CGFloat = CGFloat(arc4random() % 256) / 256 18 | let saturation : CGFloat = CGFloat(arc4random() % 128) / 256 + 0.5 19 | let brightness : CGFloat = CGFloat(arc4random() % 128) / 256 + 0.5 20 | return UIColor(hue: hue, saturation: saturation, brightness: brightness, alpha: 1) 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /Hagrid Demo/Supporting files/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-Notification@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-Notification@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-Small@2x.png", 19 | "scale" : "2x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-Small@3x.png", 25 | "scale" : "3x" 26 | }, 27 | { 28 | "size" : "40x40", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-Small-40@2x.png", 31 | "scale" : "2x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-Small-40@3x.png", 37 | "scale" : "3x" 38 | }, 39 | { 40 | "size" : "60x60", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-60@2x.png", 43 | "scale" : "2x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-60@3x.png", 49 | "scale" : "3x" 50 | }, 51 | { 52 | "size" : "20x20", 53 | "idiom" : "ipad", 54 | "filename" : "Icon-Notification.png", 55 | "scale" : "1x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-Notification@2x.png", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "size" : "29x29", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-Small.png", 67 | "scale" : "1x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-Small@2x.png", 73 | "scale" : "2x" 74 | }, 75 | { 76 | "size" : "40x40", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-Small-40.png", 79 | "scale" : "1x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-Small-40@2x.png", 85 | "scale" : "2x" 86 | }, 87 | { 88 | "size" : "76x76", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-76.png", 91 | "scale" : "1x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-76@2x.png", 97 | "scale" : "2x" 98 | }, 99 | { 100 | "size" : "83.5x83.5", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-83.5@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "1024x1024", 107 | "idiom" : "ios-marketing", 108 | "filename" : "Icon1024.png", 109 | "scale" : "1x" 110 | } 111 | ], 112 | "info" : { 113 | "version" : 1, 114 | "author" : "xcode" 115 | } 116 | } -------------------------------------------------------------------------------- /Hagrid Demo/Supporting files/Assets.xcassets/AppIcon.appiconset/Icon-60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LiveUI/Hagrid/d084df699a03a3880f47ca17d856e59b423dac71/Hagrid Demo/Supporting files/Assets.xcassets/AppIcon.appiconset/Icon-60@2x.png -------------------------------------------------------------------------------- /Hagrid Demo/Supporting files/Assets.xcassets/AppIcon.appiconset/Icon-60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LiveUI/Hagrid/d084df699a03a3880f47ca17d856e59b423dac71/Hagrid Demo/Supporting files/Assets.xcassets/AppIcon.appiconset/Icon-60@3x.png -------------------------------------------------------------------------------- /Hagrid Demo/Supporting files/Assets.xcassets/AppIcon.appiconset/Icon-76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LiveUI/Hagrid/d084df699a03a3880f47ca17d856e59b423dac71/Hagrid Demo/Supporting files/Assets.xcassets/AppIcon.appiconset/Icon-76.png -------------------------------------------------------------------------------- /Hagrid Demo/Supporting files/Assets.xcassets/AppIcon.appiconset/Icon-76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LiveUI/Hagrid/d084df699a03a3880f47ca17d856e59b423dac71/Hagrid Demo/Supporting files/Assets.xcassets/AppIcon.appiconset/Icon-76@2x.png -------------------------------------------------------------------------------- /Hagrid Demo/Supporting files/Assets.xcassets/AppIcon.appiconset/Icon-83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LiveUI/Hagrid/d084df699a03a3880f47ca17d856e59b423dac71/Hagrid Demo/Supporting files/Assets.xcassets/AppIcon.appiconset/Icon-83.5@2x.png -------------------------------------------------------------------------------- /Hagrid Demo/Supporting files/Assets.xcassets/AppIcon.appiconset/Icon-Notification.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LiveUI/Hagrid/d084df699a03a3880f47ca17d856e59b423dac71/Hagrid Demo/Supporting files/Assets.xcassets/AppIcon.appiconset/Icon-Notification.png -------------------------------------------------------------------------------- /Hagrid Demo/Supporting files/Assets.xcassets/AppIcon.appiconset/Icon-Notification@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LiveUI/Hagrid/d084df699a03a3880f47ca17d856e59b423dac71/Hagrid Demo/Supporting files/Assets.xcassets/AppIcon.appiconset/Icon-Notification@2x.png -------------------------------------------------------------------------------- /Hagrid Demo/Supporting files/Assets.xcassets/AppIcon.appiconset/Icon-Notification@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LiveUI/Hagrid/d084df699a03a3880f47ca17d856e59b423dac71/Hagrid Demo/Supporting files/Assets.xcassets/AppIcon.appiconset/Icon-Notification@3x.png -------------------------------------------------------------------------------- /Hagrid Demo/Supporting files/Assets.xcassets/AppIcon.appiconset/Icon-Small-40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LiveUI/Hagrid/d084df699a03a3880f47ca17d856e59b423dac71/Hagrid Demo/Supporting files/Assets.xcassets/AppIcon.appiconset/Icon-Small-40.png -------------------------------------------------------------------------------- /Hagrid Demo/Supporting files/Assets.xcassets/AppIcon.appiconset/Icon-Small-40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LiveUI/Hagrid/d084df699a03a3880f47ca17d856e59b423dac71/Hagrid Demo/Supporting files/Assets.xcassets/AppIcon.appiconset/Icon-Small-40@2x.png -------------------------------------------------------------------------------- /Hagrid Demo/Supporting files/Assets.xcassets/AppIcon.appiconset/Icon-Small-40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LiveUI/Hagrid/d084df699a03a3880f47ca17d856e59b423dac71/Hagrid Demo/Supporting files/Assets.xcassets/AppIcon.appiconset/Icon-Small-40@3x.png -------------------------------------------------------------------------------- /Hagrid Demo/Supporting files/Assets.xcassets/AppIcon.appiconset/Icon-Small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LiveUI/Hagrid/d084df699a03a3880f47ca17d856e59b423dac71/Hagrid Demo/Supporting files/Assets.xcassets/AppIcon.appiconset/Icon-Small.png -------------------------------------------------------------------------------- /Hagrid Demo/Supporting files/Assets.xcassets/AppIcon.appiconset/Icon-Small@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LiveUI/Hagrid/d084df699a03a3880f47ca17d856e59b423dac71/Hagrid Demo/Supporting files/Assets.xcassets/AppIcon.appiconset/Icon-Small@2x.png -------------------------------------------------------------------------------- /Hagrid Demo/Supporting files/Assets.xcassets/AppIcon.appiconset/Icon-Small@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LiveUI/Hagrid/d084df699a03a3880f47ca17d856e59b423dac71/Hagrid Demo/Supporting files/Assets.xcassets/AppIcon.appiconset/Icon-Small@3x.png -------------------------------------------------------------------------------- /Hagrid Demo/Supporting files/Assets.xcassets/AppIcon.appiconset/Icon1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LiveUI/Hagrid/d084df699a03a3880f47ca17d856e59b423dac71/Hagrid Demo/Supporting files/Assets.xcassets/AppIcon.appiconset/Icon1024.png -------------------------------------------------------------------------------- /Hagrid Demo/Supporting files/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Hagrid Demo/Supporting files/Assets.xcassets/TestData/AlbumCover.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Pearl-Jam-Ten.jpg" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /Hagrid Demo/Supporting files/Assets.xcassets/TestData/AlbumCover.imageset/Pearl-Jam-Ten.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LiveUI/Hagrid/d084df699a03a3880f47ca17d856e59b423dac71/Hagrid Demo/Supporting files/Assets.xcassets/TestData/AlbumCover.imageset/Pearl-Jam-Ten.jpg -------------------------------------------------------------------------------- /Hagrid Demo/Supporting files/Assets.xcassets/TestData/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | }, 6 | "properties" : { 7 | "provides-namespace" : true 8 | } 9 | } -------------------------------------------------------------------------------- /Hagrid Demo/Supporting files/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 | -------------------------------------------------------------------------------- /Hagrid Demo/Supporting files/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleDisplayName 8 | Hagrid 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 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 | -------------------------------------------------------------------------------- /Hagrid Demo/View controllers/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // Hagrid 4 | // 5 | // Created by Ondrej Rafaj on 29/05/2018. 6 | // Copyright © 2018 LiveUI. All rights reserved. 7 | // 8 | 9 | import Hagrid 10 | 11 | 12 | class ViewController: GridScrollViewController { 13 | 14 | // MARK: Example app 15 | 16 | let albumCover = UIImageView() 17 | 18 | let albumTitleLabel = UILabel() 19 | let artistLabel = UILabel() 20 | let yearLabel = UILabel() 21 | let purchaseButton = UIButton() 22 | let ratingLabel = UILabel() 23 | 24 | let separator = UIView() 25 | 26 | let aboutLabel = UILabel() 27 | 28 | /// Setup layout for all elements 29 | func setupLayout() { 30 | // Add an album cover 31 | gridView.add(subview: albumCover, space: 5) { make in 32 | make.height.equalTo(self.albumCover.snp.width) 33 | } 34 | 35 | gridView.add(subview: albumTitleLabel, from: .relation(albumCover), space: .last, padding: .left(12)) 36 | 37 | let subtitlesLast: Position = .custom { _ in 38 | return self.gridView.bounds.size.width <= 414 ? .last : .reversed(2) 39 | } 40 | 41 | gridView.add(subview: artistLabel, .below(albumTitleLabel, margin: 2), from: .match(albumTitleLabel), space: subtitlesLast) 42 | 43 | gridView.add(subview: yearLabel, .below(artistLabel, margin: 2), from: .match(albumTitleLabel), space: subtitlesLast) 44 | 45 | gridView.add(subview: purchaseButton, .custom({ _ in 46 | if self.gridView.bounds.size.width <= 414 { 47 | return .below(self.yearLabel, margin: 3) 48 | } else { 49 | return .match(self.artistLabel) 50 | } 51 | }), from: .custom({ _ in 52 | if self.gridView.bounds.size.width <= 414 { 53 | return .relation(self.albumCover, margin: 12) 54 | } else { 55 | return .relation(self.artistLabel, margin: 6) 56 | } 57 | }), space: .last) { make in 58 | make.height.equalTo(28) 59 | } 60 | 61 | gridView.add(subview: ratingLabel, .above(separator, margin: 12), from: .dynamic, space: .last) { make in 62 | make.height.equalTo(28) 63 | } 64 | 65 | gridView.add(subview: separator, .below([albumCover, yearLabel, purchaseButton], margin: 12)) { make in 66 | make.height.equalTo(1) 67 | } 68 | 69 | gridView.add(subview: aboutLabel, .below(separator, margin: 12)) 70 | } 71 | 72 | /// Create album cover 73 | func configureAlbumCover() { 74 | albumCover.backgroundColor = .random 75 | albumCover.image = UIImage(named: "TestData/AlbumCover") 76 | albumCover.contentMode = .scaleAspectFill 77 | albumCover.clipsToBounds = true 78 | albumCover.layer.cornerRadius = 3 79 | } 80 | 81 | /// Create album labels 82 | func configureAlbumLabels() { 83 | albumTitleLabel.font = UIFont.boldSystemFont(ofSize: 18) 84 | albumTitleLabel.text = "Ten" 85 | albumTitleLabel.textColor = .darkText 86 | 87 | artistLabel.font = UIFont.systemFont(ofSize: 15) 88 | artistLabel.text = "Pearl Jam" 89 | artistLabel.textColor = .gray 90 | 91 | yearLabel.font = UIFont.systemFont(ofSize: 12) 92 | yearLabel.text = "Released: 1991" 93 | yearLabel.textColor = .gray 94 | 95 | ratingLabel.font = UIFont.boldSystemFont(ofSize: 34) 96 | ratingLabel.text = "* * * * *" 97 | ratingLabel.textColor = .orange 98 | 99 | separator.backgroundColor = UIColor.lightGray.withAlphaComponent(0.3) 100 | 101 | aboutLabel.font = UIFont.systemFont(ofSize: 12) 102 | aboutLabel.numberOfLines = 0 103 | aboutLabel.text = """ 104 | Ten is the debut studio album by American rock band Pearl Jam, released on August 27, 1991 through Epic Records. Following the disbanding of bassist Jeff Ament and guitarist Stone Gossard's previous group Mother Love Bone, the two recruited vocalist Eddie Vedder, guitarist Mike McCready, and drummer Dave Krusen to form Pearl Jam in 1990. Most of the songs began as instrumental jams, to which Vedder added lyrics about topics such as depression, homelessness, and abuse. 105 | 106 | Ten was not an immediate success, but by late 1992 it had reached number two on the Billboard 200 chart. The album produced three hit singles: "Alive", "Even Flow", and "Jeremy". While Pearl Jam was accused of jumping on the grunge bandwagon at the time—despite the fact that Ten had been both recorded and released before Nirvana's Nevermind — Ten was instrumental in popularizing alternative rock in the mainstream. In February 2013, the album crossed the 10 million mark in sales, becoming the 22nd one to do so in the Nielsen SoundScan era and has been certified 13× platinum by the RIAA. It remains Pearl Jam's most commercially successful album. 107 | 108 | Pearl Jam: 109 | Pearl Jam is an American rock band formed in Seattle, Washington, in 1990. Since its inception, the band's line-up has consisted of Eddie Vedder (lead vocals), Mike McCready (lead guitar), Stone Gossard (rhythm guitar) and Jeff Ament (bass). The band's fifth member is drummer Matt Cameron (also of Soundgarden), who has been with the band since 1998. Boom Gaspar (piano) has also been a session/touring member with the band since 2002. Drummers Dave Krusen, Matt Chamberlain, Dave Abbruzzese and Jack Irons are former members of the band. 110 | 111 | Formed after the demise of Gossard and Ament's previous band, Mother Love Bone, Pearl Jam broke into the mainstream with its debut album, Ten, in 1991. One of the key bands in the grunge movement of the early 1990s, over the course of the band's career, its members became noted for their refusal to adhere to traditional music industry practices, including refusing to make proper music videos or give interviews, and engaging in a much-publicized boycott of Ticketmaster. In 2006, Rolling Stone described the band as having "spent much of the past decade deliberately tearing apart their own fame."[1] 112 | 113 | To date, the band has sold nearly 32 million records in the United States[2] and an estimated 60 million worldwide.[3][4] Pearl Jam has outlasted and outsold many of its contemporaries from the alternative rock breakthrough of the early 1990s, and is considered one of the most influential bands of that decade.[5] Stephen Thomas Erlewine of AllMusic referred to Pearl Jam as "the most popular American rock & roll band of the '90s".[6] Pearl Jam was inducted into the Rock and Roll Hall of Fame on April 7, 2017, in its first year of eligibility. 114 | """ 115 | aboutLabel.textColor = .darkText 116 | } 117 | 118 | /// Configure purchase button 119 | func configurePurchaseButton() { 120 | purchaseButton.layer.borderWidth = 1 121 | purchaseButton.layer.borderColor = purchaseButton.tintColor.cgColor 122 | purchaseButton.layer.cornerRadius = 4 123 | purchaseButton.setTitle("Buy £", for: .normal) 124 | purchaseButton.setTitleColor(purchaseButton.tintColor, for: .normal) 125 | purchaseButton.setTitleColor(.lightGray, for: .highlighted) 126 | purchaseButton.titleLabel?.font = UIFont.boldSystemFont(ofSize: 12) 127 | } 128 | 129 | /// Add all subviews on the canvas 130 | func addSubviews() { 131 | configureAlbumCover() 132 | configureAlbumLabels() 133 | configurePurchaseButton() 134 | 135 | setupLayout() 136 | } 137 | 138 | // MARK: Other interface 139 | 140 | override func viewDidLoad() { 141 | super.viewDidLoad() 142 | 143 | setupNavBar() 144 | setupGridView() 145 | 146 | navigationController?.navigationBar.isTranslucent = false 147 | gridView.backgroundColor = .white 148 | } 149 | 150 | func setupNavBar() { 151 | title = "Hagrid" 152 | 153 | let columns = UISegmentedControl(items: ["12", "15", "18"]) 154 | columns.addTarget(self, action: #selector(selectColumns(_:)), for: .valueChanged) 155 | columns.selectedSegmentIndex = 0 156 | let columnsItem = UIBarButtonItem(customView: columns) 157 | navigationItem.leftBarButtonItem = columnsItem 158 | 159 | let toggle = UIBarButtonItem(title: "Debug", style: .plain, target: self, action: #selector(toggleGrid)) 160 | navigationItem.rightBarButtonItem = toggle 161 | } 162 | 163 | func setupGridView() { 164 | toggleGrid() 165 | 166 | gridView.config.padding = .full(top: 6, left: 12, right: 12) 167 | 168 | addSubviews() 169 | } 170 | 171 | // MARK: Actions 172 | 173 | @objc func selectColumns(_ sender: UISegmentedControl) { 174 | UIView.animate(withDuration: 0.3, delay: 0.0, options: .beginFromCurrentState, animations: { 175 | if sender.selectedSegmentIndex == 0 { 176 | self.gridView.config.numberOfColumns = 12 177 | } else if sender.selectedSegmentIndex == 1 { 178 | self.gridView.config.numberOfColumns = 15 179 | } else { 180 | self.gridView.config.numberOfColumns = 18 181 | } 182 | }) 183 | } 184 | 185 | @objc func toggleGrid() { 186 | gridView.config.displayGrid = !gridView.config.displayGrid 187 | 188 | let debugColor = UIColor.lightGray.withAlphaComponent(0.3) 189 | 190 | albumTitleLabel.backgroundColor = gridView.config.displayGrid ? debugColor : .clear 191 | artistLabel.backgroundColor = gridView.config.displayGrid ? debugColor : .clear 192 | yearLabel.backgroundColor = gridView.config.displayGrid ? debugColor : .clear 193 | ratingLabel.backgroundColor = gridView.config.displayGrid ? debugColor : .clear 194 | } 195 | 196 | } 197 | -------------------------------------------------------------------------------- /Hagrid-iOS/Supporting files/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSPrincipalClass 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /Hagrid-iOS/Supporting files/TheGrid.h: -------------------------------------------------------------------------------- 1 | // 2 | // Hagrid.h 3 | // Hagrid 4 | // 5 | // Created by Ondrej Rafaj on 29/05/2018. 6 | // Copyright © 2018 LiveUI. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for TheGrid. 12 | FOUNDATION_EXPORT double TheGridVersionNumber; 13 | 14 | //! Project version string for TheGrid. 15 | FOUNDATION_EXPORT const unsigned char TheGridVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | 20 | -------------------------------------------------------------------------------- /Hagrid-macOS/Supporting files/Hagrid_macOS.h: -------------------------------------------------------------------------------- 1 | // 2 | // Hagrid_macOS.h 3 | // Hagrid-macOS 4 | // 5 | // Created by Ondrej Rafaj on 31/05/2018. 6 | // Copyright © 2018 LiveUI. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for Hagrid_macOS. 12 | FOUNDATION_EXPORT double Hagrid_macOSVersionNumber; 13 | 14 | //! Project version string for Hagrid_macOS. 15 | FOUNDATION_EXPORT const unsigned char Hagrid_macOSVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | 20 | -------------------------------------------------------------------------------- /Hagrid-macOS/Supporting files/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSHumanReadableCopyright 22 | Copyright © 2018 LiveUI. All rights reserved. 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Hagrid-tvOS/Supporting files/Hagrid_appleTV.h: -------------------------------------------------------------------------------- 1 | // 2 | // Hagrid_appleTV.h 3 | // Hagrid-appleTV 4 | // 5 | // Created by Ondrej Rafaj on 31/05/2018. 6 | // Copyright © 2018 LiveUI. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for Hagrid_appleTV. 12 | FOUNDATION_EXPORT double Hagrid_appleTVVersionNumber; 13 | 14 | //! Project version string for Hagrid_appleTV. 15 | FOUNDATION_EXPORT const unsigned char Hagrid_appleTVVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | 20 | -------------------------------------------------------------------------------- /Hagrid-tvOS/Supporting files/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSPrincipalClass 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /Hagrid.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # Be sure to run `pod lib lint Reloaded.podspec' to ensure this is a 3 | # valid spec before submitting. 4 | # 5 | # Any lines starting with a # are optional, but their use is encouraged 6 | # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html 7 | # 8 | 9 | Pod::Spec.new do |s| 10 | s.name = 'Hagrid' 11 | s.version = '1.0.2' 12 | s.summary = 'Brings proper grid layout to the Apple platforms!' 13 | s.swift_version = '4.1' 14 | 15 | # This description is used to generate tags and improve search results. 16 | # * Think: What does it do? Why did you write it? What is the focus? 17 | # * Try to keep it short, snappy and to the point. 18 | # * Write the description between the DESC delimiters below. 19 | # * Finally, don't worry about the indent, CocoaPods strips it! 20 | 21 | s.description = <<-DESC 22 | Hagrid is a simple to use grid layout engine based on SnapKit. Written purely in swift and with a great amount of love! 23 | DESC 24 | 25 | s.homepage = 'https://github.com/LiveUI/Hagrid' 26 | # s.screenshots = 'www.example.com/screenshots_1', 'www.example.com/screenshots_2' 27 | s.license = { :type => 'MIT', :file => 'LICENSE' } 28 | s.author = { 'rafiki270' => 'dev@liveui.io' } 29 | s.source = { :git => 'https://github.com/LiveUI/Hagrid.git', :tag => s.version.to_s } 30 | # s.social_media_url = 'https://twitter.com/rafiki270' 31 | 32 | s.ios.deployment_target = '9.3' 33 | s.tvos.deployment_target = '10.2' 34 | s.macos.deployment_target = '10.12' 35 | 36 | s.source_files = 'Classes/**/*' 37 | 38 | # s.public_header_files = 'Pod/Classes/**/*.h' 39 | s.dependency 'SnapKit', '~> 4.0.0' 40 | end 41 | -------------------------------------------------------------------------------- /Hagrid.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 15020A9B20C0A46700BBADBD /* Hagrid.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 151640EA20BD7A8C0062FA33 /* Hagrid.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 11 | 15020A9E20C0A5A200BBADBD /* Hagrid.podspec in Resources */ = {isa = PBXBuildFile; fileRef = 15020A9D20C0A5A200BBADBD /* Hagrid.podspec */; }; 12 | 15020AA820C0A65200BBADBD /* Hagrid_appleTV.h in Headers */ = {isa = PBXBuildFile; fileRef = 15020AA620C0A65200BBADBD /* Hagrid_appleTV.h */; settings = {ATTRIBUTES = (Public, ); }; }; 13 | 15020AAE20C0A6B500BBADBD /* GridView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1516412120BD7D760062FA33 /* GridView.swift */; }; 14 | 15020AAF20C0A6B500BBADBD /* GridScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 153F9D8420C027B70051D0D3 /* GridScrollView.swift */; }; 15 | 15020AB020C0A6B500BBADBD /* GridScrollViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 153F9D8220C023610051D0D3 /* GridScrollViewController.swift */; }; 16 | 15020AB120C0A6B500BBADBD /* GridViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 153F9D7420BE02D70051D0D3 /* GridViewController.swift */; }; 17 | 15020AB220C0A6B500BBADBD /* UIColor+Tools.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15D00E7920BDB8C20051A8CF /* UIColor+Tools.swift */; }; 18 | 15020AB320C0A6B500BBADBD /* GridView+Drawing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15D00E7B20BDBE8A0051A8CF /* GridView+Drawing.swift */; }; 19 | 15020AB420C0A6B500BBADBD /* GridView+Calculations.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15D00E7D20BDBECF0051A8CF /* GridView+Calculations.swift */; }; 20 | 15020AB520C0A6B500BBADBD /* GridView+Subviews.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15D00E8520BDC84A0051A8CF /* GridView+Subviews.swift */; }; 21 | 15020AB620C0A6B500BBADBD /* GridSrollView+Subviews.swift in Sources */ = {isa = PBXBuildFile; fileRef = 153F9D8920C02B440051D0D3 /* GridSrollView+Subviews.swift */; }; 22 | 15020AB720C0A6B500BBADBD /* GridView+Layout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15D00E9420BDDE280051A8CF /* GridView+Layout.swift */; }; 23 | 15020AB820C0A6B500BBADBD /* Vertical.swift in Sources */ = {isa = PBXBuildFile; fileRef = 153F9D7620BE1ACE0051D0D3 /* Vertical.swift */; }; 24 | 15020AB920C0A6B500BBADBD /* Position.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15D00E8020BDC3B80051A8CF /* Position.swift */; }; 25 | 15020ABA20C0A6B500BBADBD /* Padding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15D00E8E20BDD0690051A8CF /* Padding.swift */; }; 26 | 15020ABB20C0A6B500BBADBD /* Properties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15D00E9020BDD1930051A8CF /* Properties.swift */; }; 27 | 15020ABC20C0A6B500BBADBD /* Subview.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15D00E9220BDD2250051A8CF /* Subview.swift */; }; 28 | 15020AC820C0A9A300BBADBD /* Hagrid_macOS.h in Headers */ = {isa = PBXBuildFile; fileRef = 15020AC620C0A9A300BBADBD /* Hagrid_macOS.h */; settings = {ATTRIBUTES = (Public, ); }; }; 29 | 15020ACD20C0A9F800BBADBD /* Properties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15D00E9020BDD1930051A8CF /* Properties.swift */; }; 30 | 15020ACE20C0A9F800BBADBD /* Subview.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15D00E9220BDD2250051A8CF /* Subview.swift */; }; 31 | 15020AD020C0AA8100BBADBD /* Compatibility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15020ACF20C0AA8100BBADBD /* Compatibility.swift */; }; 32 | 15020AD120C0AA8100BBADBD /* Compatibility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15020ACF20C0AA8100BBADBD /* Compatibility.swift */; }; 33 | 15020AD220C0AA8100BBADBD /* Compatibility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15020ACF20C0AA8100BBADBD /* Compatibility.swift */; }; 34 | 15020AD320C0AB7500BBADBD /* Vertical.swift in Sources */ = {isa = PBXBuildFile; fileRef = 153F9D7620BE1ACE0051D0D3 /* Vertical.swift */; }; 35 | 15020AD420C0AB7500BBADBD /* Position.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15D00E8020BDC3B80051A8CF /* Position.swift */; }; 36 | 15020AD520C0AB7500BBADBD /* Padding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15D00E8E20BDD0690051A8CF /* Padding.swift */; }; 37 | 15020AD620C0AC8D00BBADBD /* GridView+Calculations.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15D00E7D20BDBECF0051A8CF /* GridView+Calculations.swift */; }; 38 | 15020AD720C0AC8D00BBADBD /* GridView+Subviews.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15D00E8520BDC84A0051A8CF /* GridView+Subviews.swift */; }; 39 | 15020AD920C0AC8D00BBADBD /* GridView+Layout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15D00E9420BDDE280051A8CF /* GridView+Layout.swift */; }; 40 | 15020ADA20C0AC9F00BBADBD /* GridView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1516412120BD7D760062FA33 /* GridView.swift */; }; 41 | 15020ADD20C0AF7000BBADBD /* Cartfile in Resources */ = {isa = PBXBuildFile; fileRef = 15020ADC20C0AF6F00BBADBD /* Cartfile */; }; 42 | 15020ADE20C0AF7000BBADBD /* Cartfile in Resources */ = {isa = PBXBuildFile; fileRef = 15020ADC20C0AF6F00BBADBD /* Cartfile */; }; 43 | 15020ADF20C0AF7000BBADBD /* Cartfile in Resources */ = {isa = PBXBuildFile; fileRef = 15020ADC20C0AF6F00BBADBD /* Cartfile */; }; 44 | 15020AE020C0BF6700BBADBD /* GridView+Drawing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15D00E7B20BDBE8A0051A8CF /* GridView+Drawing.swift */; }; 45 | 15020AE120C0BF6B00BBADBD /* UIColor+Tools.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15D00E7920BDB8C20051A8CF /* UIColor+Tools.swift */; }; 46 | 15020AE320C0BFAC00BBADBD /* GridScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 153F9D8420C027B70051D0D3 /* GridScrollView.swift */; }; 47 | 15020AE420C0BFB800BBADBD /* GridScrollViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 153F9D8220C023610051D0D3 /* GridScrollViewController.swift */; }; 48 | 15020AE520C0BFEA00BBADBD /* GridViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 153F9D7420BE02D70051D0D3 /* GridViewController.swift */; }; 49 | 15020AE620C0BFEA00BBADBD /* GridSrollView+Subviews.swift in Sources */ = {isa = PBXBuildFile; fileRef = 153F9D8920C02B440051D0D3 /* GridSrollView+Subviews.swift */; }; 50 | 15020AE820C147F600BBADBD /* GridTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15020AE720C147F600BBADBD /* GridTableViewCell.swift */; }; 51 | 15020AE920C147F600BBADBD /* GridTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15020AE720C147F600BBADBD /* GridTableViewCell.swift */; }; 52 | 15020AEA20C147F600BBADBD /* GridTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15020AE720C147F600BBADBD /* GridTableViewCell.swift */; }; 53 | 151640FB20BD7A8C0062FA33 /* TheGrid.h in Headers */ = {isa = PBXBuildFile; fileRef = 151640ED20BD7A8C0062FA33 /* TheGrid.h */; settings = {ATTRIBUTES = (Public, ); }; }; 54 | 1516411020BD7B740062FA33 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1516410F20BD7B740062FA33 /* AppDelegate.swift */; }; 55 | 1516411220BD7B740062FA33 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1516411120BD7B740062FA33 /* ViewController.swift */; }; 56 | 1516411720BD7B750062FA33 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 1516411620BD7B750062FA33 /* Assets.xcassets */; }; 57 | 1516411A20BD7B750062FA33 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 1516411820BD7B750062FA33 /* LaunchScreen.storyboard */; }; 58 | 1516412220BD7D760062FA33 /* GridView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1516412120BD7D760062FA33 /* GridView.swift */; }; 59 | 153F9D7520BE02D70051D0D3 /* GridViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 153F9D7420BE02D70051D0D3 /* GridViewController.swift */; }; 60 | 153F9D7720BE1ACE0051D0D3 /* Vertical.swift in Sources */ = {isa = PBXBuildFile; fileRef = 153F9D7620BE1ACE0051D0D3 /* Vertical.swift */; }; 61 | 153F9D8320C023610051D0D3 /* GridScrollViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 153F9D8220C023610051D0D3 /* GridScrollViewController.swift */; }; 62 | 153F9D8520C027B70051D0D3 /* GridScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 153F9D8420C027B70051D0D3 /* GridScrollView.swift */; }; 63 | 153F9D8A20C02B440051D0D3 /* GridSrollView+Subviews.swift in Sources */ = {isa = PBXBuildFile; fileRef = 153F9D8920C02B440051D0D3 /* GridSrollView+Subviews.swift */; }; 64 | 157D35DA20DAA1D3005C01D1 /* SnapKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 157D35D920DAA1D2005C01D1 /* SnapKit.framework */; }; 65 | 157D35DC20DAA1E6005C01D1 /* SnapKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 157D35DB20DAA1E6005C01D1 /* SnapKit.framework */; }; 66 | 157D35DE20DAA1F3005C01D1 /* SnapKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 157D35DD20DAA1F3005C01D1 /* SnapKit.framework */; }; 67 | 157D35E020DAAB1C005C01D1 /* SnapKit.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 157D35D920DAA1D2005C01D1 /* SnapKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 68 | 157D35E420DAB115005C01D1 /* GridCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 157D35E320DAB115005C01D1 /* GridCollectionViewCell.swift */; }; 69 | 157D35E520DAB115005C01D1 /* GridCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 157D35E320DAB115005C01D1 /* GridCollectionViewCell.swift */; }; 70 | 157D35E720DAB129005C01D1 /* GridTableViewHeaderFooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 157D35E620DAB129005C01D1 /* GridTableViewHeaderFooterView.swift */; }; 71 | 157D35E820DAB129005C01D1 /* GridTableViewHeaderFooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 157D35E620DAB129005C01D1 /* GridTableViewHeaderFooterView.swift */; }; 72 | 157D35EA20DAB1AB005C01D1 /* GridCollectionReusableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 157D35E920DAB1AB005C01D1 /* GridCollectionReusableView.swift */; }; 73 | 157D35EB20DAB1AB005C01D1 /* GridCollectionReusableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 157D35E920DAB1AB005C01D1 /* GridCollectionReusableView.swift */; }; 74 | 15D00E7A20BDB8C20051A8CF /* UIColor+Tools.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15D00E7920BDB8C20051A8CF /* UIColor+Tools.swift */; }; 75 | 15D00E7C20BDBE8A0051A8CF /* GridView+Drawing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15D00E7B20BDBE8A0051A8CF /* GridView+Drawing.swift */; }; 76 | 15D00E7E20BDBECF0051A8CF /* GridView+Calculations.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15D00E7D20BDBECF0051A8CF /* GridView+Calculations.swift */; }; 77 | 15D00E8120BDC3B80051A8CF /* Position.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15D00E8020BDC3B80051A8CF /* Position.swift */; }; 78 | 15D00E8420BDC49F0051A8CF /* UIColor+Tools.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15D00E8320BDC49F0051A8CF /* UIColor+Tools.swift */; }; 79 | 15D00E8620BDC84A0051A8CF /* GridView+Subviews.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15D00E8520BDC84A0051A8CF /* GridView+Subviews.swift */; }; 80 | 15D00E8F20BDD0690051A8CF /* Padding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15D00E8E20BDD0690051A8CF /* Padding.swift */; }; 81 | 15D00E9120BDD1930051A8CF /* Properties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15D00E9020BDD1930051A8CF /* Properties.swift */; }; 82 | 15D00E9320BDD2250051A8CF /* Subview.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15D00E9220BDD2250051A8CF /* Subview.swift */; }; 83 | 15D00E9520BDDE280051A8CF /* GridView+Layout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15D00E9420BDDE280051A8CF /* GridView+Layout.swift */; }; 84 | C661724320D91300002511D3 /* GridViewInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = C661723F20D912F1002511D3 /* GridViewInterface.swift */; }; 85 | C661724420D91300002511D3 /* ScrollViewForwarder.swift in Sources */ = {isa = PBXBuildFile; fileRef = C661724020D912F1002511D3 /* ScrollViewForwarder.swift */; }; 86 | C661724520D91300002511D3 /* GridViewInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = C661723F20D912F1002511D3 /* GridViewInterface.swift */; }; 87 | C661724620D91300002511D3 /* ScrollViewForwarder.swift in Sources */ = {isa = PBXBuildFile; fileRef = C661724020D912F1002511D3 /* ScrollViewForwarder.swift */; }; 88 | C661724720D91301002511D3 /* GridViewInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = C661723F20D912F1002511D3 /* GridViewInterface.swift */; }; 89 | C661724820D91301002511D3 /* ScrollViewForwarder.swift in Sources */ = {isa = PBXBuildFile; fileRef = C661724020D912F1002511D3 /* ScrollViewForwarder.swift */; }; 90 | /* End PBXBuildFile section */ 91 | 92 | /* Begin PBXContainerItemProxy section */ 93 | 153F9D9120C08E470051D0D3 /* PBXContainerItemProxy */ = { 94 | isa = PBXContainerItemProxy; 95 | containerPortal = 151640E120BD7A8C0062FA33 /* Project object */; 96 | proxyType = 1; 97 | remoteGlobalIDString = 151640E920BD7A8C0062FA33; 98 | remoteInfo = TheGrid; 99 | }; 100 | /* End PBXContainerItemProxy section */ 101 | 102 | /* Begin PBXCopyFilesBuildPhase section */ 103 | 153F9D9320C08E470051D0D3 /* Embed Frameworks */ = { 104 | isa = PBXCopyFilesBuildPhase; 105 | buildActionMask = 2147483647; 106 | dstPath = ""; 107 | dstSubfolderSpec = 10; 108 | files = ( 109 | 15020A9B20C0A46700BBADBD /* Hagrid.framework in Embed Frameworks */, 110 | 157D35E020DAAB1C005C01D1 /* SnapKit.framework in Embed Frameworks */, 111 | ); 112 | name = "Embed Frameworks"; 113 | runOnlyForDeploymentPostprocessing = 0; 114 | }; 115 | /* End PBXCopyFilesBuildPhase section */ 116 | 117 | /* Begin PBXFileReference section */ 118 | 15020A9D20C0A5A200BBADBD /* Hagrid.podspec */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Hagrid.podspec; sourceTree = ""; }; 119 | 15020AA420C0A65200BBADBD /* Hagrid.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Hagrid.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 120 | 15020AA620C0A65200BBADBD /* Hagrid_appleTV.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Hagrid_appleTV.h; sourceTree = ""; }; 121 | 15020AA720C0A65200BBADBD /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 122 | 15020AC420C0A9A300BBADBD /* Hagrid.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Hagrid.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 123 | 15020AC620C0A9A300BBADBD /* Hagrid_macOS.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Hagrid_macOS.h; sourceTree = ""; }; 124 | 15020AC720C0A9A300BBADBD /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 125 | 15020ACF20C0AA8100BBADBD /* Compatibility.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Compatibility.swift; sourceTree = ""; }; 126 | 15020ADC20C0AF6F00BBADBD /* Cartfile */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Cartfile; sourceTree = ""; }; 127 | 15020AE720C147F600BBADBD /* GridTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GridTableViewCell.swift; sourceTree = ""; }; 128 | 151640EA20BD7A8C0062FA33 /* Hagrid.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Hagrid.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 129 | 151640ED20BD7A8C0062FA33 /* TheGrid.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TheGrid.h; sourceTree = ""; }; 130 | 151640EE20BD7A8C0062FA33 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 131 | 1516410D20BD7B740062FA33 /* Hagrid-Demo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Hagrid-Demo.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 132 | 1516410F20BD7B740062FA33 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 133 | 1516411120BD7B740062FA33 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 134 | 1516411620BD7B750062FA33 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 135 | 1516411920BD7B750062FA33 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 136 | 1516411B20BD7B750062FA33 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 137 | 1516412120BD7D760062FA33 /* GridView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GridView.swift; sourceTree = ""; }; 138 | 153F9D7420BE02D70051D0D3 /* GridViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GridViewController.swift; sourceTree = ""; }; 139 | 153F9D7620BE1ACE0051D0D3 /* Vertical.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Vertical.swift; sourceTree = ""; }; 140 | 153F9D7920BE22F00051D0D3 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; 141 | 153F9D7A20BE22F00051D0D3 /* Podfile.lock */ = {isa = PBXFileReference; lastKnownFileType = text; path = Podfile.lock; sourceTree = ""; }; 142 | 153F9D7B20BE22F00051D0D3 /* LICENSE */ = {isa = PBXFileReference; lastKnownFileType = text; path = LICENSE; sourceTree = ""; }; 143 | 153F9D7C20BE22F00051D0D3 /* Podfile */ = {isa = PBXFileReference; lastKnownFileType = text; path = Podfile; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; 144 | 153F9D7D20BE2AEA0051D0D3 /* Other */ = {isa = PBXFileReference; lastKnownFileType = folder; path = Other; sourceTree = ""; }; 145 | 153F9D8220C023610051D0D3 /* GridScrollViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GridScrollViewController.swift; sourceTree = ""; }; 146 | 153F9D8420C027B70051D0D3 /* GridScrollView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GridScrollView.swift; sourceTree = ""; }; 147 | 153F9D8920C02B440051D0D3 /* GridSrollView+Subviews.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "GridSrollView+Subviews.swift"; sourceTree = ""; }; 148 | 157D35D920DAA1D2005C01D1 /* SnapKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SnapKit.framework; path = Carthage/Build/iOS/SnapKit.framework; sourceTree = SOURCE_ROOT; }; 149 | 157D35DB20DAA1E6005C01D1 /* SnapKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SnapKit.framework; path = Carthage/Build/tvOS/SnapKit.framework; sourceTree = SOURCE_ROOT; }; 150 | 157D35DD20DAA1F3005C01D1 /* SnapKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SnapKit.framework; path = Carthage/Build/Mac/SnapKit.framework; sourceTree = SOURCE_ROOT; }; 151 | 157D35E320DAB115005C01D1 /* GridCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GridCollectionViewCell.swift; sourceTree = ""; }; 152 | 157D35E620DAB129005C01D1 /* GridTableViewHeaderFooterView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GridTableViewHeaderFooterView.swift; sourceTree = ""; }; 153 | 157D35E920DAB1AB005C01D1 /* GridCollectionReusableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GridCollectionReusableView.swift; sourceTree = ""; }; 154 | 15D00E7920BDB8C20051A8CF /* UIColor+Tools.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIColor+Tools.swift"; sourceTree = ""; }; 155 | 15D00E7B20BDBE8A0051A8CF /* GridView+Drawing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "GridView+Drawing.swift"; sourceTree = ""; }; 156 | 15D00E7D20BDBECF0051A8CF /* GridView+Calculations.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "GridView+Calculations.swift"; sourceTree = ""; }; 157 | 15D00E8020BDC3B80051A8CF /* Position.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Position.swift; sourceTree = ""; }; 158 | 15D00E8320BDC49F0051A8CF /* UIColor+Tools.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIColor+Tools.swift"; sourceTree = ""; }; 159 | 15D00E8520BDC84A0051A8CF /* GridView+Subviews.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "GridView+Subviews.swift"; sourceTree = ""; }; 160 | 15D00E8E20BDD0690051A8CF /* Padding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Padding.swift; sourceTree = ""; }; 161 | 15D00E9020BDD1930051A8CF /* Properties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Properties.swift; sourceTree = ""; }; 162 | 15D00E9220BDD2250051A8CF /* Subview.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Subview.swift; sourceTree = ""; }; 163 | 15D00E9420BDDE280051A8CF /* GridView+Layout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "GridView+Layout.swift"; sourceTree = ""; }; 164 | C661723F20D912F1002511D3 /* GridViewInterface.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GridViewInterface.swift; sourceTree = ""; }; 165 | C661724020D912F1002511D3 /* ScrollViewForwarder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScrollViewForwarder.swift; sourceTree = ""; }; 166 | /* End PBXFileReference section */ 167 | 168 | /* Begin PBXFrameworksBuildPhase section */ 169 | 15020AA020C0A65200BBADBD /* Frameworks */ = { 170 | isa = PBXFrameworksBuildPhase; 171 | buildActionMask = 2147483647; 172 | files = ( 173 | 157D35DC20DAA1E6005C01D1 /* SnapKit.framework in Frameworks */, 174 | ); 175 | runOnlyForDeploymentPostprocessing = 0; 176 | }; 177 | 15020AC020C0A9A300BBADBD /* Frameworks */ = { 178 | isa = PBXFrameworksBuildPhase; 179 | buildActionMask = 2147483647; 180 | files = ( 181 | 157D35DE20DAA1F3005C01D1 /* SnapKit.framework in Frameworks */, 182 | ); 183 | runOnlyForDeploymentPostprocessing = 0; 184 | }; 185 | 151640E620BD7A8C0062FA33 /* Frameworks */ = { 186 | isa = PBXFrameworksBuildPhase; 187 | buildActionMask = 2147483647; 188 | files = ( 189 | 157D35DA20DAA1D3005C01D1 /* SnapKit.framework in Frameworks */, 190 | ); 191 | runOnlyForDeploymentPostprocessing = 0; 192 | }; 193 | 1516410A20BD7B740062FA33 /* Frameworks */ = { 194 | isa = PBXFrameworksBuildPhase; 195 | buildActionMask = 2147483647; 196 | files = ( 197 | ); 198 | runOnlyForDeploymentPostprocessing = 0; 199 | }; 200 | /* End PBXFrameworksBuildPhase section */ 201 | 202 | /* Begin PBXGroup section */ 203 | 15020AA520C0A65200BBADBD /* Hagrid-tvOS */ = { 204 | isa = PBXGroup; 205 | children = ( 206 | 157D35D720DAA1AC005C01D1 /* Frameworks */, 207 | 15020ABE20C0A71300BBADBD /* Supporting files */, 208 | ); 209 | path = "Hagrid-tvOS"; 210 | sourceTree = ""; 211 | }; 212 | 15020ABE20C0A71300BBADBD /* Supporting files */ = { 213 | isa = PBXGroup; 214 | children = ( 215 | 15020AA620C0A65200BBADBD /* Hagrid_appleTV.h */, 216 | 15020AA720C0A65200BBADBD /* Info.plist */, 217 | ); 218 | path = "Supporting files"; 219 | sourceTree = ""; 220 | }; 221 | 15020AC520C0A9A300BBADBD /* Hagrid-macOS */ = { 222 | isa = PBXGroup; 223 | children = ( 224 | 157D35D620DAA19C005C01D1 /* Frameworks */, 225 | 15020ACC20C0A9BE00BBADBD /* Supporting files */, 226 | ); 227 | path = "Hagrid-macOS"; 228 | sourceTree = ""; 229 | }; 230 | 15020ACC20C0A9BE00BBADBD /* Supporting files */ = { 231 | isa = PBXGroup; 232 | children = ( 233 | 15020AC620C0A9A300BBADBD /* Hagrid_macOS.h */, 234 | 15020AC720C0A9A300BBADBD /* Info.plist */, 235 | ); 236 | path = "Supporting files"; 237 | sourceTree = ""; 238 | }; 239 | 151640E020BD7A8C0062FA33 = { 240 | isa = PBXGroup; 241 | children = ( 242 | 153F9D7820BE22DF0051D0D3 /* Configs */, 243 | 153F9D9620C0A2070051D0D3 /* Classes */, 244 | 151640EC20BD7A8C0062FA33 /* Hagrid-iOS */, 245 | 15020AA520C0A65200BBADBD /* Hagrid-tvOS */, 246 | 15020AC520C0A9A300BBADBD /* Hagrid-macOS */, 247 | 1516410E20BD7B740062FA33 /* Hagrid Demo */, 248 | 151640EB20BD7A8C0062FA33 /* Products */, 249 | ); 250 | sourceTree = ""; 251 | }; 252 | 151640EB20BD7A8C0062FA33 /* Products */ = { 253 | isa = PBXGroup; 254 | children = ( 255 | 151640EA20BD7A8C0062FA33 /* Hagrid.framework */, 256 | 1516410D20BD7B740062FA33 /* Hagrid-Demo.app */, 257 | 15020AA420C0A65200BBADBD /* Hagrid.framework */, 258 | 15020AC420C0A9A300BBADBD /* Hagrid.framework */, 259 | ); 260 | name = Products; 261 | sourceTree = ""; 262 | }; 263 | 151640EC20BD7A8C0062FA33 /* Hagrid-iOS */ = { 264 | isa = PBXGroup; 265 | children = ( 266 | 157D35D820DAA1B1005C01D1 /* Frameworks */, 267 | 1516410420BD7AFF0062FA33 /* Supporting files */, 268 | ); 269 | path = "Hagrid-iOS"; 270 | sourceTree = ""; 271 | }; 272 | 1516410420BD7AFF0062FA33 /* Supporting files */ = { 273 | isa = PBXGroup; 274 | children = ( 275 | 151640ED20BD7A8C0062FA33 /* TheGrid.h */, 276 | 151640EE20BD7A8C0062FA33 /* Info.plist */, 277 | ); 278 | path = "Supporting files"; 279 | sourceTree = ""; 280 | }; 281 | 1516410520BD7B420062FA33 /* Libs */ = { 282 | isa = PBXGroup; 283 | children = ( 284 | 15D00E9020BDD1930051A8CF /* Properties.swift */, 285 | 15D00E9220BDD2250051A8CF /* Subview.swift */, 286 | 15020ACF20C0AA8100BBADBD /* Compatibility.swift */, 287 | ); 288 | path = Libs; 289 | sourceTree = ""; 290 | }; 291 | 1516410620BD7B4A0062FA33 /* Enums */ = { 292 | isa = PBXGroup; 293 | children = ( 294 | 153F9D7620BE1ACE0051D0D3 /* Vertical.swift */, 295 | 15D00E8020BDC3B80051A8CF /* Position.swift */, 296 | 15D00E8E20BDD0690051A8CF /* Padding.swift */, 297 | ); 298 | path = Enums; 299 | sourceTree = ""; 300 | }; 301 | 1516410E20BD7B740062FA33 /* Hagrid Demo */ = { 302 | isa = PBXGroup; 303 | children = ( 304 | 15D00E8220BDC4900051A8CF /* Extensions */, 305 | 1516412420BD818B0062FA33 /* View controllers */, 306 | 1516410F20BD7B740062FA33 /* AppDelegate.swift */, 307 | 1516412320BD81790062FA33 /* Supporting files */, 308 | ); 309 | path = "Hagrid Demo"; 310 | sourceTree = ""; 311 | }; 312 | 1516412320BD81790062FA33 /* Supporting files */ = { 313 | isa = PBXGroup; 314 | children = ( 315 | 1516411620BD7B750062FA33 /* Assets.xcassets */, 316 | 1516411820BD7B750062FA33 /* LaunchScreen.storyboard */, 317 | 1516411B20BD7B750062FA33 /* Info.plist */, 318 | ); 319 | path = "Supporting files"; 320 | sourceTree = ""; 321 | }; 322 | 1516412420BD818B0062FA33 /* View controllers */ = { 323 | isa = PBXGroup; 324 | children = ( 325 | 1516411120BD7B740062FA33 /* ViewController.swift */, 326 | ); 327 | path = "View controllers"; 328 | sourceTree = ""; 329 | }; 330 | 153F9D7320BE02300051D0D3 /* View controllers */ = { 331 | isa = PBXGroup; 332 | children = ( 333 | 153F9D8220C023610051D0D3 /* GridScrollViewController.swift */, 334 | 153F9D7420BE02D70051D0D3 /* GridViewController.swift */, 335 | ); 336 | path = "View controllers"; 337 | sourceTree = ""; 338 | }; 339 | 153F9D7820BE22DF0051D0D3 /* Configs */ = { 340 | isa = PBXGroup; 341 | children = ( 342 | 153F9D7920BE22F00051D0D3 /* README.md */, 343 | 15020ADC20C0AF6F00BBADBD /* Cartfile */, 344 | 15020A9D20C0A5A200BBADBD /* Hagrid.podspec */, 345 | 153F9D7C20BE22F00051D0D3 /* Podfile */, 346 | 153F9D7A20BE22F00051D0D3 /* Podfile.lock */, 347 | 153F9D7B20BE22F00051D0D3 /* LICENSE */, 348 | 153F9D7D20BE2AEA0051D0D3 /* Other */, 349 | ); 350 | name = Configs; 351 | sourceTree = ""; 352 | }; 353 | 153F9D7F20C022F80051D0D3 /* Views */ = { 354 | isa = PBXGroup; 355 | children = ( 356 | 1516412120BD7D760062FA33 /* GridView.swift */, 357 | 153F9D8420C027B70051D0D3 /* GridScrollView.swift */, 358 | 15020AE720C147F600BBADBD /* GridTableViewCell.swift */, 359 | 157D35E620DAB129005C01D1 /* GridTableViewHeaderFooterView.swift */, 360 | 157D35E320DAB115005C01D1 /* GridCollectionViewCell.swift */, 361 | 157D35E920DAB1AB005C01D1 /* GridCollectionReusableView.swift */, 362 | ); 363 | path = Views; 364 | sourceTree = ""; 365 | }; 366 | 153F9D9620C0A2070051D0D3 /* Classes */ = { 367 | isa = PBXGroup; 368 | children = ( 369 | C661723E20D912F1002511D3 /* Protocols */, 370 | 153F9D7F20C022F80051D0D3 /* Views */, 371 | 153F9D7320BE02300051D0D3 /* View controllers */, 372 | 15D00E7F20BDC3A50051A8CF /* Extensions */, 373 | 1516410620BD7B4A0062FA33 /* Enums */, 374 | 1516410520BD7B420062FA33 /* Libs */, 375 | ); 376 | path = Classes; 377 | sourceTree = ""; 378 | }; 379 | 157D35D620DAA19C005C01D1 /* Frameworks */ = { 380 | isa = PBXGroup; 381 | children = ( 382 | 157D35DD20DAA1F3005C01D1 /* SnapKit.framework */, 383 | ); 384 | name = Frameworks; 385 | sourceTree = ""; 386 | }; 387 | 157D35D720DAA1AC005C01D1 /* Frameworks */ = { 388 | isa = PBXGroup; 389 | children = ( 390 | 157D35DB20DAA1E6005C01D1 /* SnapKit.framework */, 391 | ); 392 | name = Frameworks; 393 | sourceTree = ""; 394 | }; 395 | 157D35D820DAA1B1005C01D1 /* Frameworks */ = { 396 | isa = PBXGroup; 397 | children = ( 398 | 157D35D920DAA1D2005C01D1 /* SnapKit.framework */, 399 | ); 400 | name = Frameworks; 401 | sourceTree = ""; 402 | }; 403 | 15D00E7F20BDC3A50051A8CF /* Extensions */ = { 404 | isa = PBXGroup; 405 | children = ( 406 | 15D00E7920BDB8C20051A8CF /* UIColor+Tools.swift */, 407 | 15D00E7B20BDBE8A0051A8CF /* GridView+Drawing.swift */, 408 | 15D00E7D20BDBECF0051A8CF /* GridView+Calculations.swift */, 409 | 15D00E8520BDC84A0051A8CF /* GridView+Subviews.swift */, 410 | 153F9D8920C02B440051D0D3 /* GridSrollView+Subviews.swift */, 411 | 15D00E9420BDDE280051A8CF /* GridView+Layout.swift */, 412 | ); 413 | path = Extensions; 414 | sourceTree = ""; 415 | }; 416 | 15D00E8220BDC4900051A8CF /* Extensions */ = { 417 | isa = PBXGroup; 418 | children = ( 419 | 15D00E8320BDC49F0051A8CF /* UIColor+Tools.swift */, 420 | ); 421 | path = Extensions; 422 | sourceTree = ""; 423 | }; 424 | C661723E20D912F1002511D3 /* Protocols */ = { 425 | isa = PBXGroup; 426 | children = ( 427 | C661723F20D912F1002511D3 /* GridViewInterface.swift */, 428 | C661724020D912F1002511D3 /* ScrollViewForwarder.swift */, 429 | ); 430 | path = Protocols; 431 | sourceTree = ""; 432 | }; 433 | /* End PBXGroup section */ 434 | 435 | /* Begin PBXHeadersBuildPhase section */ 436 | 15020AA120C0A65200BBADBD /* Headers */ = { 437 | isa = PBXHeadersBuildPhase; 438 | buildActionMask = 2147483647; 439 | files = ( 440 | 15020AA820C0A65200BBADBD /* Hagrid_appleTV.h in Headers */, 441 | ); 442 | runOnlyForDeploymentPostprocessing = 0; 443 | }; 444 | 15020AC120C0A9A300BBADBD /* Headers */ = { 445 | isa = PBXHeadersBuildPhase; 446 | buildActionMask = 2147483647; 447 | files = ( 448 | 15020AC820C0A9A300BBADBD /* Hagrid_macOS.h in Headers */, 449 | ); 450 | runOnlyForDeploymentPostprocessing = 0; 451 | }; 452 | 151640E720BD7A8C0062FA33 /* Headers */ = { 453 | isa = PBXHeadersBuildPhase; 454 | buildActionMask = 2147483647; 455 | files = ( 456 | 151640FB20BD7A8C0062FA33 /* TheGrid.h in Headers */, 457 | ); 458 | runOnlyForDeploymentPostprocessing = 0; 459 | }; 460 | /* End PBXHeadersBuildPhase section */ 461 | 462 | /* Begin PBXNativeTarget section */ 463 | 15020AA320C0A65200BBADBD /* Hagrid-tvOS */ = { 464 | isa = PBXNativeTarget; 465 | buildConfigurationList = 15020AA920C0A65200BBADBD /* Build configuration list for PBXNativeTarget "Hagrid-tvOS" */; 466 | buildPhases = ( 467 | 15020A9F20C0A65200BBADBD /* Sources */, 468 | 15020AA020C0A65200BBADBD /* Frameworks */, 469 | 15020AA120C0A65200BBADBD /* Headers */, 470 | 15020AA220C0A65200BBADBD /* Resources */, 471 | ); 472 | buildRules = ( 473 | ); 474 | dependencies = ( 475 | ); 476 | name = "Hagrid-tvOS"; 477 | productName = "Hagrid-appleTV"; 478 | productReference = 15020AA420C0A65200BBADBD /* Hagrid.framework */; 479 | productType = "com.apple.product-type.framework"; 480 | }; 481 | 15020AC320C0A9A300BBADBD /* Hagrid-macOS */ = { 482 | isa = PBXNativeTarget; 483 | buildConfigurationList = 15020AC920C0A9A300BBADBD /* Build configuration list for PBXNativeTarget "Hagrid-macOS" */; 484 | buildPhases = ( 485 | 15020ABF20C0A9A300BBADBD /* Sources */, 486 | 15020AC020C0A9A300BBADBD /* Frameworks */, 487 | 15020AC120C0A9A300BBADBD /* Headers */, 488 | 15020AC220C0A9A300BBADBD /* Resources */, 489 | ); 490 | buildRules = ( 491 | ); 492 | dependencies = ( 493 | ); 494 | name = "Hagrid-macOS"; 495 | productName = "Hagrid-macOS"; 496 | productReference = 15020AC420C0A9A300BBADBD /* Hagrid.framework */; 497 | productType = "com.apple.product-type.framework"; 498 | }; 499 | 151640E920BD7A8C0062FA33 /* Hagrid-iOS */ = { 500 | isa = PBXNativeTarget; 501 | buildConfigurationList = 151640FE20BD7A8C0062FA33 /* Build configuration list for PBXNativeTarget "Hagrid-iOS" */; 502 | buildPhases = ( 503 | 151640E520BD7A8C0062FA33 /* Sources */, 504 | 151640E620BD7A8C0062FA33 /* Frameworks */, 505 | 151640E720BD7A8C0062FA33 /* Headers */, 506 | 151640E820BD7A8C0062FA33 /* Resources */, 507 | ); 508 | buildRules = ( 509 | ); 510 | dependencies = ( 511 | ); 512 | name = "Hagrid-iOS"; 513 | productName = TheGrid; 514 | productReference = 151640EA20BD7A8C0062FA33 /* Hagrid.framework */; 515 | productType = "com.apple.product-type.framework"; 516 | }; 517 | 1516410C20BD7B740062FA33 /* Hagrid-Demo */ = { 518 | isa = PBXNativeTarget; 519 | buildConfigurationList = 1516411C20BD7B750062FA33 /* Build configuration list for PBXNativeTarget "Hagrid-Demo" */; 520 | buildPhases = ( 521 | 1516410920BD7B740062FA33 /* Sources */, 522 | 1516410A20BD7B740062FA33 /* Frameworks */, 523 | 1516410B20BD7B740062FA33 /* Resources */, 524 | 153F9D9320C08E470051D0D3 /* Embed Frameworks */, 525 | ); 526 | buildRules = ( 527 | ); 528 | dependencies = ( 529 | 153F9D9220C08E470051D0D3 /* PBXTargetDependency */, 530 | ); 531 | name = "Hagrid-Demo"; 532 | productName = Hagrid; 533 | productReference = 1516410D20BD7B740062FA33 /* Hagrid-Demo.app */; 534 | productType = "com.apple.product-type.application"; 535 | }; 536 | /* End PBXNativeTarget section */ 537 | 538 | /* Begin PBXProject section */ 539 | 151640E120BD7A8C0062FA33 /* Project object */ = { 540 | isa = PBXProject; 541 | attributes = { 542 | LastSwiftUpdateCheck = 0930; 543 | LastUpgradeCheck = 0930; 544 | ORGANIZATIONNAME = LiveUI; 545 | TargetAttributes = { 546 | 15020AA320C0A65200BBADBD = { 547 | CreatedOnToolsVersion = 9.3.1; 548 | }; 549 | 15020AC320C0A9A300BBADBD = { 550 | CreatedOnToolsVersion = 9.3.1; 551 | }; 552 | 151640E920BD7A8C0062FA33 = { 553 | CreatedOnToolsVersion = 9.3; 554 | LastSwiftMigration = 0930; 555 | }; 556 | 1516410C20BD7B740062FA33 = { 557 | CreatedOnToolsVersion = 9.3; 558 | }; 559 | }; 560 | }; 561 | buildConfigurationList = 151640E420BD7A8C0062FA33 /* Build configuration list for PBXProject "Hagrid" */; 562 | compatibilityVersion = "Xcode 9.3"; 563 | developmentRegion = en; 564 | hasScannedForEncodings = 0; 565 | knownRegions = ( 566 | en, 567 | Base, 568 | ); 569 | mainGroup = 151640E020BD7A8C0062FA33; 570 | productRefGroup = 151640EB20BD7A8C0062FA33 /* Products */; 571 | projectDirPath = ""; 572 | projectRoot = ""; 573 | targets = ( 574 | 1516410C20BD7B740062FA33 /* Hagrid-Demo */, 575 | 151640E920BD7A8C0062FA33 /* Hagrid-iOS */, 576 | 15020AA320C0A65200BBADBD /* Hagrid-tvOS */, 577 | 15020AC320C0A9A300BBADBD /* Hagrid-macOS */, 578 | ); 579 | }; 580 | /* End PBXProject section */ 581 | 582 | /* Begin PBXResourcesBuildPhase section */ 583 | 15020AA220C0A65200BBADBD /* Resources */ = { 584 | isa = PBXResourcesBuildPhase; 585 | buildActionMask = 2147483647; 586 | files = ( 587 | 15020ADE20C0AF7000BBADBD /* Cartfile in Resources */, 588 | ); 589 | runOnlyForDeploymentPostprocessing = 0; 590 | }; 591 | 15020AC220C0A9A300BBADBD /* Resources */ = { 592 | isa = PBXResourcesBuildPhase; 593 | buildActionMask = 2147483647; 594 | files = ( 595 | 15020ADF20C0AF7000BBADBD /* Cartfile in Resources */, 596 | ); 597 | runOnlyForDeploymentPostprocessing = 0; 598 | }; 599 | 151640E820BD7A8C0062FA33 /* Resources */ = { 600 | isa = PBXResourcesBuildPhase; 601 | buildActionMask = 2147483647; 602 | files = ( 603 | 15020ADD20C0AF7000BBADBD /* Cartfile in Resources */, 604 | ); 605 | runOnlyForDeploymentPostprocessing = 0; 606 | }; 607 | 1516410B20BD7B740062FA33 /* Resources */ = { 608 | isa = PBXResourcesBuildPhase; 609 | buildActionMask = 2147483647; 610 | files = ( 611 | 1516411A20BD7B750062FA33 /* LaunchScreen.storyboard in Resources */, 612 | 1516411720BD7B750062FA33 /* Assets.xcassets in Resources */, 613 | 15020A9E20C0A5A200BBADBD /* Hagrid.podspec in Resources */, 614 | ); 615 | runOnlyForDeploymentPostprocessing = 0; 616 | }; 617 | /* End PBXResourcesBuildPhase section */ 618 | 619 | /* Begin PBXSourcesBuildPhase section */ 620 | 15020A9F20C0A65200BBADBD /* Sources */ = { 621 | isa = PBXSourcesBuildPhase; 622 | buildActionMask = 2147483647; 623 | files = ( 624 | 15020AB920C0A6B500BBADBD /* Position.swift in Sources */, 625 | 15020AE920C147F600BBADBD /* GridTableViewCell.swift in Sources */, 626 | 15020ABC20C0A6B500BBADBD /* Subview.swift in Sources */, 627 | 15020AB320C0A6B500BBADBD /* GridView+Drawing.swift in Sources */, 628 | 157D35E520DAB115005C01D1 /* GridCollectionViewCell.swift in Sources */, 629 | 15020AB720C0A6B500BBADBD /* GridView+Layout.swift in Sources */, 630 | 15020AB220C0A6B500BBADBD /* UIColor+Tools.swift in Sources */, 631 | C661724520D91300002511D3 /* GridViewInterface.swift in Sources */, 632 | C661724620D91300002511D3 /* ScrollViewForwarder.swift in Sources */, 633 | 15020AB820C0A6B500BBADBD /* Vertical.swift in Sources */, 634 | 157D35EB20DAB1AB005C01D1 /* GridCollectionReusableView.swift in Sources */, 635 | 15020AAE20C0A6B500BBADBD /* GridView.swift in Sources */, 636 | 15020AD120C0AA8100BBADBD /* Compatibility.swift in Sources */, 637 | 15020AB520C0A6B500BBADBD /* GridView+Subviews.swift in Sources */, 638 | 15020ABB20C0A6B500BBADBD /* Properties.swift in Sources */, 639 | 15020AB420C0A6B500BBADBD /* GridView+Calculations.swift in Sources */, 640 | 15020AB120C0A6B500BBADBD /* GridViewController.swift in Sources */, 641 | 15020ABA20C0A6B500BBADBD /* Padding.swift in Sources */, 642 | 157D35E820DAB129005C01D1 /* GridTableViewHeaderFooterView.swift in Sources */, 643 | 15020AB020C0A6B500BBADBD /* GridScrollViewController.swift in Sources */, 644 | 15020AAF20C0A6B500BBADBD /* GridScrollView.swift in Sources */, 645 | 15020AB620C0A6B500BBADBD /* GridSrollView+Subviews.swift in Sources */, 646 | ); 647 | runOnlyForDeploymentPostprocessing = 0; 648 | }; 649 | 15020ABF20C0A9A300BBADBD /* Sources */ = { 650 | isa = PBXSourcesBuildPhase; 651 | buildActionMask = 2147483647; 652 | files = ( 653 | 15020AEA20C147F600BBADBD /* GridTableViewCell.swift in Sources */, 654 | 15020AE020C0BF6700BBADBD /* GridView+Drawing.swift in Sources */, 655 | 15020ACD20C0A9F800BBADBD /* Properties.swift in Sources */, 656 | 15020AE320C0BFAC00BBADBD /* GridScrollView.swift in Sources */, 657 | 15020ACE20C0A9F800BBADBD /* Subview.swift in Sources */, 658 | 15020AE520C0BFEA00BBADBD /* GridViewController.swift in Sources */, 659 | C661724720D91301002511D3 /* GridViewInterface.swift in Sources */, 660 | C661724820D91301002511D3 /* ScrollViewForwarder.swift in Sources */, 661 | 15020AD920C0AC8D00BBADBD /* GridView+Layout.swift in Sources */, 662 | 15020ADA20C0AC9F00BBADBD /* GridView.swift in Sources */, 663 | 15020AD220C0AA8100BBADBD /* Compatibility.swift in Sources */, 664 | 15020AD420C0AB7500BBADBD /* Position.swift in Sources */, 665 | 15020AD320C0AB7500BBADBD /* Vertical.swift in Sources */, 666 | 15020AD620C0AC8D00BBADBD /* GridView+Calculations.swift in Sources */, 667 | 15020AD720C0AC8D00BBADBD /* GridView+Subviews.swift in Sources */, 668 | 15020AD520C0AB7500BBADBD /* Padding.swift in Sources */, 669 | 15020AE420C0BFB800BBADBD /* GridScrollViewController.swift in Sources */, 670 | 15020AE620C0BFEA00BBADBD /* GridSrollView+Subviews.swift in Sources */, 671 | 15020AE120C0BF6B00BBADBD /* UIColor+Tools.swift in Sources */, 672 | ); 673 | runOnlyForDeploymentPostprocessing = 0; 674 | }; 675 | 151640E520BD7A8C0062FA33 /* Sources */ = { 676 | isa = PBXSourcesBuildPhase; 677 | buildActionMask = 2147483647; 678 | files = ( 679 | 15020AE820C147F600BBADBD /* GridTableViewCell.swift in Sources */, 680 | 15D00E8F20BDD0690051A8CF /* Padding.swift in Sources */, 681 | 1516412220BD7D760062FA33 /* GridView.swift in Sources */, 682 | 15D00E8120BDC3B80051A8CF /* Position.swift in Sources */, 683 | 157D35E420DAB115005C01D1 /* GridCollectionViewCell.swift in Sources */, 684 | 153F9D7720BE1ACE0051D0D3 /* Vertical.swift in Sources */, 685 | 15D00E9520BDDE280051A8CF /* GridView+Layout.swift in Sources */, 686 | C661724320D91300002511D3 /* GridViewInterface.swift in Sources */, 687 | C661724420D91300002511D3 /* ScrollViewForwarder.swift in Sources */, 688 | 15D00E7C20BDBE8A0051A8CF /* GridView+Drawing.swift in Sources */, 689 | 157D35EA20DAB1AB005C01D1 /* GridCollectionReusableView.swift in Sources */, 690 | 15020AD020C0AA8100BBADBD /* Compatibility.swift in Sources */, 691 | 153F9D8A20C02B440051D0D3 /* GridSrollView+Subviews.swift in Sources */, 692 | 153F9D8320C023610051D0D3 /* GridScrollViewController.swift in Sources */, 693 | 15D00E7A20BDB8C20051A8CF /* UIColor+Tools.swift in Sources */, 694 | 15D00E9120BDD1930051A8CF /* Properties.swift in Sources */, 695 | 153F9D8520C027B70051D0D3 /* GridScrollView.swift in Sources */, 696 | 153F9D7520BE02D70051D0D3 /* GridViewController.swift in Sources */, 697 | 157D35E720DAB129005C01D1 /* GridTableViewHeaderFooterView.swift in Sources */, 698 | 15D00E9320BDD2250051A8CF /* Subview.swift in Sources */, 699 | 15D00E8620BDC84A0051A8CF /* GridView+Subviews.swift in Sources */, 700 | 15D00E7E20BDBECF0051A8CF /* GridView+Calculations.swift in Sources */, 701 | ); 702 | runOnlyForDeploymentPostprocessing = 0; 703 | }; 704 | 1516410920BD7B740062FA33 /* Sources */ = { 705 | isa = PBXSourcesBuildPhase; 706 | buildActionMask = 2147483647; 707 | files = ( 708 | 15D00E8420BDC49F0051A8CF /* UIColor+Tools.swift in Sources */, 709 | 1516411220BD7B740062FA33 /* ViewController.swift in Sources */, 710 | 1516411020BD7B740062FA33 /* AppDelegate.swift in Sources */, 711 | ); 712 | runOnlyForDeploymentPostprocessing = 0; 713 | }; 714 | /* End PBXSourcesBuildPhase section */ 715 | 716 | /* Begin PBXTargetDependency section */ 717 | 153F9D9220C08E470051D0D3 /* PBXTargetDependency */ = { 718 | isa = PBXTargetDependency; 719 | target = 151640E920BD7A8C0062FA33 /* Hagrid-iOS */; 720 | targetProxy = 153F9D9120C08E470051D0D3 /* PBXContainerItemProxy */; 721 | }; 722 | /* End PBXTargetDependency section */ 723 | 724 | /* Begin PBXVariantGroup section */ 725 | 1516411820BD7B750062FA33 /* LaunchScreen.storyboard */ = { 726 | isa = PBXVariantGroup; 727 | children = ( 728 | 1516411920BD7B750062FA33 /* Base */, 729 | ); 730 | name = LaunchScreen.storyboard; 731 | sourceTree = ""; 732 | }; 733 | /* End PBXVariantGroup section */ 734 | 735 | /* Begin XCBuildConfiguration section */ 736 | 15020AAA20C0A65200BBADBD /* Debug */ = { 737 | isa = XCBuildConfiguration; 738 | buildSettings = { 739 | CODE_SIGN_IDENTITY = ""; 740 | CODE_SIGN_STYLE = Automatic; 741 | DEFINES_MODULE = YES; 742 | DYLIB_COMPATIBILITY_VERSION = 1; 743 | DYLIB_CURRENT_VERSION = 1; 744 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 745 | FRAMEWORK_SEARCH_PATHS = ( 746 | "$(inherited)", 747 | "$(PROJECT_DIR)/Carthage/Build/tvOS", 748 | ); 749 | INFOPLIST_FILE = "Hagrid-tvOS/Supporting files/Info.plist"; 750 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 751 | LD_RUNPATH_SEARCH_PATHS = ( 752 | "$(inherited)", 753 | "@executable_path/Frameworks", 754 | "@loader_path/Frameworks", 755 | ); 756 | PRODUCT_BUNDLE_IDENTIFIER = "io.liveui.Hagrid-appleTV"; 757 | PRODUCT_NAME = Hagrid; 758 | SDKROOT = appletvos; 759 | SKIP_INSTALL = YES; 760 | SWIFT_VERSION = 4.0; 761 | TARGETED_DEVICE_FAMILY = 3; 762 | TVOS_DEPLOYMENT_TARGET = 10.2; 763 | }; 764 | name = Debug; 765 | }; 766 | 15020AAB20C0A65200BBADBD /* Release */ = { 767 | isa = XCBuildConfiguration; 768 | buildSettings = { 769 | CODE_SIGN_IDENTITY = ""; 770 | CODE_SIGN_STYLE = Automatic; 771 | DEFINES_MODULE = YES; 772 | DYLIB_COMPATIBILITY_VERSION = 1; 773 | DYLIB_CURRENT_VERSION = 1; 774 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 775 | FRAMEWORK_SEARCH_PATHS = ( 776 | "$(inherited)", 777 | "$(PROJECT_DIR)/Carthage/Build/tvOS", 778 | ); 779 | INFOPLIST_FILE = "Hagrid-tvOS/Supporting files/Info.plist"; 780 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 781 | LD_RUNPATH_SEARCH_PATHS = ( 782 | "$(inherited)", 783 | "@executable_path/Frameworks", 784 | "@loader_path/Frameworks", 785 | ); 786 | PRODUCT_BUNDLE_IDENTIFIER = "io.liveui.Hagrid-appleTV"; 787 | PRODUCT_NAME = Hagrid; 788 | SDKROOT = appletvos; 789 | SKIP_INSTALL = YES; 790 | SWIFT_VERSION = 4.0; 791 | TARGETED_DEVICE_FAMILY = 3; 792 | TVOS_DEPLOYMENT_TARGET = 10.2; 793 | }; 794 | name = Release; 795 | }; 796 | 15020ACA20C0A9A300BBADBD /* Debug */ = { 797 | isa = XCBuildConfiguration; 798 | buildSettings = { 799 | CODE_SIGN_IDENTITY = "-"; 800 | CODE_SIGN_STYLE = Automatic; 801 | COMBINE_HIDPI_IMAGES = YES; 802 | DEFINES_MODULE = YES; 803 | DYLIB_COMPATIBILITY_VERSION = 1; 804 | DYLIB_CURRENT_VERSION = 1; 805 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 806 | FRAMEWORK_SEARCH_PATHS = ( 807 | "$(inherited)", 808 | "$(PROJECT_DIR)/Carthage/Build/Mac", 809 | ); 810 | FRAMEWORK_VERSION = A; 811 | INFOPLIST_FILE = "Hagrid-macOS/Supporting files/Info.plist"; 812 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 813 | LD_RUNPATH_SEARCH_PATHS = ( 814 | "$(inherited)", 815 | "@executable_path/../Frameworks", 816 | "@loader_path/Frameworks", 817 | ); 818 | MACOSX_DEPLOYMENT_TARGET = 10.12; 819 | PRODUCT_BUNDLE_IDENTIFIER = "io.liveui.Hagrid-macOS"; 820 | PRODUCT_NAME = Hagrid; 821 | SDKROOT = macosx; 822 | SKIP_INSTALL = YES; 823 | SWIFT_VERSION = 4.0; 824 | }; 825 | name = Debug; 826 | }; 827 | 15020ACB20C0A9A300BBADBD /* Release */ = { 828 | isa = XCBuildConfiguration; 829 | buildSettings = { 830 | CODE_SIGN_IDENTITY = "-"; 831 | CODE_SIGN_STYLE = Automatic; 832 | COMBINE_HIDPI_IMAGES = YES; 833 | DEFINES_MODULE = YES; 834 | DYLIB_COMPATIBILITY_VERSION = 1; 835 | DYLIB_CURRENT_VERSION = 1; 836 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 837 | FRAMEWORK_SEARCH_PATHS = ( 838 | "$(inherited)", 839 | "$(PROJECT_DIR)/Carthage/Build/Mac", 840 | ); 841 | FRAMEWORK_VERSION = A; 842 | INFOPLIST_FILE = "Hagrid-macOS/Supporting files/Info.plist"; 843 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 844 | LD_RUNPATH_SEARCH_PATHS = ( 845 | "$(inherited)", 846 | "@executable_path/../Frameworks", 847 | "@loader_path/Frameworks", 848 | ); 849 | MACOSX_DEPLOYMENT_TARGET = 10.12; 850 | PRODUCT_BUNDLE_IDENTIFIER = "io.liveui.Hagrid-macOS"; 851 | PRODUCT_NAME = Hagrid; 852 | SDKROOT = macosx; 853 | SKIP_INSTALL = YES; 854 | SWIFT_VERSION = 4.0; 855 | }; 856 | name = Release; 857 | }; 858 | 151640FC20BD7A8C0062FA33 /* Debug */ = { 859 | isa = XCBuildConfiguration; 860 | buildSettings = { 861 | ALWAYS_SEARCH_USER_PATHS = NO; 862 | CLANG_ANALYZER_NONNULL = YES; 863 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 864 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 865 | CLANG_CXX_LIBRARY = "libc++"; 866 | CLANG_ENABLE_MODULES = YES; 867 | CLANG_ENABLE_OBJC_ARC = YES; 868 | CLANG_ENABLE_OBJC_WEAK = YES; 869 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 870 | CLANG_WARN_BOOL_CONVERSION = YES; 871 | CLANG_WARN_COMMA = YES; 872 | CLANG_WARN_CONSTANT_CONVERSION = YES; 873 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 874 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 875 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 876 | CLANG_WARN_EMPTY_BODY = YES; 877 | CLANG_WARN_ENUM_CONVERSION = YES; 878 | CLANG_WARN_INFINITE_RECURSION = YES; 879 | CLANG_WARN_INT_CONVERSION = YES; 880 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 881 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 882 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 883 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 884 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 885 | CLANG_WARN_STRICT_PROTOTYPES = YES; 886 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 887 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 888 | CLANG_WARN_UNREACHABLE_CODE = YES; 889 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 890 | CODE_SIGN_IDENTITY = "iPhone Developer"; 891 | COPY_PHASE_STRIP = NO; 892 | CURRENT_PROJECT_VERSION = 1; 893 | DEBUG_INFORMATION_FORMAT = dwarf; 894 | ENABLE_STRICT_OBJC_MSGSEND = YES; 895 | ENABLE_TESTABILITY = YES; 896 | GCC_C_LANGUAGE_STANDARD = gnu11; 897 | GCC_DYNAMIC_NO_PIC = NO; 898 | GCC_NO_COMMON_BLOCKS = YES; 899 | GCC_OPTIMIZATION_LEVEL = 0; 900 | GCC_PREPROCESSOR_DEFINITIONS = ( 901 | "DEBUG=1", 902 | "$(inherited)", 903 | ); 904 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 905 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 906 | GCC_WARN_UNDECLARED_SELECTOR = YES; 907 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 908 | GCC_WARN_UNUSED_FUNCTION = YES; 909 | GCC_WARN_UNUSED_VARIABLE = YES; 910 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 911 | MTL_ENABLE_DEBUG_INFO = YES; 912 | ONLY_ACTIVE_ARCH = YES; 913 | SDKROOT = iphoneos; 914 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 915 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 916 | VERSIONING_SYSTEM = "apple-generic"; 917 | VERSION_INFO_PREFIX = ""; 918 | }; 919 | name = Debug; 920 | }; 921 | 151640FD20BD7A8C0062FA33 /* Release */ = { 922 | isa = XCBuildConfiguration; 923 | buildSettings = { 924 | ALWAYS_SEARCH_USER_PATHS = NO; 925 | CLANG_ANALYZER_NONNULL = YES; 926 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 927 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 928 | CLANG_CXX_LIBRARY = "libc++"; 929 | CLANG_ENABLE_MODULES = YES; 930 | CLANG_ENABLE_OBJC_ARC = YES; 931 | CLANG_ENABLE_OBJC_WEAK = YES; 932 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 933 | CLANG_WARN_BOOL_CONVERSION = YES; 934 | CLANG_WARN_COMMA = YES; 935 | CLANG_WARN_CONSTANT_CONVERSION = YES; 936 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 937 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 938 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 939 | CLANG_WARN_EMPTY_BODY = YES; 940 | CLANG_WARN_ENUM_CONVERSION = YES; 941 | CLANG_WARN_INFINITE_RECURSION = YES; 942 | CLANG_WARN_INT_CONVERSION = YES; 943 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 944 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 945 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 946 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 947 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 948 | CLANG_WARN_STRICT_PROTOTYPES = YES; 949 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 950 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 951 | CLANG_WARN_UNREACHABLE_CODE = YES; 952 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 953 | CODE_SIGN_IDENTITY = "iPhone Developer"; 954 | COPY_PHASE_STRIP = NO; 955 | CURRENT_PROJECT_VERSION = 1; 956 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 957 | ENABLE_NS_ASSERTIONS = NO; 958 | ENABLE_STRICT_OBJC_MSGSEND = YES; 959 | GCC_C_LANGUAGE_STANDARD = gnu11; 960 | GCC_NO_COMMON_BLOCKS = YES; 961 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 962 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 963 | GCC_WARN_UNDECLARED_SELECTOR = YES; 964 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 965 | GCC_WARN_UNUSED_FUNCTION = YES; 966 | GCC_WARN_UNUSED_VARIABLE = YES; 967 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 968 | MTL_ENABLE_DEBUG_INFO = NO; 969 | SDKROOT = iphoneos; 970 | SWIFT_COMPILATION_MODE = wholemodule; 971 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 972 | VALIDATE_PRODUCT = YES; 973 | VERSIONING_SYSTEM = "apple-generic"; 974 | VERSION_INFO_PREFIX = ""; 975 | }; 976 | name = Release; 977 | }; 978 | 151640FF20BD7A8C0062FA33 /* Debug */ = { 979 | isa = XCBuildConfiguration; 980 | buildSettings = { 981 | CLANG_ENABLE_MODULES = YES; 982 | CODE_SIGN_IDENTITY = ""; 983 | CODE_SIGN_STYLE = Automatic; 984 | DEFINES_MODULE = YES; 985 | DEVELOPMENT_TEAM = 3R8ZGADY96; 986 | DYLIB_COMPATIBILITY_VERSION = 1; 987 | DYLIB_CURRENT_VERSION = 1; 988 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 989 | FRAMEWORK_SEARCH_PATHS = ( 990 | "$(inherited)", 991 | "$(PROJECT_DIR)/Carthage/Build/iOS", 992 | ); 993 | INFOPLIST_FILE = "Hagrid-iOS/Supporting files/Info.plist"; 994 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 995 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 996 | LD_RUNPATH_SEARCH_PATHS = ( 997 | "$(inherited)", 998 | "@executable_path/Frameworks", 999 | "@loader_path/Frameworks", 1000 | ); 1001 | PRODUCT_BUNDLE_IDENTIFIER = "io.liveui.Hagrid-iOS"; 1002 | PRODUCT_NAME = Hagrid; 1003 | SKIP_INSTALL = YES; 1004 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 1005 | SWIFT_VERSION = 4.0; 1006 | TARGETED_DEVICE_FAMILY = "1,2"; 1007 | }; 1008 | name = Debug; 1009 | }; 1010 | 1516410020BD7A8C0062FA33 /* Release */ = { 1011 | isa = XCBuildConfiguration; 1012 | buildSettings = { 1013 | CLANG_ENABLE_MODULES = YES; 1014 | CODE_SIGN_IDENTITY = ""; 1015 | CODE_SIGN_STYLE = Automatic; 1016 | DEFINES_MODULE = YES; 1017 | DEVELOPMENT_TEAM = 3R8ZGADY96; 1018 | DYLIB_COMPATIBILITY_VERSION = 1; 1019 | DYLIB_CURRENT_VERSION = 1; 1020 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 1021 | FRAMEWORK_SEARCH_PATHS = ( 1022 | "$(inherited)", 1023 | "$(PROJECT_DIR)/Carthage/Build/iOS", 1024 | ); 1025 | INFOPLIST_FILE = "Hagrid-iOS/Supporting files/Info.plist"; 1026 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 1027 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 1028 | LD_RUNPATH_SEARCH_PATHS = ( 1029 | "$(inherited)", 1030 | "@executable_path/Frameworks", 1031 | "@loader_path/Frameworks", 1032 | ); 1033 | PRODUCT_BUNDLE_IDENTIFIER = "io.liveui.Hagrid-iOS"; 1034 | PRODUCT_NAME = Hagrid; 1035 | SKIP_INSTALL = YES; 1036 | SWIFT_VERSION = 4.0; 1037 | TARGETED_DEVICE_FAMILY = "1,2"; 1038 | }; 1039 | name = Release; 1040 | }; 1041 | 1516411D20BD7B750062FA33 /* Debug */ = { 1042 | isa = XCBuildConfiguration; 1043 | buildSettings = { 1044 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 1045 | CODE_SIGN_STYLE = Automatic; 1046 | DEVELOPMENT_TEAM = 3R8ZGADY96; 1047 | FRAMEWORK_SEARCH_PATHS = ( 1048 | ./Carthage/Build/iOS/, 1049 | "$(PROJECT_DIR)/Carthage/Build/iOS", 1050 | ); 1051 | INFOPLIST_FILE = "Hagrid Demo/Supporting files/Info.plist"; 1052 | IPHONEOS_DEPLOYMENT_TARGET = 10.3; 1053 | LD_RUNPATH_SEARCH_PATHS = ( 1054 | "$(inherited)", 1055 | "@executable_path/Frameworks", 1056 | ); 1057 | PRODUCT_BUNDLE_IDENTIFIER = "io.liveui.Hagrid-Demo"; 1058 | PRODUCT_NAME = "$(TARGET_NAME)"; 1059 | SWIFT_VERSION = 4.0; 1060 | TARGETED_DEVICE_FAMILY = "1,2"; 1061 | }; 1062 | name = Debug; 1063 | }; 1064 | 1516411E20BD7B750062FA33 /* Release */ = { 1065 | isa = XCBuildConfiguration; 1066 | buildSettings = { 1067 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 1068 | CODE_SIGN_STYLE = Automatic; 1069 | DEVELOPMENT_TEAM = 3R8ZGADY96; 1070 | FRAMEWORK_SEARCH_PATHS = ( 1071 | ./Carthage/Build/iOS/, 1072 | "$(PROJECT_DIR)/Carthage/Build/iOS", 1073 | ); 1074 | INFOPLIST_FILE = "Hagrid Demo/Supporting files/Info.plist"; 1075 | IPHONEOS_DEPLOYMENT_TARGET = 10.3; 1076 | LD_RUNPATH_SEARCH_PATHS = ( 1077 | "$(inherited)", 1078 | "@executable_path/Frameworks", 1079 | ); 1080 | PRODUCT_BUNDLE_IDENTIFIER = "io.liveui.Hagrid-Demo"; 1081 | PRODUCT_NAME = "$(TARGET_NAME)"; 1082 | SWIFT_VERSION = 4.0; 1083 | TARGETED_DEVICE_FAMILY = "1,2"; 1084 | }; 1085 | name = Release; 1086 | }; 1087 | /* End XCBuildConfiguration section */ 1088 | 1089 | /* Begin XCConfigurationList section */ 1090 | 15020AA920C0A65200BBADBD /* Build configuration list for PBXNativeTarget "Hagrid-tvOS" */ = { 1091 | isa = XCConfigurationList; 1092 | buildConfigurations = ( 1093 | 15020AAA20C0A65200BBADBD /* Debug */, 1094 | 15020AAB20C0A65200BBADBD /* Release */, 1095 | ); 1096 | defaultConfigurationIsVisible = 0; 1097 | defaultConfigurationName = Release; 1098 | }; 1099 | 15020AC920C0A9A300BBADBD /* Build configuration list for PBXNativeTarget "Hagrid-macOS" */ = { 1100 | isa = XCConfigurationList; 1101 | buildConfigurations = ( 1102 | 15020ACA20C0A9A300BBADBD /* Debug */, 1103 | 15020ACB20C0A9A300BBADBD /* Release */, 1104 | ); 1105 | defaultConfigurationIsVisible = 0; 1106 | defaultConfigurationName = Release; 1107 | }; 1108 | 151640E420BD7A8C0062FA33 /* Build configuration list for PBXProject "Hagrid" */ = { 1109 | isa = XCConfigurationList; 1110 | buildConfigurations = ( 1111 | 151640FC20BD7A8C0062FA33 /* Debug */, 1112 | 151640FD20BD7A8C0062FA33 /* Release */, 1113 | ); 1114 | defaultConfigurationIsVisible = 0; 1115 | defaultConfigurationName = Release; 1116 | }; 1117 | 151640FE20BD7A8C0062FA33 /* Build configuration list for PBXNativeTarget "Hagrid-iOS" */ = { 1118 | isa = XCConfigurationList; 1119 | buildConfigurations = ( 1120 | 151640FF20BD7A8C0062FA33 /* Debug */, 1121 | 1516410020BD7A8C0062FA33 /* Release */, 1122 | ); 1123 | defaultConfigurationIsVisible = 0; 1124 | defaultConfigurationName = Release; 1125 | }; 1126 | 1516411C20BD7B750062FA33 /* Build configuration list for PBXNativeTarget "Hagrid-Demo" */ = { 1127 | isa = XCConfigurationList; 1128 | buildConfigurations = ( 1129 | 1516411D20BD7B750062FA33 /* Debug */, 1130 | 1516411E20BD7B750062FA33 /* Release */, 1131 | ); 1132 | defaultConfigurationIsVisible = 0; 1133 | defaultConfigurationName = Release; 1134 | }; 1135 | /* End XCConfigurationList section */ 1136 | }; 1137 | rootObject = 151640E120BD7A8C0062FA33 /* Project object */; 1138 | } 1139 | -------------------------------------------------------------------------------- /Hagrid.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Hagrid.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Hagrid.xcodeproj/xcshareddata/xcschemes/Hagrid iOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 53 | 54 | 64 | 65 | 71 | 72 | 73 | 74 | 75 | 76 | 82 | 83 | 89 | 90 | 91 | 92 | 94 | 95 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /Hagrid.xcodeproj/xcshareddata/xcschemes/Hagrid macOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 45 | 46 | 52 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 70 | 71 | 72 | 73 | 75 | 76 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /Hagrid.xcodeproj/xcshareddata/xcschemes/Hagrid tvOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 45 | 46 | 52 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 70 | 71 | 72 | 73 | 75 | 76 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /Hagrid.xcodeproj/xcuserdata/pro.xcuserdatad/xcschemes/Demo App.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /Hagrid.xcodeproj/xcuserdata/pro.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Demo App.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | Hagrid iOS.xcscheme_^#shared#^_ 13 | 14 | orderHint 15 | 1 16 | 17 | Hagrid macOS.xcscheme_^#shared#^_ 18 | 19 | orderHint 20 | 3 21 | 22 | Hagrid tvOS.xcscheme_^#shared#^_ 23 | 24 | orderHint 25 | 2 26 | 27 | 28 | SuppressBuildableAutocreation 29 | 30 | 15020AC320C0A9A300BBADBD 31 | 32 | primary 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 LiveUI 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 | -------------------------------------------------------------------------------- /Other/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LiveUI/Hagrid/d084df699a03a3880f47ca17d856e59b423dac71/Other/logo.png -------------------------------------------------------------------------------- /Other/screen1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LiveUI/Hagrid/d084df699a03a3880f47ca17d856e59b423dac71/Other/screen1.png -------------------------------------------------------------------------------- /Other/screen2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LiveUI/Hagrid/d084df699a03a3880f47ca17d856e59b423dac71/Other/screen2.png -------------------------------------------------------------------------------- /Other/screen3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LiveUI/Hagrid/d084df699a03a3880f47ca17d856e59b423dac71/Other/screen3.png -------------------------------------------------------------------------------- /Other/screen4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LiveUI/Hagrid/d084df699a03a3880f47ca17d856e59b423dac71/Other/screen4.png -------------------------------------------------------------------------------- /Other/screen5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LiveUI/Hagrid/d084df699a03a3880f47ca17d856e59b423dac71/Other/screen5.png -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment the next line to define a global platform for your project 2 | 3 | target 'Hagrid-Demo' do 4 | platform :ios, '10.3' 5 | use_frameworks! 6 | end 7 | -------------------------------------------------------------------------------- /Podfile.lock: -------------------------------------------------------------------------------- 1 | PODFILE CHECKSUM: ca1c3248371a0a654b6225e142d27027e73fe948 2 | 3 | COCOAPODS: 1.5.3 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Hagrid: Grid layout library for iOS, tvOS and macOS](https://github.com/LiveUI/Hagrid/raw/master/Other/logo.png) 2 | 3 | 4 | [![Platform](https://img.shields.io/cocoapods/p/Hagrid.svg?style=flat)](https://github.com/LiveUI/Hagrid) 5 | [![Cocoapods Compatible](https://img.shields.io/cocoapods/v/Hagrid.svg)](https://cocoapods.org/pods/Hagrid) 6 | [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) 7 | 8 | Brings a proper grid layout to the Apple platforms! 9 | 10 | 11 | 12 | 13 | 14 | 15 | 19 | 20 |
16 |
17 | 18 |
21 | 22 | > Use `gridView.config.displayGrid = true` to enable grid layout as per screenshots 23 | 24 | ## Available components 25 | 26 | ### Basic views 27 | * **GridView** - Basic grid view 28 | * **GridScrollView** - Grid view with all the properties available to a UIScrollView *(not available on macOS)* 29 | 30 | ### Table views 31 | * **GridTableViewCell** - UITableViewCell subclass with `gridView` being placed on `contentView` *(not available on macOS)* 32 | * **GridTableViewHeaderFooterView** - UITableViewHeaderFooterView subclass with `gridView` *(not available on macOS)* 33 | 34 | ### Collection views 35 | * **GridCollectionViewCell** - UICollectionViewCell subclass with `gridView` being placed on `contentView` *(not available on macOS)* 36 | * **GridCollectionReusableView** - UICollectionReusableView subclass with `gridView` *(not available on macOS)* 37 | 38 | ### View controllers 39 | * **GridViewController** - Basic Grid view based UIViewController *(not available on macOS)* 40 | * **GridScrollViewController** - Scroll grid view enabled view controller *(not available on macOS)* 41 | 42 | ## Usage 43 | 44 | ```swift 45 | // Add an album cover from the first column to 5 46 | // (please note we are using just a plain Int instead of the Position object below) 47 | // To make the album square we have used a custom closure with SnapKit's own `ConstraintMaker` (make) at the end of the method 48 | gridView.add(subview: albumCover, space: 5) { make in 49 | make.height.equalTo(self.albumCover.snp.width) 50 | } 51 | 52 | // Add album title label, stretching (12px padding) from the album cover image to the end of the grid view 53 | gridView.add(subview: albumTitleLabel, from: .relation(albumCover, margin: 12), space: .last) 54 | 55 | // Reusable position for the remaining two album labels. iPhones in portrait will take the rest of the screen, 56 | // on bigger devices or in landscape mode these labels finish two columns from the end of the grid view (.reversed(2)). 57 | let subtitlesLast: Position = .custom { traitCollection in 58 | return self.gridView.bounds.size.width <= 414 ? .last : .reversed(2) 59 | } 60 | 61 | // Place artist label below the album title with margin of 2px, match the left position of the album title label 62 | // and the previously calculated `subtitlesLast` for end position 63 | gridView.add(subview: artistLabel, .below(albumTitleLabel, margin: 2), from: .match(albumTitleLabel), space: subtitlesLast) 64 | 65 | // Place year released label under the artist name label, rest is same as above 66 | gridView.add(subview: yearLabel, .below(artistLabel, margin: 2), from: .match(artistLabel), space: subtitlesLast) 67 | 68 | // Place a purchase button next to the artist and year label on bigger screens and under on smaller ones (iPhone in portrait etc) 69 | // For the start position you either start 12px from the album on smaller screens or second column from the right on bigger ones 70 | // (note you can also use `UITraitCollection` in both cases if needed) 71 | gridView.add(subview: purchaseButton, .custom({ traitCollection in 72 | if self.gridView.bounds.size.width <= 414 { 73 | return .below(self.yearLabel, margin: 3) 74 | } else { 75 | return .match(self.artistLabel) 76 | } 77 | }), from: .custom({ _ in 78 | if self.gridView.bounds.size.width <= 414 { 79 | return .relation(self.albumCover, margin: 12) 80 | } else { 81 | return .relation(self.artistLabel, margin: 6) 82 | } 83 | }), space: .last) { make in 84 | make.height.equalTo(28) 85 | } 86 | 87 | // Place the star rating label above the separator. The initial position is not set and will be dynamic, the whole thing will stretch to the end 88 | gridView.add(subview: ratingLabel, .above(separator, margin: 12), from: .dynamic, space: .last) { make in 89 | make.height.equalTo(28) 90 | } 91 | 92 | // Add a 1px separator below any of the components that could reach it and space it 12px from the lowest one 93 | gridView.add(subview: separator, .below([albumCover, yearLabel, purchaseButton], margin: 12)) { make in 94 | make.height.equalTo(1) 95 | } 96 | 97 | // Add the long description copy which takes the full width and is placed below the separator 98 | gridView.add(subview: aboutLabel, .below(separator, margin: 12)) 99 | ``` 100 | 101 | ## Customisation 102 | 103 | There is a number of ways to customise your grid views: 104 | 105 | ### Number of columns 106 | 107 | Each grid view can have any number of columns, it is completely up to the developer to decide how many they want to use on which view. The default value is 12. 108 | 109 | ```swift 110 | // Set number of columns 111 | gridView.config.numberOfColumns = 24 112 | ``` 113 | 114 | ### Grid view outer padding 115 | 116 | You can set `top`, `left` or `right` padding for the entire grid view like this: 117 | 118 | ```swift 119 | gridView.config.padding = .full(top: 6, left: 12, right: 12) 120 | ``` 121 | 122 | ### Dynamic sizing 123 | 124 | You can enable dynamic sizing for the grid view by setting: 125 | 126 | ```swift 127 | gridView.config.automaticVerticalSizing = true 128 | ``` 129 | 130 | ## Positioning 131 | 132 | ### Padding 133 | Available paddings are: 134 | ```swift 135 | /// None 136 | .none 137 | 138 | /// Top 139 | .top(CGFloat) 140 | 141 | /// Left 142 | .left(CGFloat) 143 | 144 | /// Right 145 | .right(CGFloat) 146 | 147 | /// Sides (left, right) 148 | .horizontal(left: CGFloat, right: CGFloat) 149 | 150 | /// Full (top, left, right, bottom) 151 | .full(top: CGFloat, left: CGFloat, right: CGFloat) 152 | ``` 153 | 154 | ### Vertical positioning 155 | 156 | For vertical positioning you set a pixel value directly (`Int` or `Float`, later will be converted to `.exactly(fromTop: CGFloat)`) or use one of the following values: 157 | 158 | ```swift 159 | /// Top of the grid 160 | .toTop 161 | 162 | /// Exact value from the top 163 | .exactly(fromTop: CGFloat) 164 | 165 | /// Match top of another view 166 | .match(UIView, margin: CGFloat = 0) 167 | 168 | /// Maintains a position under a set of elements 169 | .below([UIView], margin: CGFloat = 0) 170 | 171 | /// Below another view (view, margin) 172 | .below(UIView, margin: CGFloat = 0) 173 | 174 | /// Above another view (view, margin) 175 | .above(UIView, margin: CGFloat = 0) 176 | 177 | /// Custom vertical position for a given size class (trait collection, not available on macOS) 178 | .custom(((_ traitCollection: UITraitCollection) -> Vertical)) 179 | ``` 180 | 181 | ### Horizontal positioning 182 | 183 | For horizontal positioning you can use an integer (`Int`) directly to specify the exact column or use one of the following methods: 184 | 185 | ```swift 186 | /// First column 187 | .first 188 | 189 | /// Specific column on the grid 190 | .col(Int) 191 | 192 | /// Last column on a grid view 193 | .last 194 | 195 | /// N-th column from the end 196 | .reversed(Int) 197 | 198 | /// Up to another element 199 | .relation(UIView, margin: CGFloat = 0) 200 | 201 | /// Match position of another view 202 | .match(UIView, margin: CGFloat = 0) 203 | 204 | /// Dynamic position 205 | .dynamic 206 | 207 | /// Custom position for a given size class (trait collection, not available on macOS) 208 | .custom(((_ traitCollection: UITraitCollection) -> Position)) 209 | ``` 210 | 211 | ## View controllers 212 | 213 | A `GridViewController` and `GridScrollViewController` are available for subclassing and your convenience with `gridView` property replacing the standard `view`. 214 | 215 | ```swift 216 | class MyViewController: GridViewController { } 217 | ``` 218 | 219 | ## Debugging 220 | 221 | To enable a background grid do the following: 222 | 223 | ```swift 224 | // Display grid 225 | gridView.config.displayGrid = true 226 | ``` 227 | 228 | ## Boost - an OpenSource Enterprise AppStore 229 | 230 | Core package for [Boost](http://www.boostappstore.com), a completely open source enterprise AppStore written in Swift! 231 | - Website: http://www.boostappstore.com 232 | - Github: https://github.com/LiveUI/Boost 233 | 234 | ## Code contributions 235 | 236 | We love PR’s, we can’t get enough of them ... so if you have an interesting improvement, bug-fix or a new feature please don’t hesitate to get in touch. If you are not sure about something before you start the development you can always contact our dev and product team through our Slack. 237 | 238 | ## Credits 239 | 240 | #### Author 241 | Ondrej Rafaj (@rafiki270 on [Github](https://github.com/rafiki270), [Twitter](https://twitter.com/rafiki270), [LiveUI Slack](http://bit.ly/2B0dEyt) and [Vapor Slack](https://vapor.team/)) 242 | 243 | #### Thanks 244 | To the SnapKit team for such an amazing tool! 245 | 246 | ## License 247 | 248 | MIT license; See the LICENSE file for more info. 249 | --------------------------------------------------------------------------------