├── .github
└── workflows
│ └── Tests.yml
├── .gitignore
├── .swiftlint.yml
├── .swiftpm
└── xcode
│ ├── package.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcuserdata
│ │ └── eon.xcuserdatad
│ │ └── UserInterfaceState.xcuserstate
│ └── xcuserdata
│ └── eon.xcuserdatad
│ └── xcschemes
│ └── xcschememanagement.plist
├── FlowLayout.xcodeproj
├── project.pbxproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ ├── xcshareddata
│ │ ├── IDEWorkspaceChecks.plist
│ │ └── swiftpm
│ │ │ └── Package.resolved
│ └── xcuserdata
│ │ ├── andrejorgensen.xcuserdatad
│ │ └── UserInterfaceState.xcuserstate
│ │ └── eon.xcuserdatad
│ │ └── UserInterfaceState.xcuserstate
├── xcshareddata
│ └── xcschemes
│ │ └── FlowLayout.xcscheme
└── xcuserdata
│ └── eon.xcuserdatad
│ ├── xcdebugger
│ └── Breakpoints_v2.xcbkptlist
│ └── xcschemes
│ └── xcschememanagement.plist
├── FlowLayout
├── AppDelegate.swift
├── Assets.xcassets
│ ├── AppIcon.appiconset
│ │ └── Contents.json
│ └── Contents.json
├── Base.lproj
│ └── LaunchScreen.storyboard
├── Info.plist
├── ViewController+Create.swift
└── ViewController.swift
├── LICENSE
├── Package.resolved
├── Package.swift
├── README.md
├── Sources
└── FlowLayout
│ ├── Header
│ ├── ButtonContainer
│ │ ├── ButtonContainer+Const.swift
│ │ ├── ButtonContainer+Create.swift
│ │ ├── ButtonContainer+Interaction.swift
│ │ ├── ButtonContainer+Setter.swift
│ │ ├── ButtonContainer+TypeAlias.swift
│ │ ├── ButtonContainer.swift
│ │ └── HeaderButton
│ │ │ ├── CustomButton.swift
│ │ │ ├── HeaderButton+Const.swift
│ │ │ ├── HeaderButton+Interactive.swift
│ │ │ ├── HeaderButton+Setter.swift
│ │ │ ├── HeaderButton+Type.swift
│ │ │ └── HeaderButton.swift
│ ├── Header+Const.swift
│ ├── Header+Create.swift
│ ├── Header+Setter.swift
│ ├── Header.swift
│ ├── HeaderTitle
│ │ ├── HeaderTitle+Const.swift
│ │ ├── HeaderTitle+Setter.swift
│ │ └── HeaderTitle.swift
│ └── Slider
│ │ ├── Slider+Animation.swift
│ │ ├── Slider+Const.swift
│ │ ├── Slider+Create.swift
│ │ ├── Slider+Setter.swift
│ │ ├── Slider.swift
│ │ └── SliderBar
│ │ └── SliderBar.swift
│ ├── HorView
│ ├── HorView+Collection.swift
│ ├── HorView+Create.swift
│ ├── HorView+Event.swift
│ ├── HorView+Getter.swift
│ ├── HorView+Register.swift
│ ├── HorView+Scroll.swift
│ ├── HorView+Setter.swift
│ ├── HorView+Style.swift
│ ├── HorView+Update.swift
│ ├── HorView.swift
│ └── utils
│ │ └── ColumnCellType.swift
│ ├── cell
│ └── horizontal
│ │ ├── HorCell
│ │ ├── HorCell+Collection.swift
│ │ ├── HorCell+Const.swift
│ │ ├── HorCell+Core.swift
│ │ ├── HorCell+Create.swift
│ │ ├── HorCell+Getter.swift
│ │ ├── HorCell+Scroll.swift
│ │ ├── HorCell+Type.swift
│ │ ├── HorCell+Update.swift
│ │ └── HorCell.swift
│ │ ├── VerCell
│ │ ├── VerCell.swift
│ │ └── custom
│ │ │ ├── ImageCell
│ │ │ ├── ImageCell+Create.swift
│ │ │ └── ImageCell.swift
│ │ │ └── PrimaryVerCell
│ │ │ ├── PrimaryVerCell+Core.swift
│ │ │ └── PrimaryVerCell.swift
│ │ └── custom
│ │ ├── PrimaryCellData
│ │ ├── CellDataKind.swift
│ │ └── PrimaryCellData.swift
│ │ ├── PrimaryHorCell
│ │ ├── PrimaryHorCell+Core.swift
│ │ ├── PrimaryHorCell+Getter.swift
│ │ ├── PrimaryHorCell+Interaction.swift
│ │ └── PrimaryHorCell.swift
│ │ ├── SecondaryHorCell
│ │ ├── SecondaryHorCell+Const.swift
│ │ ├── SecondaryHorCell+Create.swift
│ │ └── SecondaryHorCell.swift
│ │ └── TertiaryHorCell
│ │ └── TertiaryHorCell.swift
│ └── common
│ ├── CGShapeUtil.swift
│ ├── UIColorParser.swift
│ ├── UIView+Extension.swift
│ └── image
│ ├── UIImage+Extension.swift
│ └── UIImageView+Extension.swift
├── Tests
└── FlowLayoutTests
│ └── FlowLayoutTests.swift
└── demo
└── CustomView.swift
/.github/workflows/Tests.yml:
--------------------------------------------------------------------------------
1 | name: Tests
2 |
3 | on:
4 | push:
5 | branches: [ master ]
6 | pull_request:
7 | branches: [ master ]
8 | schedule:
9 | - cron: "0 20 * * 2-2"
10 |
11 | jobs:
12 | build:
13 |
14 | runs-on: macos-latest
15 |
16 | steps:
17 | - uses: actions/checkout@v2
18 | - name: Build
19 | run: swift build -v
20 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | /.build
3 | /Packages
4 | /*.xcodeproj
5 | xcuserdata/
6 |
--------------------------------------------------------------------------------
/.swiftlint.yml:
--------------------------------------------------------------------------------
1 | whitelist_rules:
2 | - anyobject_protocol
3 | - array_init
4 | #- attributes
5 | - block_based_kvo
6 | - class_delegate_protocol
7 | - closing_brace
8 | - closure_end_indentation
9 | - closure_parameter_position
10 | - closure_spacing
11 | - collection_alignment
12 | - colon
13 | - comma
14 | - compiler_protocol_init
15 | # - conditional_returns_on_newline
16 | - contains_over_first_not_nil
17 | - control_statement
18 | - deployment_target
19 | - discarded_notification_center_observer
20 | - discouraged_direct_init
21 | - discouraged_object_literal
22 | - discouraged_optional_boolean
23 | # - discouraged_optional_collection
24 | - duplicate_imports
25 | - dynamic_inline
26 | - empty_count
27 | - empty_enum_arguments
28 | - empty_parameters
29 | - empty_parentheses_with_trailing_closure
30 | - empty_string
31 | - empty_xctest_method
32 | - explicit_init
33 | - fallthrough
34 | - fatal_error_message
35 | - first_where
36 | - for_where
37 | - generic_type_name
38 | - identical_operands
39 | - identifier_name
40 | - implicit_getter
41 | - implicit_return
42 | - inert_defer
43 | - is_disjoint
44 | - joined_default_parameter
45 | - last_where
46 | - leading_whitespace
47 | - legacy_cggeometry_functions
48 | - legacy_constant
49 | - legacy_constructor
50 | - legacy_hashing
51 | - legacy_nsgeometry_functions
52 | - legacy_random
53 | - literal_expression_end_indentation
54 | - lower_acl_than_parent
55 | - mark
56 | - modifier_order
57 | - multiline_arguments
58 | # - multiline_function_chains
59 | - multiline_literal_brackets
60 | - multiline_parameters
61 | - multiline_parameters_brackets
62 | - multiple_closures_with_trailing_closure
63 | - nimble_operator
64 | - no_extension_access_modifier
65 | - no_fallthrough_only
66 | - notification_center_detachment
67 | - number_separator
68 | - object_literal
69 | - opening_brace
70 | - operator_usage_whitespace
71 | - operator_whitespace
72 | - overridden_super_call
73 | - pattern_matching_keywords
74 | - private_action
75 | # - private_outlet
76 | - private_unit_test
77 | - prohibited_super_call
78 | - protocol_property_accessors_order
79 | - redundant_discardable_let
80 | - redundant_nil_coalescing
81 | - redundant_objc_attribute
82 | - redundant_optional_initialization
83 | - redundant_set_access_control
84 | - redundant_string_enum_value
85 | - redundant_type_annotation
86 | - redundant_void_return
87 | - required_enum_case
88 | - return_arrow_whitespace
89 | - shorthand_operator
90 | - sorted_first_last
91 | # - statement_position
92 | - static_operator
93 | # - strong_iboutlet
94 | - superfluous_disable_command
95 | - switch_case_alignment
96 | # - switch_case_on_newline
97 | - syntactic_sugar
98 | - todo
99 | - toggle_bool
100 | - trailing_closure
101 | - trailing_comma
102 | - trailing_newline
103 | - trailing_semicolon
104 | - trailing_whitespace
105 | - type_name
106 | # - unavailable_function
107 | - unneeded_break_in_switch
108 | - unneeded_parentheses_in_closure_argument
109 | #- untyped_error_in_catch
110 | - unused_closure_parameter
111 | - unused_control_flow_label
112 | - unused_enumerated
113 | - unused_optional_binding
114 | - unused_setter_value
115 | - valid_ibinspectable
116 | - vertical_parameter_alignment
117 | - vertical_parameter_alignment_on_call
118 | - vertical_whitespace_closing_braces
119 | - vertical_whitespace_opening_braces
120 | - void_return
121 | - weak_computed_property
122 | - weak_delegate
123 | - xct_specific_matcher
124 | - xctfail_message
125 | - yoda_condition
126 | analyzer_rules:
127 | - unused_import
128 | - unused_private_declaration
129 | force_cast: warning
130 | force_unwrapping: warning
131 | number_separator:
132 | minimum_length: 5
133 | object_literal:
134 | image_literal: false
135 | discouraged_object_literal:
136 | color_literal: false
137 | identifier_name:
138 | max_length:
139 | warning: 100
140 | error: 100
141 | min_length:
142 | warning: 1
143 | error: 1
144 | validates_start_with_lowercase: false
145 | allowed_symbols:
146 | - '_'
147 | excluded:
148 | - 'x'
149 | - 'y'
150 | - 'a'
151 | - 'b'
152 | - 'x1'
153 | - 'x2'
154 | - 'y1'
155 | - 'y2'
156 | macOS_deployment_target: '10.12'
157 |
--------------------------------------------------------------------------------
/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.swiftpm/xcode/package.xcworkspace/xcuserdata/eon.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eonist/FlowLayout/f3148776c7852196b47383b92651661372402e3e/.swiftpm/xcode/package.xcworkspace/xcuserdata/eon.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/.swiftpm/xcode/xcuserdata/eon.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | FlowLayout.xcscheme_^#shared#^_
8 |
9 | orderHint
10 | 0
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/FlowLayout.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 52;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | F116EBD7211046B40091439E /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F116EBD6211046B40091439E /* AppDelegate.swift */; };
11 | F116EBDE211046B70091439E /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F116EBDD211046B70091439E /* Assets.xcassets */; };
12 | F116EBE1211046B70091439E /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F116EBDF211046B70091439E /* LaunchScreen.storyboard */; };
13 | F116EC3C2112E4A40091439E /* ViewController+Create.swift in Sources */ = {isa = PBXBuildFile; fileRef = F116EC3B2112E4A40091439E /* ViewController+Create.swift */; };
14 | F12441F52248001700D808D8 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F12441F42248001700D808D8 /* ViewController.swift */; };
15 | F12442442249031700D808D8 /* CustomView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F12442432249031700D808D8 /* CustomView.swift */; };
16 | F14965AB24323AAC008C1847 /* HorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F149655624323AAC008C1847 /* HorView.swift */; };
17 | F14965AC24323AAC008C1847 /* HorView+Collection.swift in Sources */ = {isa = PBXBuildFile; fileRef = F149655724323AAC008C1847 /* HorView+Collection.swift */; };
18 | F14965AD24323AAC008C1847 /* ColumnCellType.swift in Sources */ = {isa = PBXBuildFile; fileRef = F149655824323AAC008C1847 /* ColumnCellType.swift */; };
19 | F14965AE24323AAC008C1847 /* HorView+Style.swift in Sources */ = {isa = PBXBuildFile; fileRef = F149655924323AAC008C1847 /* HorView+Style.swift */; };
20 | F14965AF24323AAC008C1847 /* HorView+Create.swift in Sources */ = {isa = PBXBuildFile; fileRef = F149655A24323AAC008C1847 /* HorView+Create.swift */; };
21 | F14965B024323AAC008C1847 /* HorView+Register.swift in Sources */ = {isa = PBXBuildFile; fileRef = F149655B24323AAC008C1847 /* HorView+Register.swift */; };
22 | F14965B124323AAC008C1847 /* HorView+Getter.swift in Sources */ = {isa = PBXBuildFile; fileRef = F149655C24323AAC008C1847 /* HorView+Getter.swift */; };
23 | F14965B224323AAC008C1847 /* HorView+Scroll.swift in Sources */ = {isa = PBXBuildFile; fileRef = F149655D24323AAC008C1847 /* HorView+Scroll.swift */; };
24 | F14965B324323AAC008C1847 /* HorView+Update.swift in Sources */ = {isa = PBXBuildFile; fileRef = F149655E24323AAC008C1847 /* HorView+Update.swift */; };
25 | F14965B424323AAC008C1847 /* HorView+Setter.swift in Sources */ = {isa = PBXBuildFile; fileRef = F149655F24323AAC008C1847 /* HorView+Setter.swift */; };
26 | F14965B524323AAC008C1847 /* ImageCell+Create.swift in Sources */ = {isa = PBXBuildFile; fileRef = F149656524323AAC008C1847 /* ImageCell+Create.swift */; };
27 | F14965B624323AAC008C1847 /* ImageCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F149656624323AAC008C1847 /* ImageCell.swift */; };
28 | F14965B724323AAC008C1847 /* PrimaryVerCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F149656824323AAC008C1847 /* PrimaryVerCell.swift */; };
29 | F14965B824323AAC008C1847 /* PrimaryVerCell+Core.swift in Sources */ = {isa = PBXBuildFile; fileRef = F149656924323AAC008C1847 /* PrimaryVerCell+Core.swift */; };
30 | F14965B924323AAC008C1847 /* VerCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F149656A24323AAC008C1847 /* VerCell.swift */; };
31 | F14965BA24323AAC008C1847 /* HorCell+Update.swift in Sources */ = {isa = PBXBuildFile; fileRef = F149656C24323AAC008C1847 /* HorCell+Update.swift */; };
32 | F14965BB24323AAC008C1847 /* HorCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F149656D24323AAC008C1847 /* HorCell.swift */; };
33 | F14965BC24323AAC008C1847 /* HorCell+Collection.swift in Sources */ = {isa = PBXBuildFile; fileRef = F149656E24323AAC008C1847 /* HorCell+Collection.swift */; };
34 | F14965BD24323AAC008C1847 /* HorCell+Getter.swift in Sources */ = {isa = PBXBuildFile; fileRef = F149656F24323AAC008C1847 /* HorCell+Getter.swift */; };
35 | F14965BE24323AAC008C1847 /* HorCell+Scroll.swift in Sources */ = {isa = PBXBuildFile; fileRef = F149657024323AAC008C1847 /* HorCell+Scroll.swift */; };
36 | F14965BF24323AAC008C1847 /* HorCell+Const.swift in Sources */ = {isa = PBXBuildFile; fileRef = F149657124323AAC008C1847 /* HorCell+Const.swift */; };
37 | F14965C024323AAC008C1847 /* HorCell+Core.swift in Sources */ = {isa = PBXBuildFile; fileRef = F149657224323AAC008C1847 /* HorCell+Core.swift */; };
38 | F14965C124323AAC008C1847 /* HorCell+Type.swift in Sources */ = {isa = PBXBuildFile; fileRef = F149657324323AAC008C1847 /* HorCell+Type.swift */; };
39 | F14965C224323AAC008C1847 /* HorCell+Create.swift in Sources */ = {isa = PBXBuildFile; fileRef = F149657424323AAC008C1847 /* HorCell+Create.swift */; };
40 | F14965C324323AAC008C1847 /* SecondaryHorCell+Create.swift in Sources */ = {isa = PBXBuildFile; fileRef = F149657724323AAC008C1847 /* SecondaryHorCell+Create.swift */; };
41 | F14965C424323AAC008C1847 /* SecondaryHorCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F149657824323AAC008C1847 /* SecondaryHorCell.swift */; };
42 | F14965C524323AAC008C1847 /* SecondaryHorCell+Const.swift in Sources */ = {isa = PBXBuildFile; fileRef = F149657924323AAC008C1847 /* SecondaryHorCell+Const.swift */; };
43 | F14965C624323AAC008C1847 /* TertiaryHorCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F149657B24323AAC008C1847 /* TertiaryHorCell.swift */; };
44 | F14965C724323AAC008C1847 /* PrimaryHorCell+Interaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = F149657D24323AAC008C1847 /* PrimaryHorCell+Interaction.swift */; };
45 | F14965C824323AAC008C1847 /* PrimaryHorCell+Getter.swift in Sources */ = {isa = PBXBuildFile; fileRef = F149657E24323AAC008C1847 /* PrimaryHorCell+Getter.swift */; };
46 | F14965C924323AAC008C1847 /* PrimaryHorCell+Core.swift in Sources */ = {isa = PBXBuildFile; fileRef = F149657F24323AAC008C1847 /* PrimaryHorCell+Core.swift */; };
47 | F14965CA24323AAC008C1847 /* PrimaryHorCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F149658024323AAC008C1847 /* PrimaryHorCell.swift */; };
48 | F14965CB24323AAC008C1847 /* CellDataKind.swift in Sources */ = {isa = PBXBuildFile; fileRef = F149658224323AAC008C1847 /* CellDataKind.swift */; };
49 | F14965CC24323AAC008C1847 /* PrimaryCellData.swift in Sources */ = {isa = PBXBuildFile; fileRef = F149658324323AAC008C1847 /* PrimaryCellData.swift */; };
50 | F14965CD24323AAC008C1847 /* CGShapeUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = F149658524323AAC008C1847 /* CGShapeUtil.swift */; };
51 | F14965CE24323AAC008C1847 /* UIView+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F149658624323AAC008C1847 /* UIView+Extension.swift */; };
52 | F14965CF24323AAC008C1847 /* UIColorParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = F149658724323AAC008C1847 /* UIColorParser.swift */; };
53 | F14965D024323AAC008C1847 /* UIImageView+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F149658924323AAC008C1847 /* UIImageView+Extension.swift */; };
54 | F14965D124323AAC008C1847 /* UIImage+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F149658A24323AAC008C1847 /* UIImage+Extension.swift */; };
55 | F14965D224323AAC008C1847 /* SliderBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = F149658E24323AAC008C1847 /* SliderBar.swift */; };
56 | F14965D324323AAC008C1847 /* Slider+Setter.swift in Sources */ = {isa = PBXBuildFile; fileRef = F149658F24323AAC008C1847 /* Slider+Setter.swift */; };
57 | F14965D424323AAC008C1847 /* Slider+Animation.swift in Sources */ = {isa = PBXBuildFile; fileRef = F149659024323AAC008C1847 /* Slider+Animation.swift */; };
58 | F14965D524323AAC008C1847 /* Slider.swift in Sources */ = {isa = PBXBuildFile; fileRef = F149659124323AAC008C1847 /* Slider.swift */; };
59 | F14965D624323AAC008C1847 /* Slider+Create.swift in Sources */ = {isa = PBXBuildFile; fileRef = F149659224323AAC008C1847 /* Slider+Create.swift */; };
60 | F14965D724323AAC008C1847 /* Header+Create.swift in Sources */ = {isa = PBXBuildFile; fileRef = F149659324323AAC008C1847 /* Header+Create.swift */; };
61 | F14965D824323AAC008C1847 /* ButtonContainer+Setter.swift in Sources */ = {isa = PBXBuildFile; fileRef = F149659524323AAC008C1847 /* ButtonContainer+Setter.swift */; };
62 | F14965D924323AAC008C1847 /* HeaderButton+Const.swift in Sources */ = {isa = PBXBuildFile; fileRef = F149659724323AAC008C1847 /* HeaderButton+Const.swift */; };
63 | F14965DA24323AAC008C1847 /* CustomButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = F149659824323AAC008C1847 /* CustomButton.swift */; };
64 | F14965DB24323AAC008C1847 /* HeaderButton+Type.swift in Sources */ = {isa = PBXBuildFile; fileRef = F149659924323AAC008C1847 /* HeaderButton+Type.swift */; };
65 | F14965DC24323AAC008C1847 /* HeaderButton+Interactive.swift in Sources */ = {isa = PBXBuildFile; fileRef = F149659A24323AAC008C1847 /* HeaderButton+Interactive.swift */; };
66 | F14965DD24323AAC008C1847 /* HeaderButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = F149659B24323AAC008C1847 /* HeaderButton.swift */; };
67 | F14965DE24323AAC008C1847 /* HeaderButton+Setter.swift in Sources */ = {isa = PBXBuildFile; fileRef = F149659C24323AAC008C1847 /* HeaderButton+Setter.swift */; };
68 | F14965DF24323AAC008C1847 /* ButtonContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = F149659D24323AAC008C1847 /* ButtonContainer.swift */; };
69 | F14965E024323AAC008C1847 /* ButtonContainer+TypeAlias.swift in Sources */ = {isa = PBXBuildFile; fileRef = F149659E24323AAC008C1847 /* ButtonContainer+TypeAlias.swift */; };
70 | F14965E124323AAC008C1847 /* ButtonContainer+Create.swift in Sources */ = {isa = PBXBuildFile; fileRef = F149659F24323AAC008C1847 /* ButtonContainer+Create.swift */; };
71 | F14965E224323AAC008C1847 /* ButtonContainer+Interaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = F14965A024323AAC008C1847 /* ButtonContainer+Interaction.swift */; };
72 | F14965E324323AAC008C1847 /* ButtonContainer+Const.swift in Sources */ = {isa = PBXBuildFile; fileRef = F14965A124323AAC008C1847 /* ButtonContainer+Const.swift */; };
73 | F14965E424323AAC008C1847 /* Header.swift in Sources */ = {isa = PBXBuildFile; fileRef = F14965A224323AAC008C1847 /* Header.swift */; };
74 | F14965E524323AAC008C1847 /* Header+Const.swift in Sources */ = {isa = PBXBuildFile; fileRef = F14965A524323AAC008C1847 /* Header+Const.swift */; };
75 | F14965E624323AAC008C1847 /* HeaderTitle.swift in Sources */ = {isa = PBXBuildFile; fileRef = F14965A724323AAC008C1847 /* HeaderTitle.swift */; };
76 | F14965E724323AAC008C1847 /* HeaderTitle+Setter.swift in Sources */ = {isa = PBXBuildFile; fileRef = F14965A824323AAC008C1847 /* HeaderTitle+Setter.swift */; };
77 | F14965E824323AAC008C1847 /* HeaderTitle+Const.swift in Sources */ = {isa = PBXBuildFile; fileRef = F14965A924323AAC008C1847 /* HeaderTitle+Const.swift */; };
78 | F14965E924323AAC008C1847 /* Header+Setter.swift in Sources */ = {isa = PBXBuildFile; fileRef = F14965AA24323AAC008C1847 /* Header+Setter.swift */; };
79 | F14965EC24323B0A008C1847 /* JSONSugar in Frameworks */ = {isa = PBXBuildFile; productRef = F14965EB24323B0A008C1847 /* JSONSugar */; };
80 | F14965EF24323B22008C1847 /* ImageSugar in Frameworks */ = {isa = PBXBuildFile; productRef = F14965EE24323B22008C1847 /* ImageSugar */; };
81 | F14965F224323D2B008C1847 /* CommonCell in Frameworks */ = {isa = PBXBuildFile; productRef = F14965F124323D2B008C1847 /* CommonCell */; };
82 | F14965F524323D7B008C1847 /* NetworkSugar in Frameworks */ = {isa = PBXBuildFile; productRef = F14965F424323D7B008C1847 /* NetworkSugar */; };
83 | F15CB292268D262D00C5C303 /* Spatial in Frameworks */ = {isa = PBXBuildFile; productRef = F15CB291268D262D00C5C303 /* Spatial */; };
84 | F15CB295268D265200C5C303 /* With in Frameworks */ = {isa = PBXBuildFile; productRef = F15CB294268D265200C5C303 /* With */; };
85 | F1FB41582443556000A433E2 /* HorView+Event.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1FB41572443556000A433E2 /* HorView+Event.swift */; };
86 | F1FB415A2443599600A433E2 /* Slider+Const.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1FB41592443599600A433E2 /* Slider+Const.swift */; };
87 | /* End PBXBuildFile section */
88 |
89 | /* Begin PBXCopyFilesBuildPhase section */
90 | F12441E82247F0F800D808D8 /* Embed Frameworks */ = {
91 | isa = PBXCopyFilesBuildPhase;
92 | buildActionMask = 2147483647;
93 | dstPath = "";
94 | dstSubfolderSpec = 10;
95 | files = (
96 | );
97 | name = "Embed Frameworks";
98 | runOnlyForDeploymentPostprocessing = 0;
99 | };
100 | /* End PBXCopyFilesBuildPhase section */
101 |
102 | /* Begin PBXFileReference section */
103 | F116EBD3211046B40091439E /* FlowLayout.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = FlowLayout.app; sourceTree = BUILT_PRODUCTS_DIR; };
104 | F116EBD6211046B40091439E /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
105 | F116EBDD211046B70091439E /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
106 | F116EBE0211046B70091439E /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
107 | F116EBE2211046B70091439E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
108 | F116EC3B2112E4A40091439E /* ViewController+Create.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ViewController+Create.swift"; sourceTree = ""; };
109 | F12441F42248001700D808D8 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; };
110 | F12442432249031700D808D8 /* CustomView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomView.swift; sourceTree = ""; };
111 | F149655624323AAC008C1847 /* HorView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HorView.swift; sourceTree = ""; };
112 | F149655724323AAC008C1847 /* HorView+Collection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "HorView+Collection.swift"; sourceTree = ""; };
113 | F149655824323AAC008C1847 /* ColumnCellType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ColumnCellType.swift; sourceTree = ""; };
114 | F149655924323AAC008C1847 /* HorView+Style.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "HorView+Style.swift"; sourceTree = ""; };
115 | F149655A24323AAC008C1847 /* HorView+Create.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "HorView+Create.swift"; sourceTree = ""; };
116 | F149655B24323AAC008C1847 /* HorView+Register.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "HorView+Register.swift"; sourceTree = ""; };
117 | F149655C24323AAC008C1847 /* HorView+Getter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "HorView+Getter.swift"; sourceTree = ""; };
118 | F149655D24323AAC008C1847 /* HorView+Scroll.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "HorView+Scroll.swift"; sourceTree = ""; };
119 | F149655E24323AAC008C1847 /* HorView+Update.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "HorView+Update.swift"; sourceTree = ""; };
120 | F149655F24323AAC008C1847 /* HorView+Setter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "HorView+Setter.swift"; sourceTree = ""; };
121 | F149656524323AAC008C1847 /* ImageCell+Create.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ImageCell+Create.swift"; sourceTree = ""; };
122 | F149656624323AAC008C1847 /* ImageCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageCell.swift; sourceTree = ""; };
123 | F149656824323AAC008C1847 /* PrimaryVerCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PrimaryVerCell.swift; sourceTree = ""; };
124 | F149656924323AAC008C1847 /* PrimaryVerCell+Core.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "PrimaryVerCell+Core.swift"; sourceTree = ""; };
125 | F149656A24323AAC008C1847 /* VerCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VerCell.swift; sourceTree = ""; };
126 | F149656C24323AAC008C1847 /* HorCell+Update.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "HorCell+Update.swift"; sourceTree = ""; };
127 | F149656D24323AAC008C1847 /* HorCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HorCell.swift; sourceTree = ""; };
128 | F149656E24323AAC008C1847 /* HorCell+Collection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "HorCell+Collection.swift"; sourceTree = ""; };
129 | F149656F24323AAC008C1847 /* HorCell+Getter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "HorCell+Getter.swift"; sourceTree = ""; };
130 | F149657024323AAC008C1847 /* HorCell+Scroll.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "HorCell+Scroll.swift"; sourceTree = ""; };
131 | F149657124323AAC008C1847 /* HorCell+Const.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "HorCell+Const.swift"; sourceTree = ""; };
132 | F149657224323AAC008C1847 /* HorCell+Core.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "HorCell+Core.swift"; sourceTree = ""; };
133 | F149657324323AAC008C1847 /* HorCell+Type.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "HorCell+Type.swift"; sourceTree = ""; };
134 | F149657424323AAC008C1847 /* HorCell+Create.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "HorCell+Create.swift"; sourceTree = ""; };
135 | F149657724323AAC008C1847 /* SecondaryHorCell+Create.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "SecondaryHorCell+Create.swift"; sourceTree = ""; };
136 | F149657824323AAC008C1847 /* SecondaryHorCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SecondaryHorCell.swift; sourceTree = ""; };
137 | F149657924323AAC008C1847 /* SecondaryHorCell+Const.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "SecondaryHorCell+Const.swift"; sourceTree = ""; };
138 | F149657B24323AAC008C1847 /* TertiaryHorCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TertiaryHorCell.swift; sourceTree = ""; };
139 | F149657D24323AAC008C1847 /* PrimaryHorCell+Interaction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "PrimaryHorCell+Interaction.swift"; sourceTree = ""; };
140 | F149657E24323AAC008C1847 /* PrimaryHorCell+Getter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "PrimaryHorCell+Getter.swift"; sourceTree = ""; };
141 | F149657F24323AAC008C1847 /* PrimaryHorCell+Core.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "PrimaryHorCell+Core.swift"; sourceTree = ""; };
142 | F149658024323AAC008C1847 /* PrimaryHorCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PrimaryHorCell.swift; sourceTree = ""; };
143 | F149658224323AAC008C1847 /* CellDataKind.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CellDataKind.swift; sourceTree = ""; };
144 | F149658324323AAC008C1847 /* PrimaryCellData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PrimaryCellData.swift; sourceTree = ""; };
145 | F149658524323AAC008C1847 /* CGShapeUtil.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CGShapeUtil.swift; sourceTree = ""; };
146 | F149658624323AAC008C1847 /* UIView+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIView+Extension.swift"; sourceTree = ""; };
147 | F149658724323AAC008C1847 /* UIColorParser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIColorParser.swift; sourceTree = ""; };
148 | F149658924323AAC008C1847 /* UIImageView+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIImageView+Extension.swift"; sourceTree = ""; };
149 | F149658A24323AAC008C1847 /* UIImage+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIImage+Extension.swift"; sourceTree = ""; };
150 | F149658E24323AAC008C1847 /* SliderBar.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SliderBar.swift; sourceTree = ""; };
151 | F149658F24323AAC008C1847 /* Slider+Setter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Slider+Setter.swift"; sourceTree = ""; };
152 | F149659024323AAC008C1847 /* Slider+Animation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Slider+Animation.swift"; sourceTree = ""; };
153 | F149659124323AAC008C1847 /* Slider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Slider.swift; sourceTree = ""; };
154 | F149659224323AAC008C1847 /* Slider+Create.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Slider+Create.swift"; sourceTree = ""; };
155 | F149659324323AAC008C1847 /* Header+Create.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Header+Create.swift"; sourceTree = ""; };
156 | F149659524323AAC008C1847 /* ButtonContainer+Setter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ButtonContainer+Setter.swift"; sourceTree = ""; };
157 | F149659724323AAC008C1847 /* HeaderButton+Const.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "HeaderButton+Const.swift"; sourceTree = ""; };
158 | F149659824323AAC008C1847 /* CustomButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomButton.swift; sourceTree = ""; };
159 | F149659924323AAC008C1847 /* HeaderButton+Type.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "HeaderButton+Type.swift"; sourceTree = ""; };
160 | F149659A24323AAC008C1847 /* HeaderButton+Interactive.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "HeaderButton+Interactive.swift"; sourceTree = ""; };
161 | F149659B24323AAC008C1847 /* HeaderButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HeaderButton.swift; sourceTree = ""; };
162 | F149659C24323AAC008C1847 /* HeaderButton+Setter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "HeaderButton+Setter.swift"; sourceTree = ""; };
163 | F149659D24323AAC008C1847 /* ButtonContainer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ButtonContainer.swift; sourceTree = ""; };
164 | F149659E24323AAC008C1847 /* ButtonContainer+TypeAlias.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ButtonContainer+TypeAlias.swift"; sourceTree = ""; };
165 | F149659F24323AAC008C1847 /* ButtonContainer+Create.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ButtonContainer+Create.swift"; sourceTree = ""; };
166 | F14965A024323AAC008C1847 /* ButtonContainer+Interaction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ButtonContainer+Interaction.swift"; sourceTree = ""; };
167 | F14965A124323AAC008C1847 /* ButtonContainer+Const.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ButtonContainer+Const.swift"; sourceTree = ""; };
168 | F14965A224323AAC008C1847 /* Header.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Header.swift; sourceTree = ""; };
169 | F14965A524323AAC008C1847 /* Header+Const.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Header+Const.swift"; sourceTree = ""; };
170 | F14965A724323AAC008C1847 /* HeaderTitle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HeaderTitle.swift; sourceTree = ""; };
171 | F14965A824323AAC008C1847 /* HeaderTitle+Setter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "HeaderTitle+Setter.swift"; sourceTree = ""; };
172 | F14965A924323AAC008C1847 /* HeaderTitle+Const.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "HeaderTitle+Const.swift"; sourceTree = ""; };
173 | F14965AA24323AAC008C1847 /* Header+Setter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Header+Setter.swift"; sourceTree = ""; };
174 | F1FB41572443556000A433E2 /* HorView+Event.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "HorView+Event.swift"; sourceTree = ""; };
175 | F1FB41592443599600A433E2 /* Slider+Const.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Slider+Const.swift"; sourceTree = ""; };
176 | /* End PBXFileReference section */
177 |
178 | /* Begin PBXFrameworksBuildPhase section */
179 | F116EBD0211046B40091439E /* Frameworks */ = {
180 | isa = PBXFrameworksBuildPhase;
181 | buildActionMask = 2147483647;
182 | files = (
183 | F15CB295268D265200C5C303 /* With in Frameworks */,
184 | F14965F524323D7B008C1847 /* NetworkSugar in Frameworks */,
185 | F14965F224323D2B008C1847 /* CommonCell in Frameworks */,
186 | F15CB292268D262D00C5C303 /* Spatial in Frameworks */,
187 | F14965EC24323B0A008C1847 /* JSONSugar in Frameworks */,
188 | F14965EF24323B22008C1847 /* ImageSugar in Frameworks */,
189 | );
190 | runOnlyForDeploymentPostprocessing = 0;
191 | };
192 | /* End PBXFrameworksBuildPhase section */
193 |
194 | /* Begin PBXGroup section */
195 | F116EBCA211046B40091439E = {
196 | isa = PBXGroup;
197 | children = (
198 | F149655324323AAC008C1847 /* Sources */,
199 | F1244242224902C600D808D8 /* demo */,
200 | F116EBD5211046B40091439E /* FlowLayout */,
201 | F116EBD4211046B40091439E /* Products */,
202 | F124420F2248314E00D808D8 /* Frameworks */,
203 | );
204 | sourceTree = "";
205 | };
206 | F116EBD4211046B40091439E /* Products */ = {
207 | isa = PBXGroup;
208 | children = (
209 | F116EBD3211046B40091439E /* FlowLayout.app */,
210 | );
211 | name = Products;
212 | sourceTree = "";
213 | };
214 | F116EBD5211046B40091439E /* FlowLayout */ = {
215 | isa = PBXGroup;
216 | children = (
217 | F116EBD6211046B40091439E /* AppDelegate.swift */,
218 | F12441F42248001700D808D8 /* ViewController.swift */,
219 | F116EC3B2112E4A40091439E /* ViewController+Create.swift */,
220 | F116EBDD211046B70091439E /* Assets.xcassets */,
221 | F116EBDF211046B70091439E /* LaunchScreen.storyboard */,
222 | F116EBE2211046B70091439E /* Info.plist */,
223 | );
224 | path = FlowLayout;
225 | sourceTree = "";
226 | };
227 | F124420F2248314E00D808D8 /* Frameworks */ = {
228 | isa = PBXGroup;
229 | children = (
230 | );
231 | name = Frameworks;
232 | sourceTree = "";
233 | };
234 | F1244242224902C600D808D8 /* demo */ = {
235 | isa = PBXGroup;
236 | children = (
237 | F12442432249031700D808D8 /* CustomView.swift */,
238 | );
239 | path = demo;
240 | sourceTree = "";
241 | };
242 | F149655324323AAC008C1847 /* Sources */ = {
243 | isa = PBXGroup;
244 | children = (
245 | F149655424323AAC008C1847 /* FlowLayout */,
246 | );
247 | path = Sources;
248 | sourceTree = "";
249 | };
250 | F149655424323AAC008C1847 /* FlowLayout */ = {
251 | isa = PBXGroup;
252 | children = (
253 | F149655524323AAC008C1847 /* HorView */,
254 | F149656024323AAC008C1847 /* cell */,
255 | F149658424323AAC008C1847 /* common */,
256 | F149658B24323AAC008C1847 /* Header */,
257 | );
258 | path = FlowLayout;
259 | sourceTree = "";
260 | };
261 | F149655524323AAC008C1847 /* HorView */ = {
262 | isa = PBXGroup;
263 | children = (
264 | F15CB28F268D1F3D00C5C303 /* utils */,
265 | F149655624323AAC008C1847 /* HorView.swift */,
266 | F149655724323AAC008C1847 /* HorView+Collection.swift */,
267 | F149655924323AAC008C1847 /* HorView+Style.swift */,
268 | F149655A24323AAC008C1847 /* HorView+Create.swift */,
269 | F149655B24323AAC008C1847 /* HorView+Register.swift */,
270 | F149655C24323AAC008C1847 /* HorView+Getter.swift */,
271 | F149655D24323AAC008C1847 /* HorView+Scroll.swift */,
272 | F1FB41572443556000A433E2 /* HorView+Event.swift */,
273 | F149655E24323AAC008C1847 /* HorView+Update.swift */,
274 | F149655F24323AAC008C1847 /* HorView+Setter.swift */,
275 | );
276 | path = HorView;
277 | sourceTree = "";
278 | };
279 | F149656024323AAC008C1847 /* cell */ = {
280 | isa = PBXGroup;
281 | children = (
282 | F149656124323AAC008C1847 /* horizontal */,
283 | );
284 | path = cell;
285 | sourceTree = "";
286 | };
287 | F149656124323AAC008C1847 /* horizontal */ = {
288 | isa = PBXGroup;
289 | children = (
290 | F149656224323AAC008C1847 /* VerCell */,
291 | F149656B24323AAC008C1847 /* HorCell */,
292 | F149657524323AAC008C1847 /* custom */,
293 | );
294 | path = horizontal;
295 | sourceTree = "";
296 | };
297 | F149656224323AAC008C1847 /* VerCell */ = {
298 | isa = PBXGroup;
299 | children = (
300 | F149656324323AAC008C1847 /* custom */,
301 | F149656A24323AAC008C1847 /* VerCell.swift */,
302 | );
303 | path = VerCell;
304 | sourceTree = "";
305 | };
306 | F149656324323AAC008C1847 /* custom */ = {
307 | isa = PBXGroup;
308 | children = (
309 | F149656424323AAC008C1847 /* ImageCell */,
310 | F149656724323AAC008C1847 /* PrimaryVerCell */,
311 | );
312 | path = custom;
313 | sourceTree = "";
314 | };
315 | F149656424323AAC008C1847 /* ImageCell */ = {
316 | isa = PBXGroup;
317 | children = (
318 | F149656524323AAC008C1847 /* ImageCell+Create.swift */,
319 | F149656624323AAC008C1847 /* ImageCell.swift */,
320 | );
321 | path = ImageCell;
322 | sourceTree = "";
323 | };
324 | F149656724323AAC008C1847 /* PrimaryVerCell */ = {
325 | isa = PBXGroup;
326 | children = (
327 | F149656824323AAC008C1847 /* PrimaryVerCell.swift */,
328 | F149656924323AAC008C1847 /* PrimaryVerCell+Core.swift */,
329 | );
330 | path = PrimaryVerCell;
331 | sourceTree = "";
332 | };
333 | F149656B24323AAC008C1847 /* HorCell */ = {
334 | isa = PBXGroup;
335 | children = (
336 | F149656D24323AAC008C1847 /* HorCell.swift */,
337 | F149656C24323AAC008C1847 /* HorCell+Update.swift */,
338 | F149656E24323AAC008C1847 /* HorCell+Collection.swift */,
339 | F149656F24323AAC008C1847 /* HorCell+Getter.swift */,
340 | F149657024323AAC008C1847 /* HorCell+Scroll.swift */,
341 | F149657124323AAC008C1847 /* HorCell+Const.swift */,
342 | F149657224323AAC008C1847 /* HorCell+Core.swift */,
343 | F149657324323AAC008C1847 /* HorCell+Type.swift */,
344 | F149657424323AAC008C1847 /* HorCell+Create.swift */,
345 | );
346 | path = HorCell;
347 | sourceTree = "";
348 | };
349 | F149657524323AAC008C1847 /* custom */ = {
350 | isa = PBXGroup;
351 | children = (
352 | F149657624323AAC008C1847 /* SecondaryHorCell */,
353 | F149657A24323AAC008C1847 /* TertiaryHorCell */,
354 | F149657C24323AAC008C1847 /* PrimaryHorCell */,
355 | F149658124323AAC008C1847 /* PrimaryCellData */,
356 | );
357 | path = custom;
358 | sourceTree = "";
359 | };
360 | F149657624323AAC008C1847 /* SecondaryHorCell */ = {
361 | isa = PBXGroup;
362 | children = (
363 | F149657724323AAC008C1847 /* SecondaryHorCell+Create.swift */,
364 | F149657824323AAC008C1847 /* SecondaryHorCell.swift */,
365 | F149657924323AAC008C1847 /* SecondaryHorCell+Const.swift */,
366 | );
367 | path = SecondaryHorCell;
368 | sourceTree = "";
369 | };
370 | F149657A24323AAC008C1847 /* TertiaryHorCell */ = {
371 | isa = PBXGroup;
372 | children = (
373 | F149657B24323AAC008C1847 /* TertiaryHorCell.swift */,
374 | );
375 | path = TertiaryHorCell;
376 | sourceTree = "";
377 | };
378 | F149657C24323AAC008C1847 /* PrimaryHorCell */ = {
379 | isa = PBXGroup;
380 | children = (
381 | F149657D24323AAC008C1847 /* PrimaryHorCell+Interaction.swift */,
382 | F149657E24323AAC008C1847 /* PrimaryHorCell+Getter.swift */,
383 | F149657F24323AAC008C1847 /* PrimaryHorCell+Core.swift */,
384 | F149658024323AAC008C1847 /* PrimaryHorCell.swift */,
385 | );
386 | path = PrimaryHorCell;
387 | sourceTree = "";
388 | };
389 | F149658124323AAC008C1847 /* PrimaryCellData */ = {
390 | isa = PBXGroup;
391 | children = (
392 | F149658224323AAC008C1847 /* CellDataKind.swift */,
393 | F149658324323AAC008C1847 /* PrimaryCellData.swift */,
394 | );
395 | path = PrimaryCellData;
396 | sourceTree = "";
397 | };
398 | F149658424323AAC008C1847 /* common */ = {
399 | isa = PBXGroup;
400 | children = (
401 | F149658524323AAC008C1847 /* CGShapeUtil.swift */,
402 | F149658624323AAC008C1847 /* UIView+Extension.swift */,
403 | F149658724323AAC008C1847 /* UIColorParser.swift */,
404 | F149658824323AAC008C1847 /* image */,
405 | );
406 | path = common;
407 | sourceTree = "";
408 | };
409 | F149658824323AAC008C1847 /* image */ = {
410 | isa = PBXGroup;
411 | children = (
412 | F149658924323AAC008C1847 /* UIImageView+Extension.swift */,
413 | F149658A24323AAC008C1847 /* UIImage+Extension.swift */,
414 | );
415 | path = image;
416 | sourceTree = "";
417 | };
418 | F149658B24323AAC008C1847 /* Header */ = {
419 | isa = PBXGroup;
420 | children = (
421 | F149658C24323AAC008C1847 /* Slider */,
422 | F149659424323AAC008C1847 /* ButtonContainer */,
423 | F14965A624323AAC008C1847 /* HeaderTitle */,
424 | F14965A224323AAC008C1847 /* Header.swift */,
425 | F14965AA24323AAC008C1847 /* Header+Setter.swift */,
426 | F149659324323AAC008C1847 /* Header+Create.swift */,
427 | F14965A524323AAC008C1847 /* Header+Const.swift */,
428 | );
429 | path = Header;
430 | sourceTree = "";
431 | };
432 | F149658C24323AAC008C1847 /* Slider */ = {
433 | isa = PBXGroup;
434 | children = (
435 | F149658D24323AAC008C1847 /* SliderBar */,
436 | F149659124323AAC008C1847 /* Slider.swift */,
437 | F149658F24323AAC008C1847 /* Slider+Setter.swift */,
438 | F149659024323AAC008C1847 /* Slider+Animation.swift */,
439 | F149659224323AAC008C1847 /* Slider+Create.swift */,
440 | F1FB41592443599600A433E2 /* Slider+Const.swift */,
441 | );
442 | path = Slider;
443 | sourceTree = "";
444 | };
445 | F149658D24323AAC008C1847 /* SliderBar */ = {
446 | isa = PBXGroup;
447 | children = (
448 | F149658E24323AAC008C1847 /* SliderBar.swift */,
449 | );
450 | path = SliderBar;
451 | sourceTree = "";
452 | };
453 | F149659424323AAC008C1847 /* ButtonContainer */ = {
454 | isa = PBXGroup;
455 | children = (
456 | F149659624323AAC008C1847 /* HeaderButton */,
457 | F149659D24323AAC008C1847 /* ButtonContainer.swift */,
458 | F149659524323AAC008C1847 /* ButtonContainer+Setter.swift */,
459 | F149659E24323AAC008C1847 /* ButtonContainer+TypeAlias.swift */,
460 | F149659F24323AAC008C1847 /* ButtonContainer+Create.swift */,
461 | F14965A024323AAC008C1847 /* ButtonContainer+Interaction.swift */,
462 | F14965A124323AAC008C1847 /* ButtonContainer+Const.swift */,
463 | );
464 | path = ButtonContainer;
465 | sourceTree = "";
466 | };
467 | F149659624323AAC008C1847 /* HeaderButton */ = {
468 | isa = PBXGroup;
469 | children = (
470 | F149659824323AAC008C1847 /* CustomButton.swift */,
471 | F149659B24323AAC008C1847 /* HeaderButton.swift */,
472 | F149659724323AAC008C1847 /* HeaderButton+Const.swift */,
473 | F149659924323AAC008C1847 /* HeaderButton+Type.swift */,
474 | F149659A24323AAC008C1847 /* HeaderButton+Interactive.swift */,
475 | F149659C24323AAC008C1847 /* HeaderButton+Setter.swift */,
476 | );
477 | path = HeaderButton;
478 | sourceTree = "";
479 | };
480 | F14965A624323AAC008C1847 /* HeaderTitle */ = {
481 | isa = PBXGroup;
482 | children = (
483 | F14965A724323AAC008C1847 /* HeaderTitle.swift */,
484 | F14965A824323AAC008C1847 /* HeaderTitle+Setter.swift */,
485 | F14965A924323AAC008C1847 /* HeaderTitle+Const.swift */,
486 | );
487 | path = HeaderTitle;
488 | sourceTree = "";
489 | };
490 | F15CB28F268D1F3D00C5C303 /* utils */ = {
491 | isa = PBXGroup;
492 | children = (
493 | F149655824323AAC008C1847 /* ColumnCellType.swift */,
494 | );
495 | path = utils;
496 | sourceTree = "";
497 | };
498 | /* End PBXGroup section */
499 |
500 | /* Begin PBXNativeTarget section */
501 | F116EBD2211046B40091439E /* FlowLayout */ = {
502 | isa = PBXNativeTarget;
503 | buildConfigurationList = F116EBE5211046B70091439E /* Build configuration list for PBXNativeTarget "FlowLayout" */;
504 | buildPhases = (
505 | F116EBCF211046B40091439E /* Sources */,
506 | F116EBD0211046B40091439E /* Frameworks */,
507 | F116EBD1211046B40091439E /* Resources */,
508 | F12441E82247F0F800D808D8 /* Embed Frameworks */,
509 | 9D05884322F326C400F0A2BE /* ShellScript */,
510 | );
511 | buildRules = (
512 | );
513 | dependencies = (
514 | );
515 | name = FlowLayout;
516 | packageProductDependencies = (
517 | F14965EB24323B0A008C1847 /* JSONSugar */,
518 | F14965EE24323B22008C1847 /* ImageSugar */,
519 | F14965F124323D2B008C1847 /* CommonCell */,
520 | F14965F424323D7B008C1847 /* NetworkSugar */,
521 | F15CB291268D262D00C5C303 /* Spatial */,
522 | F15CB294268D265200C5C303 /* With */,
523 | );
524 | productName = FlowLayout;
525 | productReference = F116EBD3211046B40091439E /* FlowLayout.app */;
526 | productType = "com.apple.product-type.application";
527 | };
528 | /* End PBXNativeTarget section */
529 |
530 | /* Begin PBXProject section */
531 | F116EBCB211046B40091439E /* Project object */ = {
532 | isa = PBXProject;
533 | attributes = {
534 | LastSwiftUpdateCheck = 0940;
535 | LastUpgradeCheck = 1240;
536 | ORGANIZATIONNAME = eon;
537 | TargetAttributes = {
538 | F116EBD2211046B40091439E = {
539 | CreatedOnToolsVersion = 9.4.1;
540 | LastSwiftMigration = 1010;
541 | };
542 | };
543 | };
544 | buildConfigurationList = F116EBCE211046B40091439E /* Build configuration list for PBXProject "FlowLayout" */;
545 | compatibilityVersion = "Xcode 9.3";
546 | developmentRegion = en;
547 | hasScannedForEncodings = 0;
548 | knownRegions = (
549 | en,
550 | Base,
551 | );
552 | mainGroup = F116EBCA211046B40091439E;
553 | packageReferences = (
554 | F14965EA24323B0A008C1847 /* XCRemoteSwiftPackageReference "JSONSugar" */,
555 | F14965ED24323B22008C1847 /* XCRemoteSwiftPackageReference "ImageSugar" */,
556 | F14965F024323D2B008C1847 /* XCRemoteSwiftPackageReference "CommonCell" */,
557 | F14965F324323D7B008C1847 /* XCRemoteSwiftPackageReference "NetworkSugar" */,
558 | F15CB290268D262D00C5C303 /* XCRemoteSwiftPackageReference "Spatial" */,
559 | F15CB293268D265200C5C303 /* XCRemoteSwiftPackageReference "with" */,
560 | );
561 | productRefGroup = F116EBD4211046B40091439E /* Products */;
562 | projectDirPath = "";
563 | projectRoot = "";
564 | targets = (
565 | F116EBD2211046B40091439E /* FlowLayout */,
566 | );
567 | };
568 | /* End PBXProject section */
569 |
570 | /* Begin PBXResourcesBuildPhase section */
571 | F116EBD1211046B40091439E /* Resources */ = {
572 | isa = PBXResourcesBuildPhase;
573 | buildActionMask = 2147483647;
574 | files = (
575 | F116EBE1211046B70091439E /* LaunchScreen.storyboard in Resources */,
576 | F116EBDE211046B70091439E /* Assets.xcassets in Resources */,
577 | );
578 | runOnlyForDeploymentPostprocessing = 0;
579 | };
580 | /* End PBXResourcesBuildPhase section */
581 |
582 | /* Begin PBXShellScriptBuildPhase section */
583 | 9D05884322F326C400F0A2BE /* ShellScript */ = {
584 | isa = PBXShellScriptBuildPhase;
585 | buildActionMask = 2147483647;
586 | files = (
587 | );
588 | inputFileListPaths = (
589 | );
590 | inputPaths = (
591 | );
592 | outputFileListPaths = (
593 | );
594 | outputPaths = (
595 | );
596 | runOnlyForDeploymentPostprocessing = 0;
597 | shellPath = /bin/sh;
598 | shellScript = "if which swiftlint >/dev/null; then\nswiftlint\nelse\necho \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi\n";
599 | };
600 | /* End PBXShellScriptBuildPhase section */
601 |
602 | /* Begin PBXSourcesBuildPhase section */
603 | F116EBCF211046B40091439E /* Sources */ = {
604 | isa = PBXSourcesBuildPhase;
605 | buildActionMask = 2147483647;
606 | files = (
607 | F14965DA24323AAC008C1847 /* CustomButton.swift in Sources */,
608 | F14965E324323AAC008C1847 /* ButtonContainer+Const.swift in Sources */,
609 | F14965E424323AAC008C1847 /* Header.swift in Sources */,
610 | F14965CA24323AAC008C1847 /* PrimaryHorCell.swift in Sources */,
611 | F14965BD24323AAC008C1847 /* HorCell+Getter.swift in Sources */,
612 | F14965D324323AAC008C1847 /* Slider+Setter.swift in Sources */,
613 | F14965C724323AAC008C1847 /* PrimaryHorCell+Interaction.swift in Sources */,
614 | F116EC3C2112E4A40091439E /* ViewController+Create.swift in Sources */,
615 | F14965C824323AAC008C1847 /* PrimaryHorCell+Getter.swift in Sources */,
616 | F14965C024323AAC008C1847 /* HorCell+Core.swift in Sources */,
617 | F14965C224323AAC008C1847 /* HorCell+Create.swift in Sources */,
618 | F14965B224323AAC008C1847 /* HorView+Scroll.swift in Sources */,
619 | F12442442249031700D808D8 /* CustomView.swift in Sources */,
620 | F14965CD24323AAC008C1847 /* CGShapeUtil.swift in Sources */,
621 | F14965D224323AAC008C1847 /* SliderBar.swift in Sources */,
622 | F14965BC24323AAC008C1847 /* HorCell+Collection.swift in Sources */,
623 | F14965E724323AAC008C1847 /* HeaderTitle+Setter.swift in Sources */,
624 | F14965B724323AAC008C1847 /* PrimaryVerCell.swift in Sources */,
625 | F14965AB24323AAC008C1847 /* HorView.swift in Sources */,
626 | F1FB415A2443599600A433E2 /* Slider+Const.swift in Sources */,
627 | F14965BB24323AAC008C1847 /* HorCell.swift in Sources */,
628 | F14965D624323AAC008C1847 /* Slider+Create.swift in Sources */,
629 | F14965E024323AAC008C1847 /* ButtonContainer+TypeAlias.swift in Sources */,
630 | F14965D724323AAC008C1847 /* Header+Create.swift in Sources */,
631 | F14965C324323AAC008C1847 /* SecondaryHorCell+Create.swift in Sources */,
632 | F14965C924323AAC008C1847 /* PrimaryHorCell+Core.swift in Sources */,
633 | F14965D924323AAC008C1847 /* HeaderButton+Const.swift in Sources */,
634 | F14965D124323AAC008C1847 /* UIImage+Extension.swift in Sources */,
635 | F12441F52248001700D808D8 /* ViewController.swift in Sources */,
636 | F14965C124323AAC008C1847 /* HorCell+Type.swift in Sources */,
637 | F14965E824323AAC008C1847 /* HeaderTitle+Const.swift in Sources */,
638 | F14965CE24323AAC008C1847 /* UIView+Extension.swift in Sources */,
639 | F14965C624323AAC008C1847 /* TertiaryHorCell.swift in Sources */,
640 | F14965BA24323AAC008C1847 /* HorCell+Update.swift in Sources */,
641 | F14965AD24323AAC008C1847 /* ColumnCellType.swift in Sources */,
642 | F14965B524323AAC008C1847 /* ImageCell+Create.swift in Sources */,
643 | F14965AC24323AAC008C1847 /* HorView+Collection.swift in Sources */,
644 | F14965D524323AAC008C1847 /* Slider.swift in Sources */,
645 | F14965AF24323AAC008C1847 /* HorView+Create.swift in Sources */,
646 | F14965C424323AAC008C1847 /* SecondaryHorCell.swift in Sources */,
647 | F1FB41582443556000A433E2 /* HorView+Event.swift in Sources */,
648 | F14965E124323AAC008C1847 /* ButtonContainer+Create.swift in Sources */,
649 | F14965B924323AAC008C1847 /* VerCell.swift in Sources */,
650 | F14965B124323AAC008C1847 /* HorView+Getter.swift in Sources */,
651 | F14965E224323AAC008C1847 /* ButtonContainer+Interaction.swift in Sources */,
652 | F14965D824323AAC008C1847 /* ButtonContainer+Setter.swift in Sources */,
653 | F14965C524323AAC008C1847 /* SecondaryHorCell+Const.swift in Sources */,
654 | F14965B424323AAC008C1847 /* HorView+Setter.swift in Sources */,
655 | F14965DB24323AAC008C1847 /* HeaderButton+Type.swift in Sources */,
656 | F14965D024323AAC008C1847 /* UIImageView+Extension.swift in Sources */,
657 | F14965E924323AAC008C1847 /* Header+Setter.swift in Sources */,
658 | F14965DF24323AAC008C1847 /* ButtonContainer.swift in Sources */,
659 | F14965B324323AAC008C1847 /* HorView+Update.swift in Sources */,
660 | F14965BE24323AAC008C1847 /* HorCell+Scroll.swift in Sources */,
661 | F14965DE24323AAC008C1847 /* HeaderButton+Setter.swift in Sources */,
662 | F14965B824323AAC008C1847 /* PrimaryVerCell+Core.swift in Sources */,
663 | F14965CB24323AAC008C1847 /* CellDataKind.swift in Sources */,
664 | F14965B024323AAC008C1847 /* HorView+Register.swift in Sources */,
665 | F14965B624323AAC008C1847 /* ImageCell.swift in Sources */,
666 | F14965DC24323AAC008C1847 /* HeaderButton+Interactive.swift in Sources */,
667 | F14965E624323AAC008C1847 /* HeaderTitle.swift in Sources */,
668 | F14965AE24323AAC008C1847 /* HorView+Style.swift in Sources */,
669 | F14965BF24323AAC008C1847 /* HorCell+Const.swift in Sources */,
670 | F14965CC24323AAC008C1847 /* PrimaryCellData.swift in Sources */,
671 | F14965CF24323AAC008C1847 /* UIColorParser.swift in Sources */,
672 | F14965DD24323AAC008C1847 /* HeaderButton.swift in Sources */,
673 | F116EBD7211046B40091439E /* AppDelegate.swift in Sources */,
674 | F14965E524323AAC008C1847 /* Header+Const.swift in Sources */,
675 | F14965D424323AAC008C1847 /* Slider+Animation.swift in Sources */,
676 | );
677 | runOnlyForDeploymentPostprocessing = 0;
678 | };
679 | /* End PBXSourcesBuildPhase section */
680 |
681 | /* Begin PBXVariantGroup section */
682 | F116EBDF211046B70091439E /* LaunchScreen.storyboard */ = {
683 | isa = PBXVariantGroup;
684 | children = (
685 | F116EBE0211046B70091439E /* Base */,
686 | );
687 | name = LaunchScreen.storyboard;
688 | sourceTree = "";
689 | };
690 | /* End PBXVariantGroup section */
691 |
692 | /* Begin XCBuildConfiguration section */
693 | F116EBE3211046B70091439E /* Debug */ = {
694 | isa = XCBuildConfiguration;
695 | buildSettings = {
696 | ALWAYS_SEARCH_USER_PATHS = NO;
697 | CLANG_ANALYZER_NONNULL = YES;
698 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
699 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
700 | CLANG_CXX_LIBRARY = "libc++";
701 | CLANG_ENABLE_MODULES = YES;
702 | CLANG_ENABLE_OBJC_ARC = YES;
703 | CLANG_ENABLE_OBJC_WEAK = YES;
704 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
705 | CLANG_WARN_BOOL_CONVERSION = YES;
706 | CLANG_WARN_COMMA = YES;
707 | CLANG_WARN_CONSTANT_CONVERSION = YES;
708 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
709 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
710 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
711 | CLANG_WARN_EMPTY_BODY = YES;
712 | CLANG_WARN_ENUM_CONVERSION = YES;
713 | CLANG_WARN_INFINITE_RECURSION = YES;
714 | CLANG_WARN_INT_CONVERSION = YES;
715 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
716 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
717 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
718 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
719 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
720 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
721 | CLANG_WARN_STRICT_PROTOTYPES = YES;
722 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
723 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
724 | CLANG_WARN_UNREACHABLE_CODE = YES;
725 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
726 | CODE_SIGN_IDENTITY = "iPhone Developer";
727 | COPY_PHASE_STRIP = NO;
728 | DEBUG_INFORMATION_FORMAT = dwarf;
729 | ENABLE_STRICT_OBJC_MSGSEND = YES;
730 | ENABLE_TESTABILITY = YES;
731 | GCC_C_LANGUAGE_STANDARD = gnu11;
732 | GCC_DYNAMIC_NO_PIC = NO;
733 | GCC_NO_COMMON_BLOCKS = YES;
734 | GCC_OPTIMIZATION_LEVEL = 0;
735 | GCC_PREPROCESSOR_DEFINITIONS = (
736 | "DEBUG=1",
737 | "$(inherited)",
738 | );
739 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
740 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
741 | GCC_WARN_UNDECLARED_SELECTOR = YES;
742 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
743 | GCC_WARN_UNUSED_FUNCTION = YES;
744 | GCC_WARN_UNUSED_VARIABLE = YES;
745 | IPHONEOS_DEPLOYMENT_TARGET = 14.4;
746 | MTL_ENABLE_DEBUG_INFO = YES;
747 | ONLY_ACTIVE_ARCH = YES;
748 | SDKROOT = iphoneos;
749 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
750 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
751 | };
752 | name = Debug;
753 | };
754 | F116EBE4211046B70091439E /* Release */ = {
755 | isa = XCBuildConfiguration;
756 | buildSettings = {
757 | ALWAYS_SEARCH_USER_PATHS = NO;
758 | CLANG_ANALYZER_NONNULL = YES;
759 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
760 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
761 | CLANG_CXX_LIBRARY = "libc++";
762 | CLANG_ENABLE_MODULES = YES;
763 | CLANG_ENABLE_OBJC_ARC = YES;
764 | CLANG_ENABLE_OBJC_WEAK = YES;
765 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
766 | CLANG_WARN_BOOL_CONVERSION = YES;
767 | CLANG_WARN_COMMA = YES;
768 | CLANG_WARN_CONSTANT_CONVERSION = YES;
769 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
770 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
771 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
772 | CLANG_WARN_EMPTY_BODY = YES;
773 | CLANG_WARN_ENUM_CONVERSION = YES;
774 | CLANG_WARN_INFINITE_RECURSION = YES;
775 | CLANG_WARN_INT_CONVERSION = YES;
776 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
777 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
778 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
779 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
780 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
781 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
782 | CLANG_WARN_STRICT_PROTOTYPES = YES;
783 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
784 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
785 | CLANG_WARN_UNREACHABLE_CODE = YES;
786 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
787 | CODE_SIGN_IDENTITY = "iPhone Developer";
788 | COPY_PHASE_STRIP = NO;
789 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
790 | ENABLE_NS_ASSERTIONS = NO;
791 | ENABLE_STRICT_OBJC_MSGSEND = YES;
792 | GCC_C_LANGUAGE_STANDARD = gnu11;
793 | GCC_NO_COMMON_BLOCKS = YES;
794 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
795 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
796 | GCC_WARN_UNDECLARED_SELECTOR = YES;
797 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
798 | GCC_WARN_UNUSED_FUNCTION = YES;
799 | GCC_WARN_UNUSED_VARIABLE = YES;
800 | IPHONEOS_DEPLOYMENT_TARGET = 14.4;
801 | MTL_ENABLE_DEBUG_INFO = NO;
802 | SDKROOT = iphoneos;
803 | SWIFT_COMPILATION_MODE = wholemodule;
804 | SWIFT_OPTIMIZATION_LEVEL = "-O";
805 | VALIDATE_PRODUCT = YES;
806 | };
807 | name = Release;
808 | };
809 | F116EBE6211046B70091439E /* Debug */ = {
810 | isa = XCBuildConfiguration;
811 | buildSettings = {
812 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
813 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
814 | CODE_SIGN_STYLE = Automatic;
815 | DEVELOPMENT_TEAM = B5CC9TD7P7;
816 | INFOPLIST_FILE = FlowLayout/Info.plist;
817 | IPHONEOS_DEPLOYMENT_TARGET = 12.2;
818 | LD_RUNPATH_SEARCH_PATHS = (
819 | "$(inherited)",
820 | "@executable_path/Frameworks",
821 | );
822 | PRODUCT_BUNDLE_IDENTIFIER = codes.eon.FlowLay;
823 | PRODUCT_NAME = "$(TARGET_NAME)";
824 | SWIFT_VERSION = 5.0;
825 | TARGETED_DEVICE_FAMILY = 1;
826 | };
827 | name = Debug;
828 | };
829 | F116EBE7211046B70091439E /* Release */ = {
830 | isa = XCBuildConfiguration;
831 | buildSettings = {
832 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
833 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
834 | CODE_SIGN_STYLE = Automatic;
835 | DEVELOPMENT_TEAM = B5CC9TD7P7;
836 | INFOPLIST_FILE = FlowLayout/Info.plist;
837 | IPHONEOS_DEPLOYMENT_TARGET = 12.2;
838 | LD_RUNPATH_SEARCH_PATHS = (
839 | "$(inherited)",
840 | "@executable_path/Frameworks",
841 | );
842 | PRODUCT_BUNDLE_IDENTIFIER = codes.eon.FlowLay;
843 | PRODUCT_NAME = "$(TARGET_NAME)";
844 | SWIFT_VERSION = 5.0;
845 | TARGETED_DEVICE_FAMILY = 1;
846 | };
847 | name = Release;
848 | };
849 | /* End XCBuildConfiguration section */
850 |
851 | /* Begin XCConfigurationList section */
852 | F116EBCE211046B40091439E /* Build configuration list for PBXProject "FlowLayout" */ = {
853 | isa = XCConfigurationList;
854 | buildConfigurations = (
855 | F116EBE3211046B70091439E /* Debug */,
856 | F116EBE4211046B70091439E /* Release */,
857 | );
858 | defaultConfigurationIsVisible = 0;
859 | defaultConfigurationName = Release;
860 | };
861 | F116EBE5211046B70091439E /* Build configuration list for PBXNativeTarget "FlowLayout" */ = {
862 | isa = XCConfigurationList;
863 | buildConfigurations = (
864 | F116EBE6211046B70091439E /* Debug */,
865 | F116EBE7211046B70091439E /* Release */,
866 | );
867 | defaultConfigurationIsVisible = 0;
868 | defaultConfigurationName = Release;
869 | };
870 | /* End XCConfigurationList section */
871 |
872 | /* Begin XCRemoteSwiftPackageReference section */
873 | F14965EA24323B0A008C1847 /* XCRemoteSwiftPackageReference "JSONSugar" */ = {
874 | isa = XCRemoteSwiftPackageReference;
875 | repositoryURL = "https://github.com/eonist/JSONSugar.git";
876 | requirement = {
877 | branch = master;
878 | kind = branch;
879 | };
880 | };
881 | F14965ED24323B22008C1847 /* XCRemoteSwiftPackageReference "ImageSugar" */ = {
882 | isa = XCRemoteSwiftPackageReference;
883 | repositoryURL = "https://github.com/eonist/ImageSugar.git";
884 | requirement = {
885 | branch = master;
886 | kind = branch;
887 | };
888 | };
889 | F14965F024323D2B008C1847 /* XCRemoteSwiftPackageReference "CommonCell" */ = {
890 | isa = XCRemoteSwiftPackageReference;
891 | repositoryURL = "https://github.com/eonist/CommonCell.git";
892 | requirement = {
893 | branch = master;
894 | kind = branch;
895 | };
896 | };
897 | F14965F324323D7B008C1847 /* XCRemoteSwiftPackageReference "NetworkSugar" */ = {
898 | isa = XCRemoteSwiftPackageReference;
899 | repositoryURL = "https://github.com/eonist/NetworkSugar.git";
900 | requirement = {
901 | branch = master;
902 | kind = branch;
903 | };
904 | };
905 | F15CB290268D262D00C5C303 /* XCRemoteSwiftPackageReference "Spatial" */ = {
906 | isa = XCRemoteSwiftPackageReference;
907 | repositoryURL = "https://github.com/eonist/Spatial.git";
908 | requirement = {
909 | branch = master;
910 | kind = branch;
911 | };
912 | };
913 | F15CB293268D265200C5C303 /* XCRemoteSwiftPackageReference "with" */ = {
914 | isa = XCRemoteSwiftPackageReference;
915 | repositoryURL = "https://github.com/eonist/with";
916 | requirement = {
917 | branch = master;
918 | kind = branch;
919 | };
920 | };
921 | /* End XCRemoteSwiftPackageReference section */
922 |
923 | /* Begin XCSwiftPackageProductDependency section */
924 | F14965EB24323B0A008C1847 /* JSONSugar */ = {
925 | isa = XCSwiftPackageProductDependency;
926 | package = F14965EA24323B0A008C1847 /* XCRemoteSwiftPackageReference "JSONSugar" */;
927 | productName = JSONSugar;
928 | };
929 | F14965EE24323B22008C1847 /* ImageSugar */ = {
930 | isa = XCSwiftPackageProductDependency;
931 | package = F14965ED24323B22008C1847 /* XCRemoteSwiftPackageReference "ImageSugar" */;
932 | productName = ImageSugar;
933 | };
934 | F14965F124323D2B008C1847 /* CommonCell */ = {
935 | isa = XCSwiftPackageProductDependency;
936 | package = F14965F024323D2B008C1847 /* XCRemoteSwiftPackageReference "CommonCell" */;
937 | productName = CommonCell;
938 | };
939 | F14965F424323D7B008C1847 /* NetworkSugar */ = {
940 | isa = XCSwiftPackageProductDependency;
941 | package = F14965F324323D7B008C1847 /* XCRemoteSwiftPackageReference "NetworkSugar" */;
942 | productName = NetworkSugar;
943 | };
944 | F15CB291268D262D00C5C303 /* Spatial */ = {
945 | isa = XCSwiftPackageProductDependency;
946 | package = F15CB290268D262D00C5C303 /* XCRemoteSwiftPackageReference "Spatial" */;
947 | productName = Spatial;
948 | };
949 | F15CB294268D265200C5C303 /* With */ = {
950 | isa = XCSwiftPackageProductDependency;
951 | package = F15CB293268D265200C5C303 /* XCRemoteSwiftPackageReference "with" */;
952 | productName = With;
953 | };
954 | /* End XCSwiftPackageProductDependency section */
955 | };
956 | rootObject = F116EBCB211046B40091439E /* Project object */;
957 | }
958 |
--------------------------------------------------------------------------------
/FlowLayout.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/FlowLayout.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/FlowLayout.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved:
--------------------------------------------------------------------------------
1 | {
2 | "object": {
3 | "pins": [
4 | {
5 | "package": "CommonCell",
6 | "repositoryURL": "https://github.com/eonist/CommonCell.git",
7 | "state": {
8 | "branch": "master",
9 | "revision": "e226a1520deb37374884463fca12118bae00c7c9",
10 | "version": null
11 | }
12 | },
13 | {
14 | "package": "FileSugar",
15 | "repositoryURL": "https://github.com/eonist/FileSugar.git",
16 | "state": {
17 | "branch": "master",
18 | "revision": "523b7325aa211c34d2622c365915969122ed02bc",
19 | "version": null
20 | }
21 | },
22 | {
23 | "package": "ImageSugar",
24 | "repositoryURL": "https://github.com/eonist/ImageSugar.git",
25 | "state": {
26 | "branch": "master",
27 | "revision": "badf21a795fd627f4a44c55c57c6f3626261662e",
28 | "version": null
29 | }
30 | },
31 | {
32 | "package": "JSONSugar",
33 | "repositoryURL": "https://github.com/eonist/JSONSugar.git",
34 | "state": {
35 | "branch": "master",
36 | "revision": "f6ebe7fde5cbe574ae9516c73962ccd01e7208b1",
37 | "version": null
38 | }
39 | },
40 | {
41 | "package": "NetworkSugar",
42 | "repositoryURL": "https://github.com/eonist/NetworkSugar.git",
43 | "state": {
44 | "branch": "master",
45 | "revision": "4da36c5b8fa9c2c1ac3784cbb2474ee66e51b6ff",
46 | "version": null
47 | }
48 | },
49 | {
50 | "package": "ReusableCell",
51 | "repositoryURL": "https://github.com/eonist/ReusableCell.git",
52 | "state": {
53 | "branch": "master",
54 | "revision": "26413744c41ddcfaffaeb561c904449d784ef1d0",
55 | "version": null
56 | }
57 | },
58 | {
59 | "package": "Spatial",
60 | "repositoryURL": "https://github.com/eonist/Spatial.git",
61 | "state": {
62 | "branch": "master",
63 | "revision": "d5db4874deba1ac3adb50124367870105ff46132",
64 | "version": null
65 | }
66 | },
67 | {
68 | "package": "With",
69 | "repositoryURL": "https://github.com/eonist/with",
70 | "state": {
71 | "branch": "master",
72 | "revision": "db0935ecba9577470664105c25cf2c29481ec0fb",
73 | "version": null
74 | }
75 | }
76 | ]
77 | },
78 | "version": 1
79 | }
80 |
--------------------------------------------------------------------------------
/FlowLayout.xcodeproj/project.xcworkspace/xcuserdata/andrejorgensen.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eonist/FlowLayout/f3148776c7852196b47383b92651661372402e3e/FlowLayout.xcodeproj/project.xcworkspace/xcuserdata/andrejorgensen.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/FlowLayout.xcodeproj/project.xcworkspace/xcuserdata/eon.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eonist/FlowLayout/f3148776c7852196b47383b92651661372402e3e/FlowLayout.xcodeproj/project.xcworkspace/xcuserdata/eon.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/FlowLayout.xcodeproj/xcshareddata/xcschemes/FlowLayout.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
37 |
38 |
39 |
40 |
41 |
42 |
52 |
54 |
60 |
61 |
62 |
63 |
69 |
71 |
77 |
78 |
79 |
80 |
82 |
83 |
86 |
87 |
88 |
--------------------------------------------------------------------------------
/FlowLayout.xcodeproj/xcuserdata/eon.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
--------------------------------------------------------------------------------
/FlowLayout.xcodeproj/xcuserdata/eon.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | FlowLayout.xcscheme
8 |
9 | orderHint
10 | 0
11 |
12 | FlowLayout.xcscheme_^#shared#^_
13 |
14 | orderHint
15 | 0
16 |
17 |
18 | SuppressBuildableAutocreation
19 |
20 | F116EBD2211046B40091439E
21 |
22 | primary
23 |
24 |
25 | F12442022248314700D808D8
26 |
27 | primary
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/FlowLayout/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 | @UIApplicationMain
3 |
4 | final class AppDelegate: UIResponder, UIApplicationDelegate {
5 | lazy var window: UIWindow? = {
6 | let win = UIWindow(frame: UIScreen.main.bounds)
7 | let vc = ViewController()
8 | win.rootViewController = vc
9 | win.makeKeyAndVisible() // Important: ⚠️️ since we have no Main storyboard because this app uses programatic UI
10 | return win
11 | }()
12 | func applicationDidFinishLaunching(_ application: UIApplication) {
13 | _ = window
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/FlowLayout/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "20x20",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "20x20",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "29x29",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "29x29",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "40x40",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "40x40",
31 | "scale" : "3x"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "size" : "60x60",
36 | "scale" : "2x"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "size" : "60x60",
41 | "scale" : "3x"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "size" : "20x20",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "size" : "20x20",
51 | "scale" : "2x"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "size" : "29x29",
56 | "scale" : "1x"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "size" : "29x29",
61 | "scale" : "2x"
62 | },
63 | {
64 | "idiom" : "ipad",
65 | "size" : "40x40",
66 | "scale" : "1x"
67 | },
68 | {
69 | "idiom" : "ipad",
70 | "size" : "40x40",
71 | "scale" : "2x"
72 | },
73 | {
74 | "idiom" : "ipad",
75 | "size" : "76x76",
76 | "scale" : "1x"
77 | },
78 | {
79 | "idiom" : "ipad",
80 | "size" : "76x76",
81 | "scale" : "2x"
82 | },
83 | {
84 | "idiom" : "ipad",
85 | "size" : "83.5x83.5",
86 | "scale" : "2x"
87 | },
88 | {
89 | "idiom" : "ios-marketing",
90 | "size" : "1024x1024",
91 | "scale" : "1x"
92 | }
93 | ],
94 | "info" : {
95 | "version" : 1,
96 | "author" : "xcode"
97 | }
98 | }
--------------------------------------------------------------------------------
/FlowLayout/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/FlowLayout/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 |
--------------------------------------------------------------------------------
/FlowLayout/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 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 | LSRequiresIPhoneOS
22 |
23 | UILaunchStoryboardName
24 | LaunchScreen
25 | UIRequiredDeviceCapabilities
26 |
27 | armv7
28 |
29 | UISupportedInterfaceOrientations
30 |
31 | UIInterfaceOrientationPortrait
32 | UIInterfaceOrientationLandscapeLeft
33 | UIInterfaceOrientationLandscapeRight
34 |
35 | UISupportedInterfaceOrientations~ipad
36 |
37 | UIInterfaceOrientationPortrait
38 | UIInterfaceOrientationPortraitUpsideDown
39 | UIInterfaceOrientationLandscapeLeft
40 | UIInterfaceOrientationLandscapeRight
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/FlowLayout/ViewController+Create.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 | import Spatial
3 | import With
4 | /**
5 | * Create
6 | */
7 | extension ViewController {
8 | /**
9 | * Creates the FlowView
10 | */
11 | func createFlowView() -> HorView {
12 | with(.init()) {
13 | view.addSubview($0)
14 | $0.anchorAndSize(to: view)
15 | }
16 | }
17 | /**
18 | * Creates custom flow view
19 | */
20 | func createCustomFlowView() -> CustomView {
21 | with(.init()) {
22 | view.addSubview($0)
23 | $0.anchorAndSize(to: view)
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/FlowLayout/ViewController.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 | /**
3 | * FlowLayout
4 | * - Description: FlowLayout (Bi-directional layout framework) (Great for prototyping UX ideas and apps)
5 | * - Definition: bidirectional: functioning in two directions.
6 | * - Fixme: ⚠️️ Set the view, don't add to it, also set it as RootVC, like in weather, and reaname to VC 👈
7 | * - Fixme: ⚠️️ ReUse cell: https://tech.busuu.com/dealing-with-different-kinds-of-cells-in-swift-part-2-of-3-3fe73b0c50c6
8 | * - Fixme: ⚠️️ Apple video on advance collection views: https://developer.apple.com/videos/play/wwdc2014/232/
9 | * - Fixme: ⚠️️ mixed sizes in Collection: https://octodev.net/custom-collectionviewlayout/
10 | */
11 | final class ViewController: UIViewController {
12 | lazy var flowView: HorView = createCustomFlowView() // createFlowView()
13 | }
14 | /**
15 | * Overrides
16 | */
17 | extension ViewController {
18 | override var prefersStatusBarHidden: Bool { true } // hides statusbar
19 | override func viewDidLoad() {
20 | super.viewDidLoad()
21 | view = MainView()
22 | view.backgroundColor = .lightGray
23 | _ = flowView
24 | }
25 | /**
26 | * Set title
27 | * - Note: I guess this must be set from this method in order for it to work
28 | */
29 | override func viewWillAppear(_ animated: Bool) {
30 | super.viewWillAppear(animated)
31 | // By using the setNav... method as oppose to the isNav... we get better animation
32 | self.navigationController?.setNavigationBarHidden(true, animated: true) // self.navigationController?.isNavigationBarHidden = false//
33 | navigationItem.title = "Main" // - Fixme: ⚠️️ use enum?
34 | }
35 | }
36 | final class MainView: UIView {}
37 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Eonist
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 |
--------------------------------------------------------------------------------
/Package.resolved:
--------------------------------------------------------------------------------
1 | {
2 | "object": {
3 | "pins": [
4 | {
5 | "package": "CommonCell",
6 | "repositoryURL": "https://github.com/eonist/CommonCell.git",
7 | "state": {
8 | "branch": "master",
9 | "revision": "b252d13b872e20e702776f1d51908c3140f820cd",
10 | "version": null
11 | }
12 | },
13 | {
14 | "package": "FileSugar",
15 | "repositoryURL": "https://github.com/eonist/FileSugar.git",
16 | "state": {
17 | "branch": "master",
18 | "revision": "523b7325aa211c34d2622c365915969122ed02bc",
19 | "version": null
20 | }
21 | },
22 | {
23 | "package": "ImageSugar",
24 | "repositoryURL": "https://github.com/eonist/ImageSugar.git",
25 | "state": {
26 | "branch": "master",
27 | "revision": "badf21a795fd627f4a44c55c57c6f3626261662e",
28 | "version": null
29 | }
30 | },
31 | {
32 | "package": "JSONSugar",
33 | "repositoryURL": "https://github.com/eonist/JSONSugar.git",
34 | "state": {
35 | "branch": "master",
36 | "revision": "f6ebe7fde5cbe574ae9516c73962ccd01e7208b1",
37 | "version": null
38 | }
39 | },
40 | {
41 | "package": "NetworkSugar",
42 | "repositoryURL": "https://github.com/eonist/NetworkSugar.git",
43 | "state": {
44 | "branch": "master",
45 | "revision": "4da36c5b8fa9c2c1ac3784cbb2474ee66e51b6ff",
46 | "version": null
47 | }
48 | },
49 | {
50 | "package": "ReusableCell",
51 | "repositoryURL": "https://github.com/eonist/ReusableCell.git",
52 | "state": {
53 | "branch": "master",
54 | "revision": "26413744c41ddcfaffaeb561c904449d784ef1d0",
55 | "version": null
56 | }
57 | },
58 | {
59 | "package": "Spatial",
60 | "repositoryURL": "https://github.com/eonist/Spatial.git",
61 | "state": {
62 | "branch": "master",
63 | "revision": "d5db4874deba1ac3adb50124367870105ff46132",
64 | "version": null
65 | }
66 | },
67 | {
68 | "package": "With",
69 | "repositoryURL": "https://github.com/eonist/With.git",
70 | "state": {
71 | "branch": "master",
72 | "revision": "db0935ecba9577470664105c25cf2c29481ec0fb",
73 | "version": null
74 | }
75 | }
76 | ]
77 | },
78 | "version": 1
79 | }
80 |
--------------------------------------------------------------------------------
/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version:5.3
2 |
3 | import PackageDescription
4 |
5 | let package = Package(
6 | name: "FlowLayout",
7 | platforms: [.iOS(.v12), .macOS(.v10_13)],
8 | products: [
9 | .library(
10 | name: "FlowLayout",
11 | targets: ["FlowLayout"])
12 | ],
13 | dependencies: [
14 | .package(url: "https://github.com/eonist/CommonCell.git", .branch("master")),
15 | .package(url: "https://github.com/eonist/ImageSugar.git", .branch("master")),
16 | .package(url: "https://github.com/eonist/NetworkSugar.git", .branch("master")),
17 | .package(url: "https://github.com/eonist/JSONSugar.git", .branch("master")),
18 | .package(url: "https://github.com/eonist/With.git", .branch("master")),
19 | .package(url: "https://github.com/eonist/Spatial.git", .branch("master"))
20 | //.package(url: "https://github.com/eonist/RefreshControlKit.git", .branch("master")),
21 | //.package(url: "https://github.com/eonist/TestRunner.git", .branch("master")),
22 | //.package(url: "https://github.com/eonist/UITestSugar.git", .branch("master"))
23 | ],
24 | targets: [
25 | .target(
26 | name: "FlowLayout",
27 | dependencies: ["CommonCell", "ImageSugar", "NetworkSugar", "JSONSugar", "With", "Spatial"])
28 | ]
29 | )
30 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # FlowLayout
2 | 
3 | [](https://img.shields.io/badge/SDK-0.1-blue.svg?longCache=true)
4 | 
5 | [](https://codebeat.co/projects/github-com-eonist-flowlayout-master)
6 | [](https://github.com/apple/swift)
7 | [](https://www.producthunt.com/posts/flowlayout)
8 | [](https://github.com/sindresorhus/swiftlint-sindre) [](https://github.com/eonist/FlowLayout/actions/workflows/Tests.yml)
9 |
10 |
11 |
12 |
13 |
14 | ### Description
15 | The idea is to give aspiring app developers a dead simple "vanilla" swift library to start with. Just add some end point calls to instagram and you could literally have a minimal insta client in a day.
16 |
17 | ### Features
18 | - 100% Programmatic 👌
19 | - [3.82 GPA on Codebeat](https://codebeat.co/projects/github-com-eonist-flowlayout-master) 🏆
20 | - Dual UICollectionView setup ↕️ ↔️
21 | - Responsive header (Compact/Normal) 📏
22 | - Pull-to-refresh (vertical/horizontal) 🔄
23 | - Constraint animation 📐
24 | - Works on all iPhone/iPad models 📱
25 | - View based (no ViewController) 🖼
26 | - Vanilla swift 🍦
27 | - 0% syntactic sugar 🍭
28 | - Mostly off the shelf components 🤯
29 |
30 | ### Installation:
31 | - SPM: `.package(url: "https://github.com/eonist/FlowLayout.git", .branch("master"))`
32 |
33 | ### Pull to refresh:
34 |
35 |
36 | ### Compact mode:
37 |
38 |
39 | ### Swipe to new pages:
40 |
41 |
42 | ### Requires
43 | - the Constraint extension (Included)
44 |
45 | ### Install
46 | - Manual: open `FlowLayout.xcodeproj`
47 | - Carthage: `github "eonist/FlowLayout" "master"`
48 | - CocoaPod (coming soon)
49 |
50 | ### Credits
51 | - Incredible gif via [Gifski](https://github.com/sindresorhus/gifski-app)
52 | - The awesome people from [Swift-lang](https://slofile.com/slack/swift-lang) on Slack
53 |
54 | ### Todo
55 | - Add stockimages
56 | - Fix compact mode so that the offset is inherited in the next view
57 | - Make a new IRL video with the new fixes
58 | - Keep improving the structure and clarity
59 |
60 | ### Press
61 | - Producthunt: [https://www.producthunt.com/posts/flowlayout](https://www.producthunt.com/posts/flowlayout)
62 | - [MediaKit.zip](https://www.dropbox.com/s/5s59k5e0o6z5y0g/mediakit.zip?dl=0) via dropbox
63 | - Blog post about FlowLayout [eon.codes/blog](http://eon.codes/blog/2018/08/05/Flow-Layout/)
64 |
65 | ### License
66 | [MIT](https://en.wikipedia.org/wiki/MIT_License)
67 |
--------------------------------------------------------------------------------
/Sources/FlowLayout/Header/ButtonContainer/ButtonContainer+Const.swift:
--------------------------------------------------------------------------------
1 | #if os(iOS)
2 | import QuartzCore
3 |
4 | extension ButtonContainer {
5 | static let defaultOnButtonTap: OnButtonTap = { _ in Swift.print("Callback is missing") }
6 | static let height: CGFloat = 60
7 | }
8 |
9 | #endif
10 |
--------------------------------------------------------------------------------
/Sources/FlowLayout/Header/ButtonContainer/ButtonContainer+Create.swift:
--------------------------------------------------------------------------------
1 | #if os(iOS)
2 | import UIKit
3 | import With
4 | import Spatial
5 | /**
6 | * Create
7 | */
8 | extension ButtonContainer {
9 | /**
10 | * Creates buttons
11 | * - Fixme: ⚠️️ use with
12 | */
13 | func createButtons() -> [HeaderButton] {
14 | let titles: [String] = ColumnCellType.allCases.map { $0.rawValue.capitalized }
15 | let buttons: [HeaderButton] = titles.map { title in // All vertically centered, 30p height each
16 | with(.init(title: title)) {
17 | $0.addTarget(self, action: #selector(onTouchInside), for: .touchUpInside)
18 | self.addSubview($0)
19 | }
20 | }
21 | let widthMultiplier: CGFloat = 1 / CGFloat(titles.count) // 75pix on iphone 8
22 | buttons.distributeAndSize(dir: .hor, height: ButtonContainer.height, align: .topLeft, alignTo: .topLeft, multiplier: .init(width: widthMultiplier, height: 1))
23 | buttons.first?.setTitleColor(HorView.style.header.button.selectedFontColor, for: .normal)
24 | buttons.first?.titleLabel?.font = HorView.style.header.button.selectedFont
25 | return buttons
26 | }
27 | }
28 |
29 | #endif
30 |
--------------------------------------------------------------------------------
/Sources/FlowLayout/Header/ButtonContainer/ButtonContainer+Interaction.swift:
--------------------------------------------------------------------------------
1 | #if os(iOS)
2 | import UIKit
3 | /**
4 | * Handler
5 | */
6 | extension ButtonContainer {
7 | /**
8 | * Tap
9 | */
10 | @objc func onTouchInside(sender: UIButton) {
11 | if let headerBtn = sender as? HeaderButton, let headerTitle = headerBtn.currentTitle {
12 | onButtonClick(headerTitle)
13 | }
14 | }
15 | }
16 |
17 | #endif
18 |
--------------------------------------------------------------------------------
/Sources/FlowLayout/Header/ButtonContainer/ButtonContainer+Setter.swift:
--------------------------------------------------------------------------------
1 | #if os(iOS)
2 | import UIKit
3 |
4 | extension ButtonContainer {
5 | /**
6 | * Set index
7 | */
8 | func setIdx(idx: Int) {
9 | // Swift.print("BtnContainer.setIdx(\(idx))")
10 | let btnTitle: String? = ColumnCellType.allCases[idx].rawValue.capitalized
11 | // Swift.print("btnTitle: \(String(describing: btnTitle))")
12 | buttons.filter { $0.titleLabel?.text == btnTitle }.forEach { setActive(btn: $0) }
13 | }
14 | /**
15 | * Set active state
16 | */
17 | func setActive(btn: UIButton) {
18 | // Swift.print("setActive: \(String(describing: setActive))")
19 | buttons.forEach { $0.setActive(isActive: $0 == btn) }
20 | }
21 | }
22 |
23 | #endif
24 |
--------------------------------------------------------------------------------
/Sources/FlowLayout/Header/ButtonContainer/ButtonContainer+TypeAlias.swift:
--------------------------------------------------------------------------------
1 | #if os(iOS)
2 | import Foundation
3 | /**
4 | * Callback signature
5 | */
6 | extension ButtonContainer {
7 | typealias OnButtonTap = (_ buttonTitle: String) -> Void
8 | }
9 |
10 | #endif
11 |
--------------------------------------------------------------------------------
/Sources/FlowLayout/Header/ButtonContainer/ButtonContainer.swift:
--------------------------------------------------------------------------------
1 | #if os(iOS)
2 | import UIKit
3 | import With
4 | import Spatial
5 | /**
6 | * This is the view that has the header buttons (aka primary, secondary, tierary)
7 | */
8 | class ButtonContainer: UIView {
9 | lazy var buttons: [HeaderButton] = self.createButtons()
10 | var onButtonClick: OnButtonTap = defaultOnButtonTap
11 | override init(frame: CGRect) {
12 | super.init(frame: frame)
13 | let bg: UIView = .init()
14 | bg.backgroundColor = .green
15 | self.addSubview(bg) // Add a background to
16 | _ = buttons
17 | }
18 | /**
19 | * Boilerplate
20 | */
21 | required init(coder: NSCoder) {
22 | fatalError("init(coder:) has not been implemented")
23 | }
24 | }
25 |
26 | #endif
27 |
--------------------------------------------------------------------------------
/Sources/FlowLayout/Header/ButtonContainer/HeaderButton/CustomButton.swift:
--------------------------------------------------------------------------------
1 | #if os(iOS)
2 | import UIKit
3 | import Spatial
4 | import With
5 |
6 | class CustomButton: UIButton, ConstraintKind {
7 | // - Fixme: ⚠️️ use anchorAndSize var
8 | var anchor: (x: NSLayoutConstraint, y: NSLayoutConstraint)?
9 | var size: (w: NSLayoutConstraint, h: NSLayoutConstraint)?
10 | init(title: String) {
11 | super.init(frame: .zero)
12 | with(self) {
13 | $0.backgroundColor = .clear
14 | $0.titleLabel?.font = UIFont.systemFont(ofSize: 16.0)
15 | $0.titleLabel?.textAlignment = .center
16 | $0.setTitleColor(.black, for: .normal)
17 | $0.setTitle(title, for: .normal)
18 | }
19 | }
20 | required init?(coder aDecoder: NSCoder) {
21 | fatalError("init(coder:) has not been implemented")
22 | }
23 | }
24 |
25 | #endif
26 |
--------------------------------------------------------------------------------
/Sources/FlowLayout/Header/ButtonContainer/HeaderButton/HeaderButton+Const.swift:
--------------------------------------------------------------------------------
1 | #if os(iOS)
2 | import QuartzCore
3 | /**
4 | * Const
5 | */
6 | extension HeaderButton {
7 | static var defautOnTap: OnTap = { Swift.print("HeaderButton.defaultClickCallBack() - no call back attached") }
8 | static let width: CGFloat = 60
9 | static let height: CGFloat = 20 // Fixme: combine width and height to size
10 | }
11 |
12 | #endif
13 |
--------------------------------------------------------------------------------
/Sources/FlowLayout/Header/ButtonContainer/HeaderButton/HeaderButton+Interactive.swift:
--------------------------------------------------------------------------------
1 | #if os(iOS)
2 | import UIKit
3 |
4 | extension HeaderButton {
5 | /**
6 | * Selector handler
7 | */
8 | @objc func buttonTouched(sender: UIButton) {
9 | clickCallBack() // Calls whichever method that is attached to the call-back variable
10 | }
11 | }
12 |
13 | #endif
14 |
--------------------------------------------------------------------------------
/Sources/FlowLayout/Header/ButtonContainer/HeaderButton/HeaderButton+Setter.swift:
--------------------------------------------------------------------------------
1 | #if os(iOS)
2 | import Foundation
3 | /**
4 | * Setter
5 | */
6 | extension HeaderButton {
7 | /**
8 | * Sets the button to active mode
9 | */
10 | func setActive(isActive: Bool) {
11 | if isActive {
12 | self.setTitleColor(HorView.style.header.button.selectedFontColor, for: .normal)
13 | self.titleLabel?.font = HorView.style.header.button.selectedFont
14 | } else {
15 | self.setTitleColor(HorView.style.header.button.unSelectedFontColor, for: .normal)
16 | self.titleLabel?.font = HorView.style.header.button.unSelectedFont
17 | }
18 | }
19 | }
20 |
21 | #endif
22 |
--------------------------------------------------------------------------------
/Sources/FlowLayout/Header/ButtonContainer/HeaderButton/HeaderButton+Type.swift:
--------------------------------------------------------------------------------
1 | #if os(iOS)
2 | import Foundation
3 | /**
4 | * Type
5 | */
6 | extension HeaderButton {
7 | typealias OnTap = () -> Void
8 | }
9 |
10 | #endif
11 |
--------------------------------------------------------------------------------
/Sources/FlowLayout/Header/ButtonContainer/HeaderButton/HeaderButton.swift:
--------------------------------------------------------------------------------
1 | #if os(iOS)
2 | import UIKit
3 | /**
4 | * headerButton
5 | */
6 | final class HeaderButton: CustomButton {
7 | var clickCallBack: OnTap = HeaderButton.defautOnTap
8 | override init(title: String) {
9 | super.init(title: title)
10 | self.addTarget(self, action: #selector(buttonTouched), for: .touchUpInside)
11 | }
12 | /**
13 | * Boilerplate
14 | */
15 | required init?(coder aDecoder: NSCoder) {
16 | fatalError("init(coder:) has not been implemented")
17 | }
18 | }
19 |
20 | #endif
21 |
--------------------------------------------------------------------------------
/Sources/FlowLayout/Header/Header+Const.swift:
--------------------------------------------------------------------------------
1 | #if os(iOS)
2 | import UIKit
3 | import Spatial
4 | /**
5 | * Const
6 | */
7 | extension Header {
8 | internal static let height: CGFloat = HeaderTitle.height + ButtonContainer.height + Slider.height
9 | }
10 |
11 | #endif
12 |
--------------------------------------------------------------------------------
/Sources/FlowLayout/Header/Header+Create.swift:
--------------------------------------------------------------------------------
1 | #if os(iOS)
2 | import UIKit
3 | import Spatial
4 | import With
5 | /**
6 | * Create
7 | */
8 | extension Header {
9 | /**
10 | * TopFix (to cover up a void space)
11 | */
12 | func createTopFix() -> UIView {
13 | with(.init()) {
14 | self.addSubview($0)
15 | $0.anchorAndSize(to: self, height: Slider.height, align: .topLeft, alignTo: .topLeft)
16 | $0.backgroundColor = HorView.style.header.backgroundColor
17 | }
18 | }
19 | /**
20 | * Creates header title
21 | */
22 | func createHeaderTitle() -> HeaderTitle {
23 | with(.init()) {
24 | $0.setTitleText(text: ColumnCellType.primary.rawValue.capitalized)
25 | self.addSubview($0)
26 | $0.anchorAndSize(to: self.topFix, sizeTo: self, height: HeaderTitle.height, align: .topLeft, alignTo: .bottomLeft)
27 | }
28 | }
29 | /**
30 | * Creates buttons
31 | */
32 | func createButtonContainer() -> ButtonContainer {
33 | with(.init(frame: .zero)) {
34 | addSubview($0)
35 | $0.anchorAndSize(to: headerTitle, sizeTo: self, height: ButtonContainer.height, align: .topLeft, alignTo: .bottomLeft)
36 | }
37 | }
38 | /**
39 | * Create slider
40 | */
41 | func createSlider() -> Slider {
42 | let segmentCount: Int = ColumnCellType.allCases.count
43 | return with(.init(idx: 0, segmentCount:segmentCount, frame: .zero)) {
44 | self.addSubview($0)
45 | $0.anchorAndSize(to: self, height: Slider.height, align: .bottomLeft, alignTo: .bottomLeft)
46 | }
47 | }
48 | /**
49 | * Graphic fix (When you drag the list up and down this covers to match header color)
50 | * - Note: Basically you can drag down the header 500px before it shows
51 | * - Note: this doesn't need to be saved as a param
52 | */
53 | func createBackgroundFix() -> UIView {
54 | with(.init(frame: .zero)) {
55 | $0.backgroundColor = HorView.style.header.backgroundColor
56 | self.addSubview($0)
57 | $0.anchorAndSize(to: self, height: 500, align: .bottomLeft, alignTo: .topLeft)
58 | }
59 | }
60 | }
61 |
62 | #endif
63 |
--------------------------------------------------------------------------------
/Sources/FlowLayout/Header/Header+Setter.swift:
--------------------------------------------------------------------------------
1 | #if os(iOS)
2 | import UIKit
3 | import Spatial
4 | /**
5 | * Accessor
6 | */
7 | extension Header {
8 | /**
9 | * Set index for title label
10 | */
11 | func setTitleIdx(idx: Int) {
12 | let title: String = ColumnCellType.allCases[idx].rawValue // ?? "undefined"
13 | headerTitle.text = title
14 | }
15 | }
16 |
17 | #endif
18 |
--------------------------------------------------------------------------------
/Sources/FlowLayout/Header/Header.swift:
--------------------------------------------------------------------------------
1 | #if os(iOS)
2 | import UIKit
3 | /**
4 | * Header
5 | * - Note: Essentially contains: (title, buttons, slider)
6 | */
7 | open class Header: UIView {
8 | lazy var topFix: UIView = self.createTopFix() // to cover up a void space
9 | lazy var headerTitle: UILabel = self.createHeaderTitle()
10 | lazy var buttonContainer: ButtonContainer = self.createButtonContainer()
11 | lazy var slider: Slider = self.createSlider()
12 | override public init(frame: CGRect = .zero) {
13 | super.init(frame: frame)
14 | self.backgroundColor = HorView.style.header.backgroundColor
15 | _ = createBackgroundFix()
16 | _ = topFix
17 | _ = headerTitle
18 | _ = buttonContainer
19 | _ = slider
20 | }
21 | /**
22 | * Boilerplate
23 | */
24 | @available(*, unavailable)
25 | public required init(coder: NSCoder) {
26 | fatalError("init(coder:) has not been implemented")
27 | }
28 | }
29 |
30 | #endif
31 |
--------------------------------------------------------------------------------
/Sources/FlowLayout/Header/HeaderTitle/HeaderTitle+Const.swift:
--------------------------------------------------------------------------------
1 | #if os(iOS)
2 | import QuartzCore
3 |
4 | extension HeaderTitle {
5 | internal static let height: CGFloat = 60
6 | }
7 |
8 | #endif
9 |
--------------------------------------------------------------------------------
/Sources/FlowLayout/Header/HeaderTitle/HeaderTitle+Setter.swift:
--------------------------------------------------------------------------------
1 | #if os(iOS)
2 | import Foundation
3 |
4 | extension HeaderTitle {
5 | func setTitleText(text: String) {
6 | self.text = text
7 | }
8 | }
9 |
10 | #endif
11 |
--------------------------------------------------------------------------------
/Sources/FlowLayout/Header/HeaderTitle/HeaderTitle.swift:
--------------------------------------------------------------------------------
1 | #if os(iOS)
2 | import UIKit
3 |
4 | final class HeaderTitle: UILabel {
5 | override init(frame: CGRect) {
6 | super.init(frame: frame)
7 | self.font = HorView.style.header.title.font
8 | self.textAlignment = .center
9 | self.backgroundColor = .clear
10 | self.textColor = HorView.style.header.title.color
11 | }
12 | /**
13 | * Boilerplate
14 | */
15 | required init?(coder aDecoder: NSCoder) {
16 | fatalError("init(coder:) has not been implemented")
17 | }
18 | }
19 |
20 | #endif
21 |
--------------------------------------------------------------------------------
/Sources/FlowLayout/Header/Slider/Slider+Animation.swift:
--------------------------------------------------------------------------------
1 | #if os(iOS)
2 | import UIKit
3 | /**
4 | * Animation
5 | */
6 | extension Slider {
7 | typealias OnComplete = () -> Void
8 | /**
9 | * - Parameters:
10 | * - to: the amount to offset in the X dir
11 | * - onComplete: called when the animation completes
12 | */
13 | func animate(to: CGFloat, onComplete:@escaping OnComplete = {}) {
14 | Swift.print("Slider.animate: \(to)")
15 | UIView.animate({ // Animate
16 | self.setProgress(to: to)
17 | self.layoutIfNeeded()
18 | }, onComplete: onComplete)
19 | }
20 | }
21 |
22 | #endif
23 |
--------------------------------------------------------------------------------
/Sources/FlowLayout/Header/Slider/Slider+Const.swift:
--------------------------------------------------------------------------------
1 | #if os(iOS)
2 | import UIKit
3 |
4 | extension Slider {
5 | static let height: CGFloat = 10
6 | }
7 |
8 | #endif
9 |
--------------------------------------------------------------------------------
/Sources/FlowLayout/Header/Slider/Slider+Create.swift:
--------------------------------------------------------------------------------
1 | #if os(iOS)
2 | import UIKit
3 | import Spatial
4 | import With
5 | /**
6 | * Create
7 | */
8 | extension Slider {
9 | /**
10 | * Creates the slider bar
11 | */
12 | func createSliderBar() -> SliderBar {
13 | with(.init()) {
14 | addSubview($0)
15 | let sliderWidthMultiplier: CGFloat = 1 / CGFloat(segmentCount)
16 | $0.applyAnchorAndSize(to: self, height: Slider.height, align: .topLeft, alignTo: .topLeft, multiplier: .init(width: sliderWidthMultiplier, height: 1))
17 | }
18 | }
19 | }
20 |
21 | #endif
22 |
--------------------------------------------------------------------------------
/Sources/FlowLayout/Header/Slider/Slider+Setter.swift:
--------------------------------------------------------------------------------
1 | #if os(iOS)
2 | import UIKit
3 | import Spatial
4 | /**
5 | * Accesor
6 | */
7 | extension Slider {
8 | /**
9 | * Sets index
10 | */
11 | func setIdx(idx: Int) {
12 | let sliderBarWidth: CGFloat = self.frame.width / CGFloat(segmentCount)
13 | let x: CGFloat = sliderBarWidth * CGFloat(idx)
14 | animate(to: x)
15 | }
16 | /**
17 | * Set progress
18 | * - Parameter progress: 0 - 1
19 | */
20 | func setProgress(progress: CGFloat) {
21 | let sliderBarWidth: CGFloat = { self.frame.width / CGFloat(segmentCount) }()
22 | let x: CGFloat = sliderBarWidth * progress
23 | setProgress(to: x)
24 | }
25 | /**
26 | * - Parameter to: the x-position to set to
27 | */
28 | func setProgress(to: CGFloat) {
29 | self.sliderBar.update(offset: to, align: .left, alignTo: .left)
30 | }
31 | }
32 |
33 | #endif
34 |
--------------------------------------------------------------------------------
/Sources/FlowLayout/Header/Slider/Slider.swift:
--------------------------------------------------------------------------------
1 | #if os(iOS)
2 | import UIKit
3 | import Spatial
4 | /**
5 | * - Abstract: The topBar slider that indicates where you are horizontally
6 | */
7 | final class Slider: UIView {
8 | lazy var sliderBar: SliderBar = createSliderBar()
9 | let segmentCount: Int
10 | let idx: Int
11 | /**
12 | * - Fixme: ⚠️️ add doc
13 | */
14 | init(idx: Int, segmentCount: Int, frame: CGRect) {
15 | self.segmentCount = segmentCount
16 | self.idx = idx
17 | super.init(frame: frame)
18 | backgroundColor = HorView.style.slider.backgroundColor
19 | _ = sliderBar
20 | }
21 | /**
22 | * Boilerplate
23 | */
24 | @available(*, unavailable)
25 | required init?(coder aDecoder: NSCoder) {
26 | fatalError("init(coder:) has not been implemented")
27 | }
28 | }
29 |
30 | #endif
31 |
--------------------------------------------------------------------------------
/Sources/FlowLayout/Header/Slider/SliderBar/SliderBar.swift:
--------------------------------------------------------------------------------
1 | #if os(iOS)
2 | import UIKit
3 | import Spatial
4 |
5 | final class SliderBar: UIView, ConstraintKind {
6 | // - Fixme: ⚠️️ use anchorAndSize var
7 | var size: SizeConstraint?
8 | var anchor: AnchorConstraint?
9 | override init(frame: CGRect = .zero) {
10 | super.init(frame: frame)
11 | backgroundColor = HorView.style.slider.foregroundColor
12 | }
13 | /**
14 | * Boilerplate
15 | */
16 | required init?(coder aDecoder: NSCoder) {
17 | fatalError("init(coder:) has not been implemented")
18 | }
19 | }
20 |
21 | #endif
22 |
--------------------------------------------------------------------------------
/Sources/FlowLayout/HorView/HorView+Collection.swift:
--------------------------------------------------------------------------------
1 | #if os(iOS)
2 | import UIKit
3 | import ReusableCell
4 | /**
5 | * CollectionView related
6 | */
7 | extension HorView {
8 | /**
9 | * Num of items in table
10 | */
11 | public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
12 | items.count
13 | }
14 | /**
15 | * Respawns cells
16 | * - Fixme: ⚠️️ use simpler code for dequeuing cells, see cell frameworks
17 | */
18 | public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
19 | let cell: HorCell = {
20 | switch indexPath.row {
21 | case ColumnCellType.primary.idx:
22 | let cell: PrimaryHorCell = collectionView.dequeueReusableCell(indexPath: indexPath)
23 | let imageURLStr: String = "https://rawgit.com/stylekit/img/master/" + "pic_1_thumb.png"
24 | _ = imageURLStr
25 | let urls: [String] = .init(repeating: "🎉", count: 11) // ["a","b","c","d","a","b","c","d","a","b","c"]
26 | cell.data = PrimaryCellData(thumbURLS: urls) // When you set this, the data is applied to the UI
27 | return cell
28 | case ColumnCellType.secondary.idx:
29 | guard let cell: SecondaryHorCell = collectionView.dequeueReusableCell(withReuseIdentifier: SecondaryHorCell.id, for: indexPath as IndexPath) as? SecondaryHorCell else { fatalError("err") }
30 | return cell
31 | case ColumnCellType.tertiary.idx:
32 | guard let cell: TertiaryHorCell = collectionView.dequeueReusableCell(withReuseIdentifier: TertiaryHorCell.id, for: indexPath as IndexPath) as? TertiaryHorCell else { fatalError("err") }
33 | return cell
34 | default: fatalError("err: \(indexPath.row)")
35 | }
36 | }()
37 | // We add onScroll callback to every cell
38 | cell.onScroll = onVerticalScroll // Attach scoll-call-back-closure
39 | // cell.onItemSelect = {indexPath in Swift.print("HorView.cell.onItemSelect: \(indexPath) in cellIdx:\(self.currentPageIndex)")}//callback for cell click
40 | return cell
41 | }
42 | }
43 | /**
44 | * This method is interesting, might be able to have different sized cells in the same collectionview
45 | */
46 | // func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
47 | // return CGSize(width: self.frame.width, height: self.frame.height - 50)
48 | // }
49 |
50 | #endif
51 |
--------------------------------------------------------------------------------
/Sources/FlowLayout/HorView/HorView+Create.swift:
--------------------------------------------------------------------------------
1 | #if os(iOS)
2 | import UIKit
3 | import Spatial
4 | import With
5 | /**
6 | * HorView
7 | */
8 | extension HorView {
9 | /**
10 | * Creates header
11 | */
12 | @objc open func createHeader() -> Header {
13 | with(.init()) { // Header has title, buttons, slider
14 | addSubview($0)
15 | $0.anchorAndSize(to: self, height: Header.height)
16 | $0.buttonContainer.onButtonClick = onButtonClick
17 | }
18 | }
19 | /**
20 | * Creates horizontal collectionview
21 | * - Note: Overridable in subclass for customization
22 | * - Fixme: ⚠️️ rename to createColumnView
23 | */
24 | @objc open func createCollectionView() -> UICollectionView {
25 | let flowLayout: UICollectionViewFlowLayout = createLayout()
26 | return with(.init(frame: self.frame, collectionViewLayout: flowLayout)) {
27 | $0.dataSource = self // We must set datasource because the framework is view based not VC based
28 | $0.delegate = self // We must set delegate because the framework is view based not VC based
29 | $0.backgroundColor = .clear
30 | $0.contentInset = .zero // Reset contentInset
31 | $0.scrollIndicatorInsets = .zero // Reset scrollIndicator insets
32 | $0.isPagingEnabled = true // Makes each cell snap to whole integers
33 | $0.showsHorizontalScrollIndicator = false // Hides scrollbar
34 | self.insertSubview($0, belowSubview: header)
35 | $0.anchorAndSize(to: self)
36 | }
37 | }
38 | }
39 | /**
40 | * Create
41 | */
42 | extension HorView {
43 | /**
44 | * Returns layout
45 | */
46 | private func createLayout() -> UICollectionViewFlowLayout {
47 | with(.init()) {
48 | $0.itemSize = UIScreen.main.bounds.size // The size of each collection item
49 | $0.scrollDirection = .horizontal // Enables horizontal scrolling direction
50 | $0.minimumLineSpacing = 0 // Removes the line-spacing between cells
51 | }
52 | }
53 | }
54 |
55 | #endif
56 |
--------------------------------------------------------------------------------
/Sources/FlowLayout/HorView/HorView+Event.swift:
--------------------------------------------------------------------------------
1 | #if os(iOS)
2 | import UIKit
3 | /**
4 | * Event
5 | */
6 | extension HorView {
7 | /**
8 | * On vertical scroll callback
9 | * 1. Make Header-Button-Container stick to top
10 | * 2. Make Header stick to downward movement
11 | * 3. Allow Table to slide under Header
12 | */
13 | func onVerticalScroll(yOffset: CGFloat) {
14 | let y: CGFloat = (-1 * yOffset) - Header.height // offset y starts from 0 (easier to reason about)
15 | let headerY: CGFloat = {
16 | if y <= 0 && y >= -HeaderTitle.height { return y } // if y is negative, and y is within -HeaderTitle.height
17 | else if y < -HeaderTitle.height { return -HeaderTitle.height } // if y is beyond -Header.height, then only hide header-title
18 | else { return y } // if y is possetive, then set headerY directly
19 | }()
20 | self.header.frame.origin.y = headerY // - Fixme: ⚠️️ rather use Autolayout to update this
21 | }
22 | }
23 |
24 | #endif
25 |
--------------------------------------------------------------------------------
/Sources/FlowLayout/HorView/HorView+Getter.swift:
--------------------------------------------------------------------------------
1 | #if os(iOS)
2 | import QuartzCore
3 |
4 | extension HorView {
5 | /**
6 | * Calculates the current page
7 | */
8 | internal var currentPageIndex: Int {
9 | let x: CGFloat = collectionView.contentOffset.x
10 | let w: CGFloat = collectionView.bounds.size.width
11 | return .init(round(x / w))
12 | }
13 | }
14 |
15 | #endif
16 |
--------------------------------------------------------------------------------
/Sources/FlowLayout/HorView/HorView+Register.swift:
--------------------------------------------------------------------------------
1 | #if os(iOS)
2 | import UIKit
3 | import ReusableCell
4 | /**
5 | * Core
6 | */
7 | extension HorView {
8 | /**
9 | * Register cell types
10 | * - Fixme: ⚠️️ make the [].register call
11 | */
12 | @objc func registerCellTypes() {
13 | collectionView.register([PrimaryHorCell.self, SecondaryHorCell.self, TertiaryHorCell.self].map { $0 as HorCell.Type }) // Register Cells with ease
14 | }
15 | }
16 |
17 | #endif
18 |
--------------------------------------------------------------------------------
/Sources/FlowLayout/HorView/HorView+Scroll.swift:
--------------------------------------------------------------------------------
1 | #if os(iOS)
2 | import UIKit
3 | /**
4 | * Scrolling
5 | * - Note: Pagining in iOS is snapping to whole pages
6 | * - Reference: Horizontal CollectionView: https://github.com/maximbilan/UICollectionViewHorizontalPaging
7 | */
8 | extension HorView {
9 | /**
10 | * - Fixme: ⚠️️ Might want to make a bool flag to avoid calling if the page was the same as the last
11 | */
12 | public func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
13 | header.setTitleIdx(idx: currentPageIndex)
14 | header.buttonContainer.setIdx(idx: currentPageIndex)
15 | }
16 | /**
17 | * Called when the user scrolls in the horizontal direction
18 | * - Important: ⚠️️ You want to reference the param instead of self.collectionView, as the collection view isn't ready when viewDidScroll is called. It's called on the init of the app.
19 | */
20 | public func scrollViewDidScroll(_ scrollView: UIScrollView) {
21 | let x = scrollView.contentOffset.x
22 | let w = scrollView.bounds.size.width
23 | let currentProgress = x / w
24 | header.slider.setProgress(progress: currentProgress) // Moves the slider left and right
25 | }
26 | }
27 |
28 | #endif
29 |
--------------------------------------------------------------------------------
/Sources/FlowLayout/HorView/HorView+Setter.swift:
--------------------------------------------------------------------------------
1 | #if os(iOS)
2 | import Foundation
3 |
4 | extension HorView {
5 | /**
6 | * Set index
7 | * - Fixme: ⚠️️ add doc
8 | */
9 | func setIdx(idx: Int) {
10 | header.setTitleIdx(idx: idx)
11 | setCollectionViewIndex(idx: idx)
12 | }
13 | /**
14 | * Scrolls to collectionview index
15 | */
16 | func setCollectionViewIndex(idx: Int) {
17 | let indexPath = IndexPath(row: idx, section: 0)
18 | self.collectionView.scrollToItem(at: indexPath, at: .right, animated: true)
19 | collectionView.setNeedsLayout() // ⚠️️ might not be needed
20 | collectionView.layoutIfNeeded() // ⚠️️ might not be needed
21 | }
22 | }
23 |
24 | #endif
25 |
--------------------------------------------------------------------------------
/Sources/FlowLayout/HorView/HorView+Style.swift:
--------------------------------------------------------------------------------
1 | #if os(iOS)
2 | import UIKit
3 | /**
4 | * Type
5 | * - Fixme: ⚠️️ maybe move into own HorStyle scope?
6 | */
7 | extension HorView {
8 | typealias Style = (header: HeaderStyle, backgroundColor: UIColor, slider: SliderStyle, horCollectionViewBackgroundColor: UIColor)
9 | typealias HeaderStyle = (backgroundColor: UIColor, title: HeaderTitleStyle, button: HeaderButtonStyle)
10 | typealias HeaderTitleStyle = (color: UIColor, font: UIFont)
11 | typealias HeaderButtonStyle = (selectedFontColor: UIColor, unSelectedFontColor: UIColor, selectedFont: UIFont, unSelectedFont: UIFont)
12 | typealias SliderStyle = (backgroundColor: UIColor, foregroundColor: UIColor)
13 | }
14 | /**
15 | * Constant
16 | */
17 | extension HorView {
18 | static let style: Style = (headerStyle, .blue, sliderStyle, .lightGray)
19 | private static let headerStyle: HeaderStyle = (.blue, headerTitleStyle, headerButtonStyle)
20 | private static let headerTitleStyle: HeaderTitleStyle = (.white, .systemFont(ofSize: 22))
21 | private static let headerButtonStyle: HeaderButtonStyle = (.white, .black, .boldSystemFont(ofSize: 16), .systemFont(ofSize: 16))
22 | private static let sliderStyle: SliderStyle = (.blue, .white)
23 | }
24 |
25 | #endif
26 |
--------------------------------------------------------------------------------
/Sources/FlowLayout/HorView/HorView+Update.swift:
--------------------------------------------------------------------------------
1 | #if os(iOS)
2 | import UIKit
3 | /**
4 | * Update
5 | */
6 | extension HorView {
7 | /**
8 | * Gets all the transactions and updates the TableView items
9 | * - Abstract: We call this after we receive data from a webservice
10 | * - Fixme: ⚠️️ Add weak self here
11 | */
12 | func updateCollectionView() {
13 | DispatchQueue.main.async { // Jump on the main thread for UI update
14 | self.items = [.primary, .secondary, .tertiary] // 3, 4, 5, 5, 7, 8, 9, 10, 11, 12
15 | self.collectionView.reloadData() // Updates collectionView
16 | }
17 | }
18 | /**
19 | * onButtonClick (When user clicks header buttons)
20 | * - Fixme: ⚠️️ Move to +Event file
21 | */
22 | func onButtonClick(buttonTitle: String) {
23 | if let cellType = ColumnCellType(rawValue: buttonTitle.lowercased()) {
24 | guard let idx: Int = cellType.idx else { return } // 0, 1, 2 converts button to index
25 | self.setIdx(idx: idx)
26 | self.header.buttonContainer.setIdx(idx: idx)
27 | }
28 | }
29 | }
30 |
31 | #endif
32 |
--------------------------------------------------------------------------------
/Sources/FlowLayout/HorView/HorView.swift:
--------------------------------------------------------------------------------
1 | #if os(iOS)
2 | import UIKit
3 | /**
4 | * - Abstract: the Horisontal view holds the collectionView for the vertical cells
5 | * - Fixme: ⚠️️ In theory it could be an Idea to extend UICollectionView directly, instead of UIView
6 | */
7 | open class HorView: UIView, UICollectionViewDataSource, UICollectionViewDelegate {
8 | lazy var header: Header = self.createHeader() // Adds the header
9 | // - Fixme: ⚠️️ rename to collumnsView?
10 | lazy var collectionView: UICollectionView = self.createCollectionView() // Adds the collectionView
11 | // - Fixme: ⚠️️ maybe rename to columns?
12 | var items: [ColumnCellType] // Aka columns
13 | override public init(frame: CGRect = .zero) {
14 | items = [.primary, .secondary, .tertiary]
15 | super.init(frame: frame)
16 | self.backgroundColor = HorView.style.backgroundColor
17 | _ = header
18 | _ = collectionView // Inits the collectionView, works with empty data, before we get data from remote
19 | registerCellTypes() // Register cells
20 | // updateCollectionView() // updates the collection view with new data etc
21 | }
22 | /**
23 | * Boilerplate
24 | */
25 | @available(*, unavailable)
26 | public required init?(coder aDecoder: NSCoder) {
27 | fatalError("init(coder:) has not been implemented")
28 | }
29 | }
30 |
31 | #endif
32 |
--------------------------------------------------------------------------------
/Sources/FlowLayout/HorView/utils/ColumnCellType.swift:
--------------------------------------------------------------------------------
1 | #if os(iOS)
2 | import Foundation
3 | /**
4 | * CellType
5 | * - Fixme: ⚠️️ rename to ColumnCellType
6 | */
7 | enum ColumnCellType: String, CaseIterable {
8 | case primary, secondary, tertiary
9 | }
10 | /**
11 | * Getter
12 | */
13 | extension ColumnCellType {
14 | var idx: Int? { ColumnCellType.allCases.firstIndex(of: self) }
15 | }
16 |
17 | #endif
18 |
--------------------------------------------------------------------------------
/Sources/FlowLayout/cell/horizontal/HorCell/HorCell+Collection.swift:
--------------------------------------------------------------------------------
1 | #if os(iOS)
2 | import UIKit
3 | /**
4 | * CollectionView related
5 | */
6 | extension HorCell: UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
7 | /**
8 | * Num of items in section
9 | */
10 | func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
11 | self.count
12 | }
13 | /**
14 | * Respawns cells @ indexPath
15 | */
16 | func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
17 | dequeCell(cellForItemAt: indexPath)
18 | }
19 | /**
20 | * On item click
21 | */
22 | func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
23 | onItemSelect(indexPath)
24 | }
25 | }
26 |
27 | #endif
28 |
--------------------------------------------------------------------------------
/Sources/FlowLayout/cell/horizontal/HorCell/HorCell+Const.swift:
--------------------------------------------------------------------------------
1 | #if os(iOS)
2 | import Foundation
3 | import QuartzCore
4 |
5 | extension HorCell {
6 | open class var defaultReuseIdentifier: String { String(describing: self) } // fatalError("must be overrideen in subclass")
7 | static let margin: CGFloat = 32
8 | // Default callbacks
9 | static let defaultOnItemSelect: OnItemSelect = { indexPath in Swift.print("indexPath: \(indexPath)") }
10 | static let defaultOnScroll: OnScroll = { _ in Swift.print("HorCell.scroll - no callback attached") }
11 | }
12 | #endif
13 |
--------------------------------------------------------------------------------
/Sources/FlowLayout/cell/horizontal/HorCell/HorCell+Core.swift:
--------------------------------------------------------------------------------
1 | #if os(iOS)
2 | import UIKit
3 | /**
4 | * Core
5 | */
6 | extension HorCell {
7 | /**
8 | * Override in sub class
9 | * - Note: This method exists so that we can subclass HorCell and register different child cell types
10 | * - Fixme: ⚠️️ Replace this method with accociate type etc. See ref link in ViewController.swift
11 | */
12 | @objc func registerCell(collectionView: UICollectionView) {
13 | collectionView.register(VerCell.self, forCellWithReuseIdentifier: VerCell.id)
14 | }
15 | /**
16 | * Override in sub class
17 | * - Note: This method exists so that we can subclass HorCell and register different child cell types
18 | * - Fixme: ⚠️️ Replace this method with accociate type etc. See ref link in ViewController.swift
19 | */
20 | @objc func dequeCell(cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
21 | guard let cell: VerCell = collectionView.dequeueReusableCell(withReuseIdentifier: VerCell.id, for: indexPath as IndexPath) as? VerCell else { fatalError("err") }
22 | return cell
23 | }
24 | }
25 | #endif
26 |
--------------------------------------------------------------------------------
/Sources/FlowLayout/cell/horizontal/HorCell/HorCell+Create.swift:
--------------------------------------------------------------------------------
1 | #if os(iOS)
2 | import UIKit
3 | import With
4 | import Spatial
5 | /**
6 | * Create
7 | */
8 | extension HorCell {
9 | /**
10 | * This adds the custom look to the table (Overridable)
11 | * - Note: overrideable with subclass
12 | * - Note: flowLayout.scrollDirection is vertical by default
13 | */
14 | @objc open func createLayout() -> UICollectionViewFlowLayout {
15 | with(.init()) {
16 | let margin = HorCell.margin
17 | $0.sectionInset = .init(top: margin, left: margin, bottom: margin, right: margin)
18 | $0.minimumInteritemSpacing = 0
19 | $0.minimumLineSpacing = margin // Vertical spacing
20 | let itemSize: CGSize = {
21 | let screenWidth: CGFloat = UIScreen.main.bounds.size.width
22 | let width: CGFloat = (screenWidth - (margin * 3)) / 2
23 | let height: CGFloat = width // width + (width * 0.33)
24 | return .init(width: width, height: height)
25 | }()
26 | $0.itemSize = itemSize
27 | }
28 | }
29 | /**
30 | * Creates collectionview
31 | */
32 | func createCollectionView() -> UICollectionView {
33 | let layout: UICollectionViewFlowLayout = createLayout()
34 | return with(.init(frame: self.frame, collectionViewLayout: layout)) {
35 | $0.dataSource = self // We must set datasource because the framework is view based not VC based
36 | $0.delegate = self // We must set delegate because the framework is view based not VC based
37 | $0.contentInset = .init(top: Header.height, left: 0, bottom: 0, right: 0) // offsets the content, so that sticky header works etc
38 | $0.scrollIndicatorInsets = .init(top: Header.height, left: 0, bottom: 0, right: 0)
39 | self.registerCell(collectionView: $0) // Register cell kind
40 | $0.backgroundColor = .clear // The HorCell it self also has a bg so we set this to clear
41 | self.addSubview($0)
42 | // ⚠️️ We have to use a constraint or else the double UICollection setup starts to behave strangly ⚠️️
43 | $0.anchorAndSize(to: self) // The view needs to use constraints or else AutoLayout wont work with dual UICollectionView
44 | }
45 | }
46 | }
47 | #endif
48 |
--------------------------------------------------------------------------------
/Sources/FlowLayout/cell/horizontal/HorCell/HorCell+Getter.swift:
--------------------------------------------------------------------------------
1 | #if os(iOS)
2 | import Foundation
3 | /**
4 | * Constants
5 | */
6 | extension HorCell {
7 | @objc class var id: String { "\(HorCell.self)" } // Stores the deque cell id, overrideable in subclasses
8 | @objc var count: Int { items.count } // override this in subclasses
9 | }
10 | #endif
11 |
--------------------------------------------------------------------------------
/Sources/FlowLayout/cell/horizontal/HorCell/HorCell+Scroll.swift:
--------------------------------------------------------------------------------
1 | #if os(iOS)
2 | import UIKit
3 | /**
4 | * Scroll
5 | */
6 | extension HorCell {
7 | /**
8 | * Used to make the NavBar stick to the tableView
9 | * - Note: Calls a callback that notifies external UI elements to reposition etc
10 | * - Note: You can get the statusBarheight from UIApplication.shared.statusBarFrame.height if you also want to hide the statusbar
11 | */
12 | func scrollViewDidScroll(_ scrollView: UIScrollView) {
13 | onScroll(scrollView.contentOffset.y)
14 | }
15 | }
16 | #endif
17 |
--------------------------------------------------------------------------------
/Sources/FlowLayout/cell/horizontal/HorCell/HorCell+Type.swift:
--------------------------------------------------------------------------------
1 | #if os(iOS)
2 | import UIKit
3 | /**
4 | * Helper
5 | */
6 | extension HorCell {
7 | typealias OnScroll = (_ yOffset: CGFloat) -> Void // Needed to get the sticky header to work
8 | typealias OnItemSelect = (_ indexPath: IndexPath) -> Void
9 | }
10 | #endif
11 |
--------------------------------------------------------------------------------
/Sources/FlowLayout/cell/horizontal/HorCell/HorCell+Update.swift:
--------------------------------------------------------------------------------
1 | #if os(iOS)
2 | import Foundation
3 | /**
4 | * Update
5 | */
6 | extension HorCell {
7 | /**
8 | * Updates the TableView items
9 | */
10 | func updateCollectionView() {
11 | // Swift.print("Update collectionView👌")
12 | DispatchQueue.main.async { // jump on the main thread for UI update
13 | //Swift.print("got json from remote 👌")
14 | self.items = [0, 1, 2, 3, 4, 5, 7]
15 | self.collectionView.reloadData() // Updates collectionView
16 | }
17 | }
18 | }
19 |
20 | #endif
21 |
--------------------------------------------------------------------------------
/Sources/FlowLayout/cell/horizontal/HorCell/HorCell.swift:
--------------------------------------------------------------------------------
1 | #if os(iOS)
2 | import UIKit
3 | import With
4 | import Spatial
5 | import ReusableCell
6 | /**
7 | * - Abstract A horizontal Cell that has a vertical collectionView
8 | * - Note: This class is later subclassed as a generic class, and as such overriding things in extension doesn't work
9 | * - Fixme: ⚠️️ rename to ColumnCell?
10 | */
11 | class HorCell: UICollectionViewCell, ReusableCellKind {
12 | lazy var collectionView: UICollectionView = self.createCollectionView() // Vertical collection view
13 | var items: [Int] // Vertical items
14 | var onScroll: OnScroll = defaultOnScroll
15 | var onItemSelect: OnItemSelect = defaultOnItemSelect
16 | var data: CellDataKind? // Stores cellData
17 | /**
18 | * Initiate
19 | */
20 | override init(frame: CGRect) {
21 | items = [0, 1, 2, 3, 4, 5, 7, 8, 9] // Dummy model data
22 | super.init(frame: frame)
23 | _ = collectionView // Initiates the collectionView, works with empty data, before we get data from remote
24 | self.backgroundColor = HorView.style.horCollectionViewBackgroundColor
25 | // updateCollectionView() // Updates the collection view with data
26 | }
27 | /**
28 | * Boilerplate
29 | */
30 | @available(*, unavailable)
31 | required init?(coder aDecoder: NSCoder) {
32 | fatalError("init(coder:) has not been implemented")
33 | }
34 | }
35 |
36 | #endif
37 |
--------------------------------------------------------------------------------
/Sources/FlowLayout/cell/horizontal/VerCell/VerCell.swift:
--------------------------------------------------------------------------------
1 | #if os(iOS)
2 | import UIKit
3 | /**
4 | * Browser cell for CollectionView
5 | * - Fixme: ⚠️️ rename to rowcell?
6 | */
7 | class VerCell: UICollectionViewCell {
8 | class var id: String { "\(VerCell.self)" } // Stores the deque cell id, overrideable in subclasses
9 | override init(frame: CGRect) {
10 | super.init(frame: frame)
11 | self.backgroundColor = UIColorParser.random
12 | }
13 | /**
14 | * Boilerplate
15 | */
16 | @available(*, unavailable)
17 | required init?(coder aDecoder: NSCoder) {
18 | fatalError("init(coder:) has not been implemented")
19 | }
20 | }
21 |
22 | #endif
23 |
--------------------------------------------------------------------------------
/Sources/FlowLayout/cell/horizontal/VerCell/custom/ImageCell/ImageCell+Create.swift:
--------------------------------------------------------------------------------
1 | #if os(iOS)
2 | import UIKit
3 | import With
4 | import Spatial
5 | /**
6 | * Create
7 | */
8 | extension ImageCell {
9 | /**
10 | * Creates ImgView
11 | */
12 | func createImgView() -> UIImageView {
13 | with(.init()) { // Create the image view
14 | $0.contentMode = .scaleAspectFill // Fills the view
15 | $0.backgroundColor = .lightGray
16 | // adds a ciruclar mask to the image-view
17 | let cornerRadius = self.contentView.frame.height / 2
18 | $0.layer.cornerRadius = cornerRadius
19 | $0.clipsToBounds = true
20 | self.contentView.addSubview($0)
21 | // adds constraints
22 | $0.translatesAutoresizingMaskIntoConstraints = false
23 | $0.anchorAndSize(to: contentView )
24 | }
25 | }
26 | }
27 |
28 | #endif
29 |
--------------------------------------------------------------------------------
/Sources/FlowLayout/cell/horizontal/VerCell/custom/ImageCell/ImageCell.swift:
--------------------------------------------------------------------------------
1 | #if os(iOS)
2 | import UIKit
3 | /**
4 | * Beta (Experimental)
5 | */
6 | class ImageCell: UITableViewCell {
7 | lazy var imgView: UIImageView = self.createImgView()
8 | override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
9 | super.init(style: style, reuseIdentifier: reuseIdentifier)
10 | self.selectionStyle = .none // Disables the selection highlighting
11 | _ = imgView // Initiate UI's
12 | }
13 | @available(*, unavailable)
14 | required init?(coder aDecoder: NSCoder) {
15 | fatalError("init(coder:) has not been implemented")
16 | }
17 | }
18 |
19 | #endif
20 |
--------------------------------------------------------------------------------
/Sources/FlowLayout/cell/horizontal/VerCell/custom/PrimaryVerCell/PrimaryVerCell+Core.swift:
--------------------------------------------------------------------------------
1 | #if os(iOS)
2 | import UIKit
3 | import With
4 | /**
5 | * Core
6 | */
7 | extension PrimaryVerCell {
8 | /**
9 | * If the view is redrawn then this is called
10 | */
11 | override func layoutSubviews() {
12 | super.layoutSubviews()
13 | drawCircle()
14 | }
15 | /**
16 | * Adds a UIView that is round
17 | * - Fixme: ⚠️️ Use CGShapeUtil and draw a circle instead, faster and more performant
18 | */
19 | func drawCircle() {
20 | let rect: CGRect = .init(origin: .zero, size: .init(width: self.frame.width, height: self.frame.width))
21 | self.subviews.first?.removeFromSuperview() // Removes previouse graphics if it exists
22 | with(UIView(frame: rect)) {
23 | $0.layer.cornerRadius = self.frame.width / 2
24 | $0.layer.masksToBounds = true
25 | $0.backgroundColor = UIColorParser.random
26 | addSubview($0)
27 | }
28 | }
29 | }
30 |
31 | #endif
32 |
--------------------------------------------------------------------------------
/Sources/FlowLayout/cell/horizontal/VerCell/custom/PrimaryVerCell/PrimaryVerCell.swift:
--------------------------------------------------------------------------------
1 | #if os(iOS)
2 | import UIKit
3 | import With
4 | /**
5 | * Primary vertical cell
6 | */
7 | class PrimaryVerCell: VerCell {
8 | override class var id: String { "\(PrimaryHorCell.self)" } // Stores the deque cell id
9 | override init(frame: CGRect) {
10 | super.init(frame: frame)
11 | self.backgroundColor = .clear
12 | }
13 | }
14 |
15 | #endif
16 |
--------------------------------------------------------------------------------
/Sources/FlowLayout/cell/horizontal/custom/PrimaryCellData/CellDataKind.swift:
--------------------------------------------------------------------------------
1 | #if os(iOS)
2 | import Foundation
3 | /**
4 | * Base protocol for Horizontal cells
5 | */
6 | protocol CellDataKind {}
7 |
8 | #endif
9 |
--------------------------------------------------------------------------------
/Sources/FlowLayout/cell/horizontal/custom/PrimaryCellData/PrimaryCellData.swift:
--------------------------------------------------------------------------------
1 | #if os(iOS)
2 | import Foundation
3 |
4 | struct PrimaryCellData: CellDataKind {
5 | let thumbURLS: [String]
6 | }
7 |
8 | #endif
9 |
--------------------------------------------------------------------------------
/Sources/FlowLayout/cell/horizontal/custom/PrimaryHorCell/PrimaryHorCell+Core.swift:
--------------------------------------------------------------------------------
1 | #if os(iOS)
2 | import UIKit
3 | import With
4 | /**
5 | * Core
6 | */
7 | extension PrimaryHorCell {
8 | /**
9 | * Register cells for CollectionView
10 | */
11 | override func registerCell(collectionView: UICollectionView) {
12 | collectionView.register(PrimaryVerCell.self, forCellWithReuseIdentifier: PrimaryVerCell.id)
13 | }
14 | /**
15 | * Deques cells for CollectionView
16 | * - Fixme: ⚠️️ Remove this method, it's superflouse, rather just override the caller
17 | */
18 | override func dequeCell(cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
19 | guard let cell: PrimaryVerCell = collectionView.dequeueReusableCell(withReuseIdentifier: PrimaryVerCell.id, for: indexPath as IndexPath) as? PrimaryVerCell else { fatalError("err") }
20 | if let thumbURL: String = primaryCellData?.thumbURLS[indexPath.row] {
21 | _ = thumbURL
22 | // Swift.print("PrimaryHorCell.dequeCell() thumbURL: \(thumbURL)")
23 | //cell.thumbImage = UIImage(url:thumbURL)
24 | //cell.imgView.setImage(webPath: thumbURL)
25 | }
26 | return cell
27 | }
28 | /**
29 | * Alternate layout
30 | * - Fixme: ⚠️️ add more doc
31 | */
32 | func createAlternateLayout() -> UICollectionViewFlowLayout {
33 | with(.init()) {
34 | let margin: CGFloat = SecondaryHorCell.margin
35 | $0.sectionInset = .init(top: margin, left: margin, bottom: margin, right: margin)
36 | $0.minimumInteritemSpacing = 0
37 | $0.minimumLineSpacing = margin // vertical spacing
38 | let itemSize: CGSize = {
39 | let screenWidth: CGFloat = UIScreen.main.bounds.size.width
40 | let width: CGFloat = screenWidth - (margin * 2)
41 | let height: CGFloat = width
42 | return .init(width: width, height: height)
43 | }()
44 | $0.itemSize = itemSize//CGSize(width: 70, height: 70)
45 | }
46 | }
47 | }
48 |
49 | #endif
50 |
--------------------------------------------------------------------------------
/Sources/FlowLayout/cell/horizontal/custom/PrimaryHorCell/PrimaryHorCell+Getter.swift:
--------------------------------------------------------------------------------
1 | #if os(iOS)
2 | import Foundation
3 | /**
4 | * Getter
5 | */
6 | extension PrimaryHorCell {
7 | override class var id: String { "\(PrimaryHorCell.self)" } // Used for dequeing cells
8 | var primaryCellData: PrimaryCellData? { data as? PrimaryCellData }
9 | override var count: Int { primaryCellData?.thumbURLS.count ?? 0 }
10 | }
11 |
12 | #endif
13 |
--------------------------------------------------------------------------------
/Sources/FlowLayout/cell/horizontal/custom/PrimaryHorCell/PrimaryHorCell+Interaction.swift:
--------------------------------------------------------------------------------
1 | #if os(iOS)
2 | import Foundation
3 | /**
4 | * Interaction
5 | */
6 | extension PrimaryHorCell {
7 | /**
8 | * - Description: Example how we can change the layout of the collectionview on interaction
9 | */
10 | func itemSelect(indexPath: IndexPath) {
11 | // Swift.print("PrimaryCell.itemSelect() - indexPath: \(indexPath)")
12 | collectionView.collectionViewLayout.invalidateLayout()
13 | collectionView.collectionViewLayout = createAlternateLayout()
14 | updateCollectionView()
15 | }
16 | }
17 | #endif
18 |
19 | // let itemSize:CGSize = {
20 | // let screenWidth:CGFloat = UIScreen.main.bounds.size.width
21 | // let width:CGFloat = screenWidth - (SecondaryCell.margin*2)
22 | // let height:CGFloat = width
23 | // return .init(width: width, height: height)
24 | // }()
25 | // collectionView.visibleCells.forEach{$0.frame = .init(origin:$0.frame.origin,size:itemSize)}
26 |
--------------------------------------------------------------------------------
/Sources/FlowLayout/cell/horizontal/custom/PrimaryHorCell/PrimaryHorCell.swift:
--------------------------------------------------------------------------------
1 | #if os(iOS)
2 | import UIKit
3 | import With
4 | /**
5 | * - Fixme: ⚠️️ Rename to PrimaryHorCell
6 | */
7 | final class PrimaryHorCell: HorCell {
8 | override init(frame: CGRect) {
9 | super.init(frame: frame)
10 | self.onItemSelect = itemSelect
11 | }
12 | /**
13 | * When you set the data, the diferent UI's will be updated
14 | */
15 | override var data: CellDataKind? {
16 | didSet {
17 | guard let data = data else { fatalError("data not avaiable") }
18 | _ = data
19 | updateCollectionView()/* Updates the collection view with data */
20 | }
21 | }
22 | }
23 |
24 | #endif
25 |
--------------------------------------------------------------------------------
/Sources/FlowLayout/cell/horizontal/custom/SecondaryHorCell/SecondaryHorCell+Const.swift:
--------------------------------------------------------------------------------
1 | #if os(iOS)
2 | import UIKit
3 | /**
4 | * Const
5 | */
6 | extension SecondaryHorCell {
7 | @objc override class var id: String { "\(SecondaryHorCell.self)" }
8 | }
9 |
10 | #endif
11 |
--------------------------------------------------------------------------------
/Sources/FlowLayout/cell/horizontal/custom/SecondaryHorCell/SecondaryHorCell+Create.swift:
--------------------------------------------------------------------------------
1 | #if os(iOS)
2 | import UIKit
3 | import With
4 | /**
5 | * Create
6 | */
7 | extension SecondaryHorCell {
8 | /**
9 | * Adds portrait cell sized design
10 | * - Note: this can also be done after the fact via: self.collectionView.setCollectionViewLayout(customLayout, animated: false), but this isnt as optimized
11 | */
12 | override func createLayout() -> UICollectionViewFlowLayout {
13 | with(.init()) {
14 | let margin: CGFloat = SecondaryHorCell.margin
15 | $0.sectionInset = .init(top: margin, left: margin, bottom: margin, right: margin)
16 | $0.minimumInteritemSpacing = 0
17 | $0.minimumLineSpacing = margin // vertical spacing
18 | $0.itemSize = with(.zero) {
19 | let screenWidth: CGFloat = UIScreen.main.bounds.size.width
20 | $0.width = (screenWidth - (margin * 3)) / 2
21 | $0.height = $0.width + ($0.width * 0.33)
22 | }
23 | }
24 | }
25 | }
26 |
27 | #endif
28 |
--------------------------------------------------------------------------------
/Sources/FlowLayout/cell/horizontal/custom/SecondaryHorCell/SecondaryHorCell.swift:
--------------------------------------------------------------------------------
1 | #if os(iOS)
2 | import UIKit
3 |
4 | final class SecondaryHorCell: HorCell {
5 | override init(frame: CGRect) {
6 | super.init(frame: frame)
7 | }
8 | }
9 |
10 | #endif
11 |
--------------------------------------------------------------------------------
/Sources/FlowLayout/cell/horizontal/custom/TertiaryHorCell/TertiaryHorCell.swift:
--------------------------------------------------------------------------------
1 | #if os(iOS)
2 | import UIKit
3 |
4 | final class TertiaryHorCell: HorCell {
5 | override init(frame: CGRect) {
6 | super.init(frame: frame)
7 | }
8 | }
9 | /**
10 | * Const
11 | */
12 | extension TertiaryHorCell {
13 | @objc override class var id: String { "\(TertiaryHorCell.self)" }
14 | }
15 |
16 | #endif
17 |
--------------------------------------------------------------------------------
/Sources/FlowLayout/common/CGShapeUtil.swift:
--------------------------------------------------------------------------------
1 | #if os(iOS)
2 | import UIKit
3 | /**
4 | * - Fixme: ⚠️️ remove this, use GeomKit via spm instead or?
5 | */
6 | internal class CGShapeUtil {
7 | /**
8 | * Draw line
9 | * - NOTE: remember to: shapeLayer.addSublayer(lineLayer)
10 | * It's also possible to do this with UIBezierPath
11 | * let path:UIBezierPath = {
12 | * let aPath = UIBezierPath.init()//cgPath: linePath
13 | * aPath.move(to: p1)
14 | * aPath.addLine(to: p2)
15 | * aPath.close()//Keep using the method addLineToPoint until you get to the one where about to close the path
16 | * return aPath
17 | * }()
18 | */
19 | static func drawLine(lineLayer: CAShapeLayer, line: (p1: CGPoint, p2: CGPoint), style: (stroke: UIColor, strokeWidth: CGFloat)) -> CAShapeLayer {
20 | let lineLayer: CAShapeLayer = .init()
21 | let path: CGMutablePath = .init()
22 | path.move(to: line.p1)
23 | path.addLine(to: line.p2)
24 | lineLayer.path = path/*Setup the CAShapeLayer with the path, colors, and line width*/
25 | lineLayer.strokeColor = style.stroke.cgColor
26 | lineLayer.lineWidth = style.strokeWidth
27 | lineLayer.lineCap = .butt
28 | return lineLayer
29 | }
30 | /**
31 | * Draws circle
32 | * - PARAM: progress: 0-1
33 | * - Fixme: ⚠️️ write method for nsbez... that use bezierPath.appendBezierPathWithArcWithCenter as init
34 | */
35 | static func drawCircle(with circleLayer: CAShapeLayer, circle:(center: CGPoint, radius: CGFloat), style: (fill: UIColor, stroke: UIColor, strokeWidth: CGFloat), progress: CGFloat) -> CAShapeLayer {
36 | let origin: CGPoint = .init(x: circle.center.x - circle.radius, y: circle.center.y - circle.radius)
37 | let size: CGSize = .init(width: circle.radius * 2, height: circle.radius * 2)
38 | let rect: CGRect = .init(origin:origin, size: size)
39 | // let circlePath:BezierPath = .init(ovalIn: rect)
40 | let cgPath: CGPath = .init(ellipseIn: rect, transform: nil)
41 | //UIBezierPath(arcCenter: CGPoint(x: circle.center.x, y: circle.center.y), radius:circle.radius, startAngle: CGFloat(Double.pi * -0.5), endAngle: CGFloat(Double.pi * 1.5), clockwise: true)/*The path should be the entire circle, for the strokeEnd and strokeStart to work*/
42 | // - Fixme: ⚠️️ use with
43 | circleLayer.path = cgPath//circlePath.cgPath/*Setup the CAShapeLayer with the path, colors, and line width*/
44 | circleLayer.fillColor = style.fill.cgColor
45 | circleLayer.strokeColor = style.stroke.cgColor
46 | circleLayer.lineWidth = style.strokeWidth
47 | circleLayer.lineCap = .round
48 | circleLayer.strokeEnd = progress/*Sets progress of the stroke between predefined start and predefined end*/
49 | return circleLayer
50 | }
51 | }
52 |
53 | #endif
54 |
--------------------------------------------------------------------------------
/Sources/FlowLayout/common/UIColorParser.swift:
--------------------------------------------------------------------------------
1 | #if os(iOS)
2 | import UIKit
3 |
4 | internal class UIColorParser {
5 | /**
6 | * Returns random color
7 | * ## Examples: self.backgroundColor = UIColorParser.random
8 | */
9 | static var random: UIColor {
10 | let r: CGFloat = .random(in: (0..<255.0)) / 255.0
11 | let g: CGFloat = .random(in: (0..<255.0)) / 255.0
12 | let b: CGFloat = .random(in: (0..<255.0)) / 255.0
13 | let uiColor: UIColor = .init(red: r, green: g, blue: b, alpha: 1)
14 | return uiColor
15 | }
16 | }
17 |
18 | #endif
19 |
--------------------------------------------------------------------------------
/Sources/FlowLayout/common/UIView+Extension.swift:
--------------------------------------------------------------------------------
1 | #if os(iOS)
2 | import UIKit
3 | /**
4 | * Controller related
5 | */
6 | extension UIView {
7 | /**
8 | * Easily get Controller
9 | * - Fixme: ⚠️️ add more doc
10 | */
11 | internal func controller() -> UIViewController? {
12 | if let nextViewControllerResponder = next as? UIViewController {
13 | return nextViewControllerResponder
14 | } else if let nextViewResponder = next as? UIView {
15 | return nextViewResponder.controller()
16 | } else {
17 | return nil
18 | }
19 | }
20 | /**
21 | * Easily get navController from
22 | * - Fixme: ⚠️️ add more doc
23 | */
24 | internal func navigationController() -> UINavigationController? {
25 | guard let controller = controller() else { return nil }
26 | return controller.navigationController
27 | }
28 | }
29 |
30 | #endif
31 |
--------------------------------------------------------------------------------
/Sources/FlowLayout/common/image/UIImage+Extension.swift:
--------------------------------------------------------------------------------
1 | #if os(iOS)
2 | import UIKit
3 | /**
4 | * - Fixme: ⚠️️ Add a way to also get response, maybe look into result?
5 | * - Fixme: ⚠️️ remove this, use ImageKit via spm instead
6 | */
7 | extension UIImage {
8 | internal typealias DownloadComplete = (UIImage?, IMGError?) -> Void
9 | internal enum IMGError: Error {
10 | case invalideWebPath
11 | case imageDataIsCorrupted
12 | case errorGettingDataFromURL
13 | }
14 | /**
15 | * Get UIImage from webPath
16 | */
17 | internal static func image(webPath: String, onComplete:@escaping DownloadComplete) {
18 | guard let url = URL(string: webPath) else { onComplete(nil, .invalideWebPath); return }
19 | Utils.downloadImage(url: url, downloadComplete: onComplete)
20 | }
21 | }
22 | /**
23 | * Helpers
24 | */
25 | private class Utils {
26 | /**
27 | * Assign and convert data to Image
28 | */
29 | static func downloadImage(url: URL, downloadComplete:@escaping UIImage.DownloadComplete) {
30 | getDataFromUrl(url: url) { data, response, error in
31 | guard let data = data, error == nil else { downloadComplete(nil, .errorGettingDataFromURL); return }
32 | Swift.print(response?.suggestedFilename ?? url.lastPathComponent)
33 | guard let image = UIImage(data: data) else { downloadComplete(nil, .imageDataIsCorrupted); return }
34 | downloadComplete(image, nil)
35 | }
36 | }
37 | typealias URLQuery = (Data?, URLResponse?, Error?) -> Void
38 | /**
39 | * Get data from URL
40 | */
41 | private static func getDataFromUrl(url: URL, completion: @escaping URLQuery) {
42 | URLSession.shared.dataTask(with: url) { data, response, error in
43 | completion(data, response, error)
44 | }.resume()
45 | }
46 | }
47 |
48 | #endif
49 |
--------------------------------------------------------------------------------
/Sources/FlowLayout/common/image/UIImageView+Extension.swift:
--------------------------------------------------------------------------------
1 | #if os(iOS)
2 | import UIKit
3 | /**
4 | * - Fixme: ⚠️️ remove this, use ImageKit via spm instead
5 | */
6 | extension UIImageView {
7 | typealias OnError = (UIImage.IMGError?) -> Void
8 | static var defaultErrorHandler: OnError = { err in
9 | Swift.print("img error: \(String(describing: err))")
10 | }
11 | /**
12 | * Simplifies loading an image from a weburl to uiimageview
13 | */
14 | internal convenience init(webPath: String, useBGThread: Bool = true, onError:@escaping OnError = defaultErrorHandler) {
15 | self.init()
16 | setImage(webPath: webPath, useBGThread: useBGThread)
17 | }
18 | /**
19 | * Create UIIMageView from filePath
20 | */
21 | internal convenience init(filePath: String) { // should throw?
22 | let img = UIImage(contentsOfFile: filePath)
23 | self.init(image: img)
24 | }
25 | /**
26 | * Sets image async
27 | */
28 | internal func setImage(webPath: String, useBGThread: Bool = true, onError:@escaping OnError = defaultErrorHandler) {
29 | let thread: DispatchQueue = (useBGThread ? .global(qos: .background) : .main) // Create bg or main thread
30 | thread.async {
31 | UIImage.image(webPath: webPath) { (image: UIImage?, error: UIImage.IMGError?) in
32 | if let img = image {
33 | DispatchQueue.main.async { // UI stuff must happen on the main thread
34 | self.image = img // set the image
35 | }
36 | } else {
37 | onError(error) // call the error handler
38 | }
39 | }
40 | }
41 | }
42 | }
43 |
44 | #endif
45 |
--------------------------------------------------------------------------------
/Tests/FlowLayoutTests/FlowLayoutTests.swift:
--------------------------------------------------------------------------------
1 | import XCTest
2 | @testable import FlowLayout
3 |
4 | final class FlowLayoutTests: XCTestCase {
5 | func testExample() {
6 | _ = {}()
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/demo/CustomView.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | import QuartzCore
3 | /**
4 | * - Note: So this class represents the view you can replicate and add your own logic too
5 | */
6 | class CustomView: HorView {
7 | override init(frame: CGRect = .zero) {
8 | super.init(frame: frame)
9 | }
10 | }
11 |
--------------------------------------------------------------------------------