├── .gitignore
├── .swiftpm
└── xcode
│ └── package.xcworkspace
│ └── contents.xcworkspacedata
├── 368DED3D-4B49-4487-8E45-F2BB39D190B3.jpeg
├── 64DAA174-C657-4A95-B418-1DA7484EFE3F.jpeg
├── EasyListView.podspec
├── EasyListViewExample.xcodeproj
├── project.pbxproj
└── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ └── IDEWorkspaceChecks.plist
├── EasyListViewExample.xcworkspace
├── contents.xcworkspacedata
└── xcshareddata
│ └── IDEWorkspaceChecks.plist
├── EasyListViewExample
├── AppDelegate.swift
├── Assets.xcassets
│ ├── AppIcon.appiconset
│ │ └── Contents.json
│ ├── Contents.json
│ ├── avatar.imageset
│ │ ├── 054.00.png
│ │ └── Contents.json
│ └── indicator.imageset
│ │ ├── Contents.json
│ │ ├── indicator@2x.png
│ │ └── indicator@3x.png
├── Base.lproj
│ ├── LaunchScreen.storyboard
│ └── Main.storyboard
├── Demo1
│ └── FormListController.swift
├── Demo2
│ ├── UserCell.swift
│ └── UserListController.swift
├── EasyListViewExample-Bridging-Header.h
├── Info.plist
├── Main
│ ├── EasyListCell.swift
│ ├── MainViewController.swift
│ ├── ObjCTestController.h
│ └── ObjCTestController.m
└── Vendor
│ ├── ESPullToRefresh
│ ├── Animator
│ │ ├── Base.lproj
│ │ │ └── Localizable.strings
│ │ ├── ESRefreshFooterAnimator.swift
│ │ ├── ESRefreshHeaderAnimator.swift
│ │ ├── icon_pull_to_refresh_arrow@2x.png
│ │ ├── zh-Hans.lproj
│ │ │ └── Localizable.strings
│ │ └── zh-Hant.lproj
│ │ │ └── Localizable.strings
│ ├── ES.swift
│ ├── ESPullToRefresh+Manager.swift
│ ├── ESPullToRefresh.swift
│ ├── ESRefreshAnimator.swift
│ ├── ESRefreshComponent.swift
│ └── ESRefreshProtocol.swift
│ └── Stevia
│ ├── Stevia+Alignment.swift
│ ├── Stevia+Baselines.swift
│ ├── Stevia+Center.swift
│ ├── Stevia+Constraints.swift
│ ├── Stevia+Content.swift
│ ├── Stevia+DoubleDash.swift
│ ├── Stevia+Equation.swift
│ ├── Stevia+Fill.swift
│ ├── Stevia+FlexibleMargin.swift
│ ├── Stevia+GetConstraint.swift
│ ├── Stevia+Hierarchy.swift
│ ├── Stevia+LayoutAnchors.swift
│ ├── Stevia+Notifications.swift
│ ├── Stevia+Operators.swift
│ ├── Stevia+Percentage.swift
│ ├── Stevia+Position.swift
│ ├── Stevia+Size.swift
│ ├── Stevia+Stacks.swift
│ └── Stevia+Style.swift
├── Kapture1.gif
├── Kapture2.gif
├── LICENSE
├── Package.swift
├── Podfile
├── Podfile.lock
├── Pods
├── EasyCompatible
│ ├── LICENSE
│ ├── README.md
│ └── Sources
│ │ └── EasyCompatible.swift
├── Local Podspecs
│ └── EasyListView.podspec.json
├── Manifest.lock
├── Pods.xcodeproj
│ └── project.pbxproj
└── Target Support Files
│ ├── EasyCompatible
│ ├── EasyCompatible-Info.plist
│ ├── EasyCompatible-dummy.m
│ ├── EasyCompatible-prefix.pch
│ ├── EasyCompatible-umbrella.h
│ ├── EasyCompatible.debug.xcconfig
│ ├── EasyCompatible.modulemap
│ └── EasyCompatible.release.xcconfig
│ ├── EasyListView
│ ├── EasyListView-Info.plist
│ ├── EasyListView-dummy.m
│ ├── EasyListView-prefix.pch
│ ├── EasyListView-umbrella.h
│ ├── EasyListView.debug.xcconfig
│ ├── EasyListView.modulemap
│ └── EasyListView.release.xcconfig
│ └── Pods-EasyListViewExample
│ ├── Pods-EasyListViewExample-Info.plist
│ ├── Pods-EasyListViewExample-acknowledgements.markdown
│ ├── Pods-EasyListViewExample-acknowledgements.plist
│ ├── Pods-EasyListViewExample-dummy.m
│ ├── Pods-EasyListViewExample-frameworks-Debug-input-files.xcfilelist
│ ├── Pods-EasyListViewExample-frameworks-Debug-output-files.xcfilelist
│ ├── Pods-EasyListViewExample-frameworks-Release-input-files.xcfilelist
│ ├── Pods-EasyListViewExample-frameworks-Release-output-files.xcfilelist
│ ├── Pods-EasyListViewExample-frameworks.sh
│ ├── Pods-EasyListViewExample-umbrella.h
│ ├── Pods-EasyListViewExample.debug.xcconfig
│ ├── Pods-EasyListViewExample.modulemap
│ └── Pods-EasyListViewExample.release.xcconfig
├── README.md
└── Sources
├── ObjC
├── EasyListObjC.swift
└── EasyListObjCDeprecated.swift
└── Swift
├── EasyListAppend.swift
├── EasyListAppendDeprecated.swift
├── EasyListAttributes.swift
├── EasyListConstraint.swift
├── EasyListCoordinator.swift
├── EasyListDelete.swift
├── EasyListDeleteDeprecated.swift
├── EasyListDisposable.swift
├── EasyListExtension.swift
├── EasyListInsert.swift
├── EasyListInsertDeprecated.swift
└── EasyListView.swift
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | #
3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
4 |
5 | ## User settings
6 | xcuserdata/
7 |
8 | ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9)
9 | *.xcscmblueprint
10 | *.xccheckout
11 |
12 | ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4)
13 | build/
14 | DerivedData/
15 | *.moved-aside
16 | *.pbxuser
17 | !default.pbxuser
18 | *.mode1v3
19 | !default.mode1v3
20 | *.mode2v3
21 | !default.mode2v3
22 | *.perspectivev3
23 | !default.perspectivev3
24 |
25 | ## Obj-C/Swift specific
26 | *.hmap
27 |
28 | ## App packaging
29 | *.ipa
30 | *.dSYM.zip
31 | *.dSYM
32 |
33 | ## Playgrounds
34 | timeline.xctimeline
35 | playground.xcworkspace
36 |
37 | # Swift Package Manager
38 | #
39 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
40 | # Packages/
41 | # Package.pins
42 | # Package.resolved
43 | # *.xcodeproj
44 | #
45 | # Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata
46 | # hence it is not needed unless you have added a package configuration file to your project
47 | # .swiftpm
48 |
49 | .build/
50 |
51 | # CocoaPods
52 | #
53 | # We recommend against adding the Pods directory to your .gitignore. However
54 | # you should judge for yourself, the pros and cons are mentioned at:
55 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
56 | #
57 | # Pods/
58 | #
59 | # Add this line if you want to avoid checking in source code from the Xcode workspace
60 | # *.xcworkspace
61 |
62 | # Carthage
63 | #
64 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
65 | # Carthage/Checkouts
66 |
67 | Carthage/Build/
68 |
69 | # Accio dependency management
70 | Dependencies/
71 | .accio/
72 |
73 | # fastlane
74 | #
75 | # It is recommended to not store the screenshots in the git repo.
76 | # Instead, use fastlane to re-generate the screenshots whenever they are needed.
77 | # For more information about the recommended setup visit:
78 | # https://docs.fastlane.tools/best-practices/source-control/#source-control
79 |
80 | fastlane/report.xml
81 | fastlane/Preview.html
82 | fastlane/screenshots/**/*.png
83 | fastlane/test_output
84 |
85 | # Code Injection
86 | #
87 | # After new code Injection tools there's a generated folder /iOSInjectionProject
88 | # https://github.com/johnno1962/injectionforxcode
89 |
90 | iOSInjectionProject/
91 |
--------------------------------------------------------------------------------
/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/368DED3D-4B49-4487-8E45-F2BB39D190B3.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moliya/EasyListView/de589ce71c695759034df8f28d8b7b4168b4168e/368DED3D-4B49-4487-8E45-F2BB39D190B3.jpeg
--------------------------------------------------------------------------------
/64DAA174-C657-4A95-B418-1DA7484EFE3F.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moliya/EasyListView/de589ce71c695759034df8f28d8b7b4168b4168e/64DAA174-C657-4A95-B418-1DA7484EFE3F.jpeg
--------------------------------------------------------------------------------
/EasyListView.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |s|
2 |
3 | s.name = "EasyListView"
4 | s.version = "1.3.1"
5 | s.summary = "快速搭建静态及可重用列表"
6 | s.homepage = "https://github.com/moliya/EasyListView"
7 | s.license = "MIT"
8 | s.author = {'Carefree' => '946715806@qq.com'}
9 | s.source = { :git => "https://github.com/moliya/EasyListView.git", :tag => s.version}
10 | s.requires_arc = true
11 | s.platform = :ios, '9.0'
12 | s.swift_version = '5.0'
13 | s.dependency 'EasyCompatible'
14 | s.source_files = "Sources/**/*"
15 | end
16 |
--------------------------------------------------------------------------------
/EasyListViewExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/EasyListViewExample.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/EasyListViewExample.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/EasyListViewExample.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/EasyListViewExample/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // EasyListViewExample
4 | //
5 | // Created by carefree on 2020/6/11.
6 | // Copyright © 2020 carefree. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | @UIApplicationMain
12 | class AppDelegate: UIResponder, UIApplicationDelegate {
13 |
14 | var window: UIWindow?
15 |
16 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
17 | // Override point for customization after application launch.
18 | if #available(iOS 13.0, *) {
19 | let appearance = UINavigationBarAppearance()
20 | appearance.configureWithOpaqueBackground()
21 | UINavigationBar.appearance().scrollEdgeAppearance = appearance
22 | UINavigationBar.appearance().standardAppearance = appearance
23 | }
24 | return true
25 | }
26 |
27 |
28 | }
29 |
30 |
--------------------------------------------------------------------------------
/EasyListViewExample/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "scale" : "2x",
6 | "size" : "20x20"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "scale" : "3x",
11 | "size" : "20x20"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "scale" : "2x",
16 | "size" : "29x29"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "scale" : "3x",
21 | "size" : "29x29"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "scale" : "2x",
26 | "size" : "40x40"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "scale" : "3x",
31 | "size" : "40x40"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "scale" : "2x",
36 | "size" : "60x60"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "scale" : "3x",
41 | "size" : "60x60"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "scale" : "1x",
46 | "size" : "20x20"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "scale" : "2x",
51 | "size" : "20x20"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "scale" : "1x",
56 | "size" : "29x29"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "scale" : "2x",
61 | "size" : "29x29"
62 | },
63 | {
64 | "idiom" : "ipad",
65 | "scale" : "1x",
66 | "size" : "40x40"
67 | },
68 | {
69 | "idiom" : "ipad",
70 | "scale" : "2x",
71 | "size" : "40x40"
72 | },
73 | {
74 | "idiom" : "ipad",
75 | "scale" : "1x",
76 | "size" : "76x76"
77 | },
78 | {
79 | "idiom" : "ipad",
80 | "scale" : "2x",
81 | "size" : "76x76"
82 | },
83 | {
84 | "idiom" : "ipad",
85 | "scale" : "2x",
86 | "size" : "83.5x83.5"
87 | },
88 | {
89 | "idiom" : "ios-marketing",
90 | "scale" : "1x",
91 | "size" : "1024x1024"
92 | }
93 | ],
94 | "info" : {
95 | "author" : "xcode",
96 | "version" : 1
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/EasyListViewExample/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/EasyListViewExample/Assets.xcassets/avatar.imageset/054.00.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moliya/EasyListView/de589ce71c695759034df8f28d8b7b4168b4168e/EasyListViewExample/Assets.xcassets/avatar.imageset/054.00.png
--------------------------------------------------------------------------------
/EasyListViewExample/Assets.xcassets/avatar.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "filename" : "054.00.png",
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/EasyListViewExample/Assets.xcassets/indicator.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "filename" : "indicator@2x.png",
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "filename" : "indicator@3x.png",
14 | "idiom" : "universal",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "author" : "xcode",
20 | "version" : 1
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/EasyListViewExample/Assets.xcassets/indicator.imageset/indicator@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moliya/EasyListView/de589ce71c695759034df8f28d8b7b4168b4168e/EasyListViewExample/Assets.xcassets/indicator.imageset/indicator@2x.png
--------------------------------------------------------------------------------
/EasyListViewExample/Assets.xcassets/indicator.imageset/indicator@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moliya/EasyListView/de589ce71c695759034df8f28d8b7b4168b4168e/EasyListViewExample/Assets.xcassets/indicator.imageset/indicator@3x.png
--------------------------------------------------------------------------------
/EasyListViewExample/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 |
--------------------------------------------------------------------------------
/EasyListViewExample/Base.lproj/Main.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 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/EasyListViewExample/Demo1/FormListController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FormListController.swift
3 | // EasyListViewExample
4 | //
5 | // Created by carefree on 2020/8/17.
6 | // Copyright © 2020 carefree. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import EasyListView
11 |
12 | class FormListController: UIViewController {
13 |
14 | lazy var scrollView: UIScrollView = {
15 | let scrollView = UIScrollView()
16 | scrollView.alwaysBounceVertical = true
17 | scrollView.backgroundColor = #colorLiteral(red: 0.9607843137, green: 0.9607843137, blue: 0.9607843137, alpha: 1)
18 |
19 | return scrollView
20 | }()
21 |
22 | var nameTextField: UITextField!
23 | var birthdayLabel: UILabel!
24 |
25 | override func viewDidLoad() {
26 | super.viewDidLoad()
27 |
28 | scrollView.delegate = self
29 | view.subviews(scrollView)
30 | view.layout(
31 | 0,
32 | |scrollView|,
33 | 0
34 | )
35 |
36 | let addSeparator: () -> Void = {
37 | let view = UIView()
38 | view.backgroundColor = #colorLiteral(red: 0.8979505897, green: 0.8981012702, blue: 0.8979307413, alpha: 1)
39 | view.height(0.5)
40 |
41 | self.scrollView.easy.appendView(view)
42 | }
43 |
44 | scrollView.easy.appendView {
45 | let label = UILabel()
46 | label.numberOfLines = 0
47 | label.font = .systemFont(ofSize: 14)
48 | label.textColor = .lightGray
49 | label.text = "Section Title"
50 | return label
51 | }.insets(UIEdgeInsets(top: 10, left: 16, bottom: 0, right: 16))
52 |
53 | let nameCell: UIView = {
54 | let cell = EasyListCell()
55 | cell.textLabel.text = "Name"
56 | cell.showIndicator = false
57 |
58 | let textField = UITextField()
59 | textField.textAlignment = .right
60 | textField.placeholder = "type your name"
61 | self.nameTextField = textField
62 |
63 | cell.subviews(textField)
64 | cell.height(48)
65 | cell.layout(
66 | 0,
67 | textField-16-|,
68 | 0
69 | )
70 | textField.Leading == cell.textLabel.Trailing + 10
71 |
72 | return cell
73 | }()
74 | scrollView.easy.appendView(nameCell).spacing(10)
75 |
76 | addSeparator()
77 |
78 | let emailCell: UIView = {
79 | let cell = EasyListCell()
80 | cell.textLabel.text = "Email"
81 | cell.showIndicator = false
82 |
83 | let textField = UITextField()
84 | textField.textAlignment = .right
85 | textField.placeholder = "email@domain.com"
86 |
87 | cell.subviews(textField)
88 | cell.height(48)
89 | cell.layout(
90 | 0,
91 | textField-16-|,
92 | 0
93 | )
94 | textField.Leading == cell.textLabel.Trailing + 10
95 |
96 | return cell
97 | }()
98 | scrollView.easy.appendView(emailCell)
99 |
100 | addSeparator()
101 |
102 | let birthdayCell: UIView = {
103 | let cell = EasyListCell()
104 | cell.textLabel.text = "Birthday"
105 | cell.onTap = {[unowned self] in
106 | self.tapBirthdayCell()
107 | }
108 |
109 | let label = UILabel()
110 | label.textAlignment = .right
111 | label.textColor = #colorLiteral(red: 0.4, green: 0.4, blue: 0.4, alpha: 1)
112 | let formatter = DateFormatter()
113 | formatter.dateFormat = "yyyy-MM-dd"
114 | label.text = formatter.string(from: Date())
115 | self.birthdayLabel = label
116 |
117 | cell.subviews(label)
118 | cell.height(48)
119 | cell.layout(
120 | label.centerVertically()-30-|
121 | )
122 | label.Leading == cell.textLabel.Trailing + 10
123 |
124 | return cell
125 | }()
126 | scrollView.easy.appendView(birthdayCell).identifier("birthday")
127 |
128 | addSeparator()
129 |
130 | let switchCell: UIView = {
131 | let cell = EasyListCell()
132 | cell.textLabel.text = "Other"
133 | cell.showIndicator = false
134 |
135 | let swt = UISwitch()
136 |
137 | cell.subviews(swt)
138 | cell.height(48)
139 | cell.layout(
140 | swt.centerVertically()-16-|
141 | )
142 |
143 | return cell
144 | }()
145 | scrollView.easy.appendView(switchCell)
146 |
147 | scrollView.easy.appendView {
148 | let label = UILabel()
149 | label.numberOfLines = 0
150 | label.font = .systemFont(ofSize: 16)
151 | label.textColor = .lightGray
152 | label.text = "1.AAA\n2.BBB\n3.CCC"
153 | return label
154 | }.insets(UIEdgeInsets(top: 10, left: 16, bottom: 0, right: 16))
155 |
156 | let inputView: UIView = {
157 | let view = UIView()
158 | view.backgroundColor = .white
159 |
160 | let label = UILabel()
161 | label.font = .systemFont(ofSize: 12)
162 | label.textColor = #colorLiteral(red: 0.2, green: 0.2, blue: 0.2, alpha: 1)
163 | label.text = "Description"
164 |
165 | let textView = UITextView()
166 | textView.layer.cornerRadius = 6
167 | textView.layer.borderColor = #colorLiteral(red: 0.8039215803, green: 0.8039215803, blue: 0.8039215803, alpha: 1)
168 | textView.layer.borderWidth = 1 / UIScreen.main.scale
169 |
170 | view.subviews(label, textView)
171 | view.height(150)
172 | view.layout(
173 | 15,
174 | |-16-label,
175 | 10,
176 | |-15-textView-15-|,
177 | 10
178 | )
179 |
180 | return view
181 | }()
182 | scrollView.easy.appendView(inputView).spacing(10)
183 |
184 | let button = UIButton(type: .custom)
185 | button.setTitle("Done", for: .normal)
186 | button.backgroundColor = #colorLiteral(red: 0.1764705926, green: 0.4980392158, blue: 0.7568627596, alpha: 1)
187 | button.layer.cornerRadius = 6
188 | button.clipsToBounds = true
189 | button.height(45)
190 | button.addTarget(self, action: #selector(tapDone), for: .touchUpInside)
191 | scrollView.easy.appendView(button).insets(UIEdgeInsets(top: 40, left: 16, bottom: 20, right: 16))
192 | }
193 |
194 | deinit {
195 | print("deinit")
196 | }
197 |
198 | func tapBirthdayCell() {
199 | if let _ = scrollView.easy.getElement(identifier: "birthdaySelector") as? UIDatePicker {
200 | scrollView.easy.deleteView("birthdaySelector")
201 | birthdayLabel.textColor = #colorLiteral(red: 0.4, green: 0.4, blue: 0.4, alpha: 1)
202 | return
203 | }
204 |
205 | let picker = UIDatePicker()
206 | picker.backgroundColor = .white
207 | picker.datePickerMode = .date
208 | if #available(iOS 13.4, *) {
209 | picker.preferredDatePickerStyle = .wheels
210 | }
211 | picker.addTarget(self, action: #selector(datePickerChanged(_:)), for: .valueChanged)
212 |
213 | if let text = birthdayLabel.text, !text.isEmpty {
214 | let formatter = DateFormatter()
215 | formatter.dateFormat = "yyyy-MM-dd"
216 | picker.date = formatter.date(from: text) ?? Date()
217 | }
218 | birthdayLabel.textColor = #colorLiteral(red: 0.9098039269, green: 0.4784313738, blue: 0.6431372762, alpha: 1)
219 |
220 | scrollView.easy.beginUpdates()
221 | scrollView.easy.insertView(picker, after: "birthday").identifier("birthdaySelector")
222 | scrollView.easy.endUpdates()
223 | }
224 |
225 | @objc func datePickerChanged(_ sender: UIDatePicker) {
226 | let formatter = DateFormatter()
227 | formatter.dateFormat = "yyyy-MM-dd"
228 | birthdayLabel.text = formatter.string(from: sender.date)
229 | }
230 |
231 | @objc func tapDone() {
232 | view.endEditing(true)
233 | let alert = UIAlertController(title: "Hello!\(nameTextField.text ?? "")", message: nil, preferredStyle: .alert)
234 | alert.addAction(UIAlertAction(title: "OK", style: .cancel, handler: nil))
235 | self.present(alert, animated: true, completion: nil)
236 | }
237 | }
238 |
239 | extension FormListController: UIScrollViewDelegate {
240 | func scrollViewDidScroll(_ scrollView: UIScrollView) {
241 | view.endEditing(true)
242 | }
243 | }
244 |
--------------------------------------------------------------------------------
/EasyListViewExample/Demo2/UserCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UserCell.swift
3 | // EasyListViewExample
4 | //
5 | // Created by carefree on 2020/8/17.
6 | // Copyright © 2020 carefree. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class UserCell: UIView {
12 |
13 | var avatarView: UIImageView!
14 | var nameLabel: UILabel!
15 |
16 | required init?(coder: NSCoder) {
17 | super.init(coder: coder)
18 | commonInit()
19 | }
20 |
21 | override init(frame: CGRect) {
22 | super.init(frame: frame)
23 | commonInit()
24 | }
25 |
26 | func commonInit() {
27 | backgroundColor = .white
28 |
29 | avatarView = UIImageView()
30 | avatarView.contentMode = .scaleAspectFit
31 | nameLabel = UILabel()
32 |
33 | let separator = UIView()
34 | separator.backgroundColor = #colorLiteral(red: 0.8979505897, green: 0.8981012702, blue: 0.8979307413, alpha: 1)
35 |
36 | subviews(avatarView, nameLabel, separator)
37 | height(66)
38 | layout(
39 | 5,
40 | |-16-avatarView,
41 | 5,
42 | |-16-separator.height(1 / UIScreen.main.scale)|,
43 | 0
44 | )
45 | avatarView.Width == avatarView.Height
46 | nameLabel.Top == avatarView.Top
47 | nameLabel.Leading == avatarView.Trailing + 5
48 | }
49 |
50 | func set(name: String?, avatar: UIImage?) {
51 | avatarView.image = avatar
52 | nameLabel.text = name
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/EasyListViewExample/Demo2/UserListController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UserListController.swift
3 | // EasyListViewExample
4 | //
5 | // Created by carefree on 2020/8/17.
6 | // Copyright © 2020 carefree. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import EasyListView
11 |
12 | class UserListController: UIViewController {
13 |
14 | lazy var scrollView: UIScrollView = {
15 | let scrollView = UIScrollView()
16 | scrollView.alwaysBounceVertical = true
17 | scrollView.backgroundColor = #colorLiteral(red: 0.9607843137, green: 0.9607843137, blue: 0.9607843137, alpha: 1)
18 |
19 | return scrollView
20 | }()
21 |
22 | var users = [String]()
23 |
24 | override func viewDidLoad() {
25 | super.viewDidLoad()
26 |
27 | scrollView.delegate = self
28 | view.subviews(scrollView)
29 | view.layout(
30 | 0,
31 | |scrollView|,
32 | 0
33 | )
34 |
35 | scrollView.es.addPullToRefresh {[unowned self] in
36 | self.loadData()
37 | }
38 | scrollView.es.addInfiniteScrolling {[unowned self] in
39 | self.loadMoreData()
40 | }
41 |
42 | let activity = UIActivityIndicatorView(style: .gray)
43 | scrollView.subviews(activity)
44 | activity.centerInContainer()
45 | activity.startAnimating()
46 | loadData()
47 | }
48 |
49 | deinit {
50 | print("deinit")
51 | }
52 |
53 | @objc func loadData() {
54 | DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
55 | if self.users.count == 0 {
56 | self.scrollView.subviews.first { $0 is UIActivityIndicatorView }?.removeFromSuperview()
57 | }
58 |
59 | //删除旧的数据
60 | self.users.removeAll()
61 | self.scrollView.easy.deleteAll()
62 |
63 | //添加新数据
64 | for i in 1 ..< 11 {
65 | self.users.append("User No.\(i)")
66 | }
67 |
68 | for index in 0 ..< self.users.count {
69 | let view = self.scrollView.easy.disposableView {[unowned self] in
70 | let cell = UserCell()
71 | cell.set(name: self.users[index], avatar: UIImage(named: "avatar"))
72 |
73 | return cell
74 | }
75 | self.scrollView.easy.appendView(view)
76 | }
77 |
78 | if self.scrollView.header?.isRefreshing == true {
79 | self.scrollView.header?.stopRefreshing()
80 | }
81 | }
82 | }
83 |
84 | @objc func loadMoreData() {
85 | DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
86 | let offset = self.users.count
87 | //添加新数据
88 | for i in 1 ..< 11 {
89 | self.users.append("User No.\(i + offset)")
90 | }
91 |
92 | self.scrollView.easy.beginUpdates()
93 |
94 | for index in offset ..< self.users.count {
95 | let view = self.scrollView.easy.disposableView {[unowned self] in
96 | let cell = UserCell()
97 | cell.set(name: self.users[index], avatar: UIImage(named: "avatar"))
98 | return cell
99 | }
100 | self.scrollView.easy.appendView(view)
101 | }
102 |
103 | self.scrollView.easy.endUpdates()
104 |
105 | self.scrollView.footer?.stopRefreshing()
106 | }
107 | }
108 |
109 |
110 | }
111 |
112 | extension UserListController: UIScrollViewDelegate {
113 | func scrollViewDidScroll(_ scrollView: UIScrollView) {
114 | scrollView.easy.triggerDisposable()
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/EasyListViewExample/EasyListViewExample-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | //
2 | // Use this file to import your target's public headers that you would like to expose to Swift.
3 | //
4 |
5 | #import "ObjCTestController.h"
6 |
--------------------------------------------------------------------------------
/EasyListViewExample/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE)
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 | LSRequiresIPhoneOS
22 |
23 | UILaunchStoryboardName
24 | LaunchScreen
25 | UIMainStoryboardFile
26 | Main
27 | UIRequiredDeviceCapabilities
28 |
29 | armv7
30 |
31 | UISupportedInterfaceOrientations
32 |
33 | UIInterfaceOrientationPortrait
34 | UIInterfaceOrientationLandscapeLeft
35 | UIInterfaceOrientationLandscapeRight
36 |
37 | UISupportedInterfaceOrientations~ipad
38 |
39 | UIInterfaceOrientationPortrait
40 | UIInterfaceOrientationPortraitUpsideDown
41 | UIInterfaceOrientationLandscapeLeft
42 | UIInterfaceOrientationLandscapeRight
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/EasyListViewExample/Main/EasyListCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // EasyListCell.swift
3 | // EasyListViewExample
4 | //
5 | // Created by carefree on 2020/8/17.
6 | // Copyright © 2020 carefree. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class EasyListCell: UIView {
12 |
13 | public var onTap: (() -> Void)?
14 | public let textLabel = UILabel()
15 | public var showIndicator = true {
16 | didSet {
17 | indicatorView.isHidden = !showIndicator
18 | }
19 | }
20 |
21 | private var normalColor: UIColor?
22 | private var highlightedColor = UIColor(red: 218/255.0, green: 218/255.0, blue: 218/255.0, alpha: 1)
23 | private var indicatorView = UIImageView()
24 |
25 | override init(frame: CGRect) {
26 | super.init(frame: frame)
27 | commonInit()
28 | }
29 |
30 | required init?(coder: NSCoder) {
31 | super.init(coder: coder)
32 | commonInit()
33 | }
34 |
35 | func commonInit() {
36 | backgroundColor = .white
37 | addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(tapAction)))
38 |
39 | textLabel.setContentHuggingPriority(.required, for: .horizontal)
40 | textLabel.font = .systemFont(ofSize: 16)
41 | textLabel.textColor = .darkText
42 |
43 | indicatorView.image = UIImage(named: "indicator")
44 |
45 | subviews(textLabel, indicatorView)
46 | layout(
47 | |-16-textLabel-""-indicatorView-16-|
48 | )
49 | textLabel.centerVertically()
50 | }
51 |
52 | @objc func tapAction() {
53 | onTap?()
54 | }
55 |
56 | override func touchesBegan(_ touches: Set, with event: UIEvent?) {
57 | super.touchesBegan(touches, with: event)
58 |
59 | normalColor = backgroundColor
60 | backgroundColor = highlightedColor
61 | }
62 |
63 | override func touchesEnded(_ touches: Set, with event: UIEvent?) {
64 | super.touchesEnded(touches, with: event)
65 |
66 | backgroundColor = normalColor
67 | }
68 |
69 | override func touchesCancelled(_ touches: Set, with event: UIEvent?) {
70 | super.touchesCancelled(touches, with: event)
71 |
72 | backgroundColor = normalColor
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/EasyListViewExample/Main/MainViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MainViewController.swift
3 | // EasyListViewExample
4 | //
5 | // Created by carefree on 2020/8/17.
6 | // Copyright © 2020 carefree. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import EasyListView
11 |
12 | class MainViewController: UIViewController {
13 |
14 | @IBOutlet weak var scrollView: UIScrollView!
15 |
16 | override func viewDidLoad() {
17 | super.viewDidLoad()
18 |
19 | let cell1: UIView = {
20 | let cell = EasyListCell()
21 | cell.textLabel.text = "示例-表单"
22 | cell.height(44)
23 | cell.onTap = { [unowned self] in
24 | self.navigationController?.pushViewController(FormListController(), animated: true)
25 | }
26 |
27 | return cell
28 | }()
29 | scrollView.easy.appendView(cell1).spacing(10)
30 |
31 | let cell2: UIView = {
32 | let cell = EasyListCell()
33 | cell.textLabel.text = "示例-列表"
34 | cell.height(44)
35 | cell.onTap = { [unowned self] in
36 | self.navigationController?.pushViewController(UserListController(), animated: true)
37 | }
38 |
39 | return cell
40 | }()
41 | scrollView.easy.appendView(cell2).spacing(0.5)
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/EasyListViewExample/Main/ObjCTestController.h:
--------------------------------------------------------------------------------
1 | //
2 | // ObjCTestController.h
3 | // EasyListViewExample
4 | //
5 | // Created by carefree on 2020/8/18.
6 | // Copyright © 2020 carefree. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | NS_ASSUME_NONNULL_BEGIN
12 |
13 | @interface ObjCTestController : UIViewController
14 |
15 | @end
16 |
17 | NS_ASSUME_NONNULL_END
18 |
--------------------------------------------------------------------------------
/EasyListViewExample/Main/ObjCTestController.m:
--------------------------------------------------------------------------------
1 | //
2 | // ObjCTestController.m
3 | // EasyListViewExample
4 | //
5 | // Created by carefree on 2020/8/18.
6 | // Copyright © 2020 carefree. All rights reserved.
7 | //
8 |
9 | #import "ObjCTestController.h"
10 | #import
11 |
12 | @interface ObjCTestController ()
13 |
14 | @end
15 |
16 | @implementation ObjCTestController
17 |
18 | - (void)viewDidLoad {
19 | [super viewDidLoad];
20 |
21 | EasyListView *listView = [[EasyListView alloc] init];
22 | [listView easy_beginUpdates];
23 | [listView easy_endUpdatesWithCompletion:nil];
24 |
25 | }
26 |
27 | @end
28 |
--------------------------------------------------------------------------------
/EasyListViewExample/Vendor/ESPullToRefresh/Animator/Base.lproj/Localizable.strings:
--------------------------------------------------------------------------------
1 | "Pull to refresh" = "Pull to refresh";
2 | "Release to refresh" = "Release to refresh";
3 | "Loading..." = "Loading...";
4 | "Loading more" = "Loading more";
5 | "No more data" = "No more data";
6 |
--------------------------------------------------------------------------------
/EasyListViewExample/Vendor/ESPullToRefresh/Animator/ESRefreshFooterAnimator.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ESRefreshFooterAnimator.swift
3 | //
4 | // Created by egg swift on 16/4/7.
5 | // Copyright (c) 2013-2016 ESPullToRefresh (https://github.com/eggswift/pull-to-refresh)
6 | //
7 | // Permission is hereby granted, free of charge, to any person obtaining a copy
8 | // of this software and associated documentation files (the "Software"), to deal
9 | // in the Software without restriction, including without limitation the rights
10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | // copies of the Software, and to permit persons to whom the Software is
12 | // furnished to do so, subject to the following conditions:
13 | //
14 | // The above copyright notice and this permission notice shall be included in
15 | // all copies or substantial portions of the Software.
16 | //
17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | // THE SOFTWARE.
24 | //
25 |
26 | import UIKit
27 |
28 | open class ESRefreshFooterAnimator: UIView, ESRefreshProtocol, ESRefreshAnimatorProtocol {
29 |
30 | open var loadingMoreDescription: String = NSLocalizedString("Loading more", comment: "")
31 | open var noMoreDataDescription: String = NSLocalizedString("No more data", comment: "")
32 | open var loadingDescription: String = NSLocalizedString("Loading...", comment: "")
33 |
34 | open var view: UIView { return self }
35 | open var duration: TimeInterval = 0.3
36 | open var insets: UIEdgeInsets = UIEdgeInsets.zero
37 | open var trigger: CGFloat = 42.0
38 | open var executeIncremental: CGFloat = 42.0
39 | open var state: ESRefreshViewState = .pullToRefresh
40 |
41 | fileprivate let titleLabel: UILabel = {
42 | let label = UILabel.init(frame: CGRect.zero)
43 | label.font = UIFont.systemFont(ofSize: 14.0)
44 | label.textColor = UIColor.init(white: 160.0 / 255.0, alpha: 1.0)
45 | label.textAlignment = .center
46 | return label
47 | }()
48 |
49 | fileprivate let indicatorView: UIActivityIndicatorView = {
50 | let indicatorView = UIActivityIndicatorView.init(style: .gray)
51 | indicatorView.isHidden = true
52 | return indicatorView
53 | }()
54 |
55 | public override init(frame: CGRect) {
56 | super.init(frame: frame)
57 | titleLabel.text = loadingMoreDescription
58 | addSubview(titleLabel)
59 | addSubview(indicatorView)
60 | }
61 |
62 | public required init(coder aDecoder: NSCoder) {
63 | fatalError("init(coder:) has not been implemented")
64 | }
65 |
66 | open func refreshAnimationBegin(view: ESRefreshComponent) {
67 | indicatorView.startAnimating()
68 | titleLabel.text = loadingDescription
69 | indicatorView.isHidden = false
70 | }
71 |
72 | open func refreshAnimationEnd(view: ESRefreshComponent) {
73 | indicatorView.stopAnimating()
74 | titleLabel.text = loadingMoreDescription
75 | indicatorView.isHidden = true
76 | }
77 |
78 | open func refresh(view: ESRefreshComponent, progressDidChange progress: CGFloat) {
79 | // do nothing
80 | }
81 |
82 | open func refresh(view: ESRefreshComponent, stateDidChange state: ESRefreshViewState) {
83 | guard self.state != state else {
84 | return
85 | }
86 | self.state = state
87 |
88 | switch state {
89 | case .refreshing, .autoRefreshing :
90 | titleLabel.text = loadingDescription
91 | break
92 | case .noMoreData:
93 | titleLabel.text = noMoreDataDescription
94 | break
95 | case .pullToRefresh:
96 | titleLabel.text = loadingMoreDescription
97 | break
98 | default:
99 | break
100 | }
101 | self.setNeedsLayout()
102 | }
103 |
104 | open override func layoutSubviews() {
105 | super.layoutSubviews()
106 | let s = self.bounds.size
107 | let w = s.width
108 | let h = s.height
109 |
110 | titleLabel.sizeToFit()
111 | titleLabel.center = CGPoint.init(x: w / 2.0, y: h / 2.0 - 5.0)
112 | indicatorView.center = CGPoint.init(x: titleLabel.frame.origin.x - 18.0, y: titleLabel.center.y)
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/EasyListViewExample/Vendor/ESPullToRefresh/Animator/ESRefreshHeaderAnimator.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ESRefreshHeaderView.swift
3 | //
4 | // Created by egg swift on 16/4/7.
5 | // Copyright (c) 2013-2016 ESPullToRefresh (https://github.com/eggswift/pull-to-refresh)
6 | // Icon from http://www.iconfont.cn
7 | //
8 | // Permission is hereby granted, free of charge, to any person obtaining a copy
9 | // of this software and associated documentation files (the "Software"), to deal
10 | // in the Software without restriction, including without limitation the rights
11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 | // copies of the Software, and to permit persons to whom the Software is
13 | // furnished to do so, subject to the following conditions:
14 | //
15 | // The above copyright notice and this permission notice shall be included in
16 | // all copies or substantial portions of the Software.
17 | //
18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 | // THE SOFTWARE.
25 | //
26 |
27 | import Foundation
28 | import QuartzCore
29 | import UIKit
30 |
31 | open class ESRefreshHeaderAnimator: UIView, ESRefreshProtocol, ESRefreshAnimatorProtocol, ESRefreshImpactProtocol {
32 | open var pullToRefreshDescription = NSLocalizedString("Pull to refresh", comment: "") {
33 | didSet {
34 | if pullToRefreshDescription != oldValue {
35 | titleLabel.text = pullToRefreshDescription
36 | }
37 | }
38 | }
39 | open var releaseToRefreshDescription = NSLocalizedString("Release to refresh", comment: "")
40 | open var loadingDescription = NSLocalizedString("Loading...", comment: "")
41 |
42 | open var view: UIView { return self }
43 | open var insets: UIEdgeInsets = UIEdgeInsets.zero
44 | open var trigger: CGFloat = 60.0
45 | open var executeIncremental: CGFloat = 60.0
46 | open var state: ESRefreshViewState = .pullToRefresh
47 |
48 | fileprivate let imageView: UIImageView = {
49 | let imageView = UIImageView.init()
50 | let frameworkBundle = Bundle(for: ESRefreshAnimator.self)
51 | if /* CocoaPods static */ let path = frameworkBundle.path(forResource: "ESPullToRefresh", ofType: "bundle"),let bundle = Bundle(path: path) {
52 | imageView.image = UIImage(named: "icon_pull_to_refresh_arrow", in: bundle, compatibleWith: nil)
53 | }else if /* Carthage */ let bundle = Bundle.init(identifier: "com.eggswift.ESPullToRefresh") {
54 | imageView.image = UIImage(named: "icon_pull_to_refresh_arrow", in: bundle, compatibleWith: nil)
55 | } else if /* CocoaPods */ let bundle = Bundle.init(identifier: "org.cocoapods.ESPullToRefresh") {
56 | imageView.image = UIImage(named: "ESPullToRefresh.bundle/icon_pull_to_refresh_arrow", in: bundle, compatibleWith: nil)
57 | } else /* Manual */ {
58 | imageView.image = UIImage(named: "icon_pull_to_refresh_arrow")
59 | }
60 | return imageView
61 | }()
62 |
63 | fileprivate let titleLabel: UILabel = {
64 | let label = UILabel.init(frame: CGRect.zero)
65 | label.font = UIFont.systemFont(ofSize: 14.0)
66 | label.textColor = UIColor.init(white: 0.625, alpha: 1.0)
67 | label.textAlignment = .left
68 | return label
69 | }()
70 |
71 | fileprivate let indicatorView: UIActivityIndicatorView = {
72 | let indicatorView = UIActivityIndicatorView.init(style: .gray)
73 | indicatorView.isHidden = true
74 | return indicatorView
75 | }()
76 |
77 | public override init(frame: CGRect) {
78 | super.init(frame: frame)
79 | titleLabel.text = pullToRefreshDescription
80 | self.addSubview(imageView)
81 | self.addSubview(titleLabel)
82 | self.addSubview(indicatorView)
83 | }
84 |
85 | public required init(coder aDecoder: NSCoder) {
86 | fatalError("init(coder:) has not been implemented")
87 | }
88 |
89 | open func refreshAnimationBegin(view: ESRefreshComponent) {
90 | indicatorView.startAnimating()
91 | indicatorView.isHidden = false
92 | imageView.isHidden = true
93 | titleLabel.text = loadingDescription
94 | imageView.transform = CGAffineTransform(rotationAngle: 0.000001 - CGFloat.pi)
95 | }
96 |
97 | open func refreshAnimationEnd(view: ESRefreshComponent) {
98 | indicatorView.stopAnimating()
99 | indicatorView.isHidden = true
100 | imageView.isHidden = false
101 | titleLabel.text = pullToRefreshDescription
102 | imageView.transform = CGAffineTransform.identity
103 | }
104 |
105 | open func refresh(view: ESRefreshComponent, progressDidChange progress: CGFloat) {
106 | // Do nothing
107 |
108 | }
109 |
110 | open func refresh(view: ESRefreshComponent, stateDidChange state: ESRefreshViewState) {
111 | guard self.state != state else {
112 | return
113 | }
114 | self.state = state
115 |
116 | switch state {
117 | case .refreshing, .autoRefreshing:
118 | titleLabel.text = loadingDescription
119 | self.setNeedsLayout()
120 | break
121 | case .releaseToRefresh:
122 | titleLabel.text = releaseToRefreshDescription
123 | self.setNeedsLayout()
124 | self.impact()
125 | UIView.animate(withDuration: 0.2, delay: 0.0, options: UIView.AnimationOptions(), animations: {
126 | [weak self] in
127 | self?.imageView.transform = CGAffineTransform(rotationAngle: 0.000001 - CGFloat.pi)
128 | }) { (animated) in }
129 | break
130 | case .pullToRefresh:
131 | titleLabel.text = pullToRefreshDescription
132 | self.setNeedsLayout()
133 | UIView.animate(withDuration: 0.2, delay: 0.0, options: UIView.AnimationOptions(), animations: {
134 | [weak self] in
135 | self?.imageView.transform = CGAffineTransform.identity
136 | }) { (animated) in }
137 | break
138 | default:
139 | break
140 | }
141 | }
142 |
143 | open override func layoutSubviews() {
144 | super.layoutSubviews()
145 | let s = self.bounds.size
146 | let w = s.width
147 | let h = s.height
148 |
149 | UIView.performWithoutAnimation {
150 | titleLabel.sizeToFit()
151 | titleLabel.center = CGPoint.init(x: w / 2.0, y: h / 2.0)
152 | indicatorView.center = CGPoint.init(x: titleLabel.frame.origin.x - 16.0, y: h / 2.0)
153 | imageView.frame = CGRect.init(x: titleLabel.frame.origin.x - 28.0, y: (h - 18.0) / 2.0, width: 18.0, height: 18.0)
154 | }
155 | }
156 |
157 | }
158 |
--------------------------------------------------------------------------------
/EasyListViewExample/Vendor/ESPullToRefresh/Animator/icon_pull_to_refresh_arrow@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moliya/EasyListView/de589ce71c695759034df8f28d8b7b4168b4168e/EasyListViewExample/Vendor/ESPullToRefresh/Animator/icon_pull_to_refresh_arrow@2x.png
--------------------------------------------------------------------------------
/EasyListViewExample/Vendor/ESPullToRefresh/Animator/zh-Hans.lproj/Localizable.strings:
--------------------------------------------------------------------------------
1 | "Pull to refresh" = "下拉刷新";
2 | "Release to refresh" = "松开刷新";
3 | "Loading..." = "加载中";
4 | "Loading more" = "正在加载更多";
5 | "No more data" = "没有更多的了";
6 |
--------------------------------------------------------------------------------
/EasyListViewExample/Vendor/ESPullToRefresh/Animator/zh-Hant.lproj/Localizable.strings:
--------------------------------------------------------------------------------
1 | "Pull to refresh" = "下拉刷新";
2 | "Release to refresh" = "松開刷新";
3 | "Loading..." = "載入中";
4 | "Loading more" = "正在載入更多";
5 | "No more data" = "沒有更多的了";
6 |
--------------------------------------------------------------------------------
/EasyListViewExample/Vendor/ESPullToRefresh/ES.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ES.swift
3 | // ESPullToRefreshExample
4 | //
5 | // Created by 木瓜 on 2017/4/25.
6 | // Copyright © 2017年 egg swift. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 |
12 | public protocol ESExtensionsProvider: class {
13 | associatedtype CompatibleType
14 | var es: CompatibleType { get }
15 | }
16 |
17 | extension ESExtensionsProvider {
18 | /// A proxy which hosts reactive extensions for `self`.
19 | public var es: ES {
20 | return ES(self)
21 | }
22 |
23 | }
24 |
25 | public struct ES {
26 | public let base: Base
27 |
28 | // Construct a proxy.
29 | //
30 | // - parameters:
31 | // - base: The object to be proxied.
32 | fileprivate init(_ base: Base) {
33 | self.base = base
34 | }
35 | }
36 |
37 | //
38 | extension UIScrollView: ESExtensionsProvider {}
39 |
40 |
41 |
--------------------------------------------------------------------------------
/EasyListViewExample/Vendor/ESPullToRefresh/ESPullToRefresh+Manager.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ESPullToRefresh+Manager.swift
3 | //
4 | // Created by egg swift on 16/4/7.
5 | // Copyright (c) 2013-2016 ESPullToRefresh (https://github.com/eggswift/pull-to-refresh)
6 | //
7 | // Permission is hereby granted, free of charge, to any person obtaining a copy
8 | // of this software and associated documentation files (the "Software"), to deal
9 | // in the Software without restriction, including without limitation the rights
10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | // copies of the Software, and to permit persons to whom the Software is
12 | // furnished to do so, subject to the following conditions:
13 | //
14 | // The above copyright notice and this permission notice shall be included in
15 | // all copies or substantial portions of the Software.
16 | //
17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | // THE SOFTWARE.
24 | //
25 |
26 | import Foundation
27 |
28 | open class ESRefreshDataManager {
29 |
30 | static let sharedManager = ESRefreshDataManager.init()
31 |
32 | static let lastRefreshKey: String = "com.espulltorefresh.lastRefreshKey"
33 | static let expiredTimeIntervalKey: String = "com.espulltorefresh.expiredTimeIntervalKey"
34 | open var lastRefreshInfo = [String: Date]()
35 | open var expiredTimeIntervalInfo = [String: TimeInterval]()
36 |
37 | public required init() {
38 | if let lastRefreshInfo = UserDefaults.standard.dictionary(forKey: ESRefreshDataManager.lastRefreshKey) as? [String: Date] {
39 | self.lastRefreshInfo = lastRefreshInfo
40 | }
41 | if let expiredTimeIntervalInfo = UserDefaults.standard.dictionary(forKey: ESRefreshDataManager.expiredTimeIntervalKey) as? [String: TimeInterval] {
42 | self.expiredTimeIntervalInfo = expiredTimeIntervalInfo
43 | }
44 | }
45 |
46 | open func date(forKey key: String) -> Date? {
47 | let date = lastRefreshInfo[key]
48 | return date
49 | }
50 |
51 | open func setDate(_ date: Date?, forKey key: String) {
52 | lastRefreshInfo[key] = date
53 | UserDefaults.standard.set(lastRefreshInfo, forKey: ESRefreshDataManager.lastRefreshKey)
54 | UserDefaults.standard.synchronize()
55 | }
56 |
57 | open func expiredTimeInterval(forKey key: String) -> TimeInterval? {
58 | let interval = expiredTimeIntervalInfo[key]
59 | return interval
60 | }
61 |
62 | open func setExpiredTimeInterval(_ interval: TimeInterval?, forKey key: String) {
63 | expiredTimeIntervalInfo[key] = interval
64 | UserDefaults.standard.set(expiredTimeIntervalInfo, forKey: ESRefreshDataManager.expiredTimeIntervalKey)
65 | UserDefaults.standard.synchronize()
66 | }
67 |
68 | open func isExpired(forKey key: String) -> Bool {
69 | guard let date = date(forKey: key) else {
70 | return true
71 | }
72 | guard let interval = expiredTimeInterval(forKey: key) else {
73 | return false
74 | }
75 | if date.timeIntervalSinceNow < -interval {
76 | return true // Expired
77 | }
78 | return false
79 | }
80 |
81 | open func isExpired(forKey key: String, block: ((Bool) -> ())?) {
82 | DispatchQueue.global().async {
83 | [weak self] in
84 | let result = self?.isExpired(forKey: key) ?? true
85 | DispatchQueue.main.async(execute: {
86 | block?(result)
87 | })
88 | }
89 | }
90 |
91 | public static func clearAll() {
92 | self.clearLastRefreshInfo()
93 | self.clearExpiredTimeIntervalInfo()
94 | }
95 |
96 | public static func clearLastRefreshInfo() {
97 | UserDefaults.standard.set(nil, forKey: ESRefreshDataManager.lastRefreshKey)
98 | }
99 |
100 | public static func clearExpiredTimeIntervalInfo() {
101 | UserDefaults.standard.set(nil, forKey: ESRefreshDataManager.expiredTimeIntervalKey)
102 | }
103 |
104 | }
105 |
--------------------------------------------------------------------------------
/EasyListViewExample/Vendor/ESPullToRefresh/ESRefreshAnimator.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ESRefreshAnimator.swift
3 | //
4 | // Created by egg swift on 16/4/7.
5 | // Copyright (c) 2013-2016 ESPullToRefresh (https://github.com/eggswift/pull-to-refresh)
6 | //
7 | // Permission is hereby granted, free of charge, to any person obtaining a copy
8 | // of this software and associated documentation files (the "Software"), to deal
9 | // in the Software without restriction, including without limitation the rights
10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | // copies of the Software, and to permit persons to whom the Software is
12 | // furnished to do so, subject to the following conditions:
13 | //
14 | // The above copyright notice and this permission notice shall be included in
15 | // all copies or substantial portions of the Software.
16 | //
17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | // THE SOFTWARE.
24 | //
25 |
26 | import Foundation
27 | import UIKit
28 |
29 | open class ESRefreshAnimator: ESRefreshProtocol, ESRefreshAnimatorProtocol {
30 | // The view that called when component refresh, returns a custom view or self if 'self' is the customized views.
31 | open var view: UIView
32 | // Customized inset.
33 | open var insets: UIEdgeInsets
34 | // Refresh event is executed threshold required y offset, set a value greater than 0.0, the default is 60.0
35 | open var trigger: CGFloat = 60.0
36 | // Offset y refresh event executed by this parameter you can customize the animation to perform when you refresh the view of reservations height
37 | open var executeIncremental: CGFloat = 60.0
38 | // Current refresh state, default is .pullToRefresh
39 | open var state: ESRefreshViewState = .pullToRefresh
40 |
41 | public init() {
42 | view = UIView()
43 | insets = UIEdgeInsets.zero
44 | }
45 |
46 | open func refreshAnimationBegin(view: ESRefreshComponent) {
47 | /// Do nothing!
48 | }
49 |
50 | open func refreshAnimationWillEnd(view: ESRefreshComponent) {
51 | /// Do nothing!
52 | }
53 |
54 | open func refreshAnimationEnd(view: ESRefreshComponent) {
55 | /// Do nothing!
56 | }
57 |
58 | open func refresh(view: ESRefreshComponent, progressDidChange progress: CGFloat) {
59 | /// Do nothing!
60 | }
61 |
62 | open func refresh(view: ESRefreshComponent, stateDidChange state: ESRefreshViewState) {
63 | /// Do nothing!
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/EasyListViewExample/Vendor/ESPullToRefresh/ESRefreshComponent.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ESRefreshComponent.swift
3 | //
4 | // Created by egg swift on 16/4/7.
5 | // Copyright (c) 2013-2016 ESPullToRefresh (https://github.com/eggswift/pull-to-refresh)
6 | //
7 | // Permission is hereby granted, free of charge, to any person obtaining a copy
8 | // of this software and associated documentation files (the "Software"), to deal
9 | // in the Software without restriction, including without limitation the rights
10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | // copies of the Software, and to permit persons to whom the Software is
12 | // furnished to do so, subject to the following conditions:
13 | //
14 | // The above copyright notice and this permission notice shall be included in
15 | // all copies or substantial portions of the Software.
16 | //
17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | // THE SOFTWARE.
24 | //
25 |
26 | import Foundation
27 | import UIKit
28 |
29 | public typealias ESRefreshHandler = (() -> ())
30 |
31 | open class ESRefreshComponent: UIView {
32 |
33 | open weak var scrollView: UIScrollView?
34 |
35 | /// @param handler Refresh callback method
36 | open var handler: ESRefreshHandler?
37 |
38 | /// @param animator Animated view refresh controls, custom must comply with the following two protocol
39 | open var animator: (ESRefreshProtocol & ESRefreshAnimatorProtocol)!
40 |
41 | /// @param refreshing or not
42 | fileprivate var _isRefreshing = false
43 | open var isRefreshing: Bool {
44 | get {
45 | return self._isRefreshing
46 | }
47 | }
48 |
49 | /// @param auto refreshing or not
50 | fileprivate var _isAutoRefreshing = false
51 | open var isAutoRefreshing: Bool {
52 | get {
53 | return self._isAutoRefreshing
54 | }
55 | }
56 |
57 | /// @param tag observing
58 | fileprivate var isObservingScrollView = false
59 | fileprivate var isIgnoreObserving = false
60 |
61 | public override init(frame: CGRect) {
62 | super.init(frame: frame)
63 | autoresizingMask = [.flexibleLeftMargin, .flexibleWidth, .flexibleRightMargin]
64 | }
65 |
66 | public convenience init(frame: CGRect, handler: @escaping ESRefreshHandler) {
67 | self.init(frame: frame)
68 | self.handler = handler
69 | self.animator = ESRefreshAnimator.init()
70 | }
71 |
72 | public convenience init(frame: CGRect, handler: @escaping ESRefreshHandler, animator: ESRefreshProtocol & ESRefreshAnimatorProtocol) {
73 | self.init(frame: frame)
74 | self.handler = handler
75 | self.animator = animator
76 | }
77 |
78 | public required init?(coder aDecoder: NSCoder) {
79 | fatalError("init(coder:) has not been implemented")
80 | }
81 |
82 | deinit {
83 | removeObserver()
84 | }
85 |
86 | open override func willMove(toSuperview newSuperview: UIView?) {
87 | super.willMove(toSuperview: newSuperview)
88 | /// Remove observer from superview immediately
89 | self.removeObserver()
90 | DispatchQueue.main.async { [weak self, newSuperview] in
91 | /// Add observer to new superview in next runloop
92 | self?.addObserver(newSuperview)
93 | }
94 | }
95 |
96 | open override func didMoveToSuperview() {
97 | super.didMoveToSuperview()
98 | self.scrollView = self.superview as? UIScrollView
99 | if let _ = animator {
100 | let v = animator.view
101 | if v.superview == nil {
102 | let inset = animator.insets
103 | self.addSubview(v)
104 | v.frame = CGRect.init(x: inset.left,
105 | y: inset.right,
106 | width: self.bounds.size.width - inset.left - inset.right,
107 | height: self.bounds.size.height - inset.top - inset.bottom)
108 | v.autoresizingMask = [
109 | .flexibleWidth,
110 | .flexibleTopMargin,
111 | .flexibleHeight,
112 | .flexibleBottomMargin
113 | ]
114 | }
115 | }
116 | }
117 |
118 | // MARK: - Action
119 |
120 | public final func startRefreshing(isAuto: Bool = false) -> Void {
121 | guard isRefreshing == false && isAutoRefreshing == false else {
122 | return
123 | }
124 |
125 | _isRefreshing = !isAuto
126 | _isAutoRefreshing = isAuto
127 |
128 | self.start()
129 | }
130 |
131 | public final func stopRefreshing() -> Void {
132 | guard isRefreshing == true || isAutoRefreshing == true else {
133 | return
134 | }
135 |
136 | self.stop()
137 | }
138 |
139 | public func start() {
140 |
141 | }
142 |
143 | public func stop() {
144 | _isRefreshing = false
145 | _isAutoRefreshing = false
146 | }
147 |
148 | // ScrollView contentSize change action
149 | public func sizeChangeAction(object: AnyObject?, change: [NSKeyValueChangeKey : Any]?) {
150 |
151 | }
152 |
153 | // ScrollView offset change action
154 | public func offsetChangeAction(object: AnyObject?, change: [NSKeyValueChangeKey : Any]?) {
155 |
156 | }
157 |
158 | }
159 |
160 | extension ESRefreshComponent /* KVO methods */ {
161 |
162 | fileprivate static var context = "ESRefreshKVOContext"
163 | fileprivate static let offsetKeyPath = "contentOffset"
164 | fileprivate static let contentSizeKeyPath = "contentSize"
165 |
166 | public func ignoreObserver(_ ignore: Bool = false) {
167 | if let scrollView = scrollView {
168 | scrollView.isScrollEnabled = !ignore
169 | }
170 | isIgnoreObserving = ignore
171 | }
172 |
173 | fileprivate func addObserver(_ view: UIView?) {
174 | if let scrollView = view as? UIScrollView, !isObservingScrollView {
175 | scrollView.addObserver(self, forKeyPath: ESRefreshComponent.offsetKeyPath, options: [.initial, .new], context: &ESRefreshComponent.context)
176 | scrollView.addObserver(self, forKeyPath: ESRefreshComponent.contentSizeKeyPath, options: [.initial, .new], context: &ESRefreshComponent.context)
177 | isObservingScrollView = true
178 | }
179 | }
180 |
181 | fileprivate func removeObserver() {
182 | if let scrollView = superview as? UIScrollView, isObservingScrollView {
183 | scrollView.removeObserver(self, forKeyPath: ESRefreshComponent.offsetKeyPath, context: &ESRefreshComponent.context)
184 | scrollView.removeObserver(self, forKeyPath: ESRefreshComponent.contentSizeKeyPath, context: &ESRefreshComponent.context)
185 | isObservingScrollView = false
186 | }
187 | }
188 |
189 | override open func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
190 | if context == &ESRefreshComponent.context {
191 | guard isUserInteractionEnabled == true && isHidden == false else {
192 | return
193 | }
194 | if keyPath == ESRefreshComponent.contentSizeKeyPath {
195 | if isIgnoreObserving == false {
196 | sizeChangeAction(object: object as AnyObject?, change: change)
197 | }
198 | } else if keyPath == ESRefreshComponent.offsetKeyPath {
199 | if isIgnoreObserving == false {
200 | offsetChangeAction(object: object as AnyObject?, change: change)
201 | }
202 | }
203 | } else {
204 |
205 | }
206 | }
207 |
208 | }
209 |
210 |
--------------------------------------------------------------------------------
/EasyListViewExample/Vendor/ESPullToRefresh/ESRefreshProtocol.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ESRefreshProtocol.swift
3 | //
4 | // Created by egg swift on 16/4/7.
5 | // Copyright (c) 2013-2016 ESPullToRefresh (https://github.com/eggswift/pull-to-refresh)
6 | //
7 | // Permission is hereby granted, free of charge, to any person obtaining a copy
8 | // of this software and associated documentation files (the "Software"), to deal
9 | // in the Software without restriction, including without limitation the rights
10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | // copies of the Software, and to permit persons to whom the Software is
12 | // furnished to do so, subject to the following conditions:
13 | //
14 | // The above copyright notice and this permission notice shall be included in
15 | // all copies or substantial portions of the Software.
16 | //
17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | // THE SOFTWARE.
24 | //
25 |
26 | import Foundation
27 | import UIKit
28 |
29 | public enum ESRefreshViewState {
30 | case pullToRefresh
31 | case releaseToRefresh
32 | case refreshing
33 | case autoRefreshing
34 | case noMoreData
35 | }
36 |
37 | /**
38 | * ESRefreshProtocol
39 | * Animation event handling callback protocol
40 | * You can customize the refresh or custom animation effects
41 | * Mutating is to be able to modify or enum struct variable in the method - http://swifter.tips/protocol-mutation/ by ONEVCAT
42 | */
43 | public protocol ESRefreshProtocol {
44 |
45 | /**
46 | Refresh operation begins execution method
47 | You can refresh your animation logic here, it will need to start the animation each time a refresh
48 | */
49 | mutating func refreshAnimationBegin(view: ESRefreshComponent)
50 |
51 | /**
52 | Refresh operation stop execution method
53 | Here you can reset your refresh control UI, such as a Stop UIImageView animations or some opened Timer refresh, etc., it will be executed once each time the need to end the animation
54 | */
55 | mutating func refreshAnimationEnd(view: ESRefreshComponent)
56 |
57 | /**
58 | Pulling status callback , progress is the percentage of the current offset with trigger, and avoid doing too many tasks in this process so as not to affect the fluency.
59 | */
60 | mutating func refresh(view: ESRefreshComponent, progressDidChange progress: CGFloat)
61 |
62 | mutating func refresh(view: ESRefreshComponent, stateDidChange state: ESRefreshViewState)
63 | }
64 |
65 |
66 | public protocol ESRefreshAnimatorProtocol {
67 |
68 | // The view that called when component refresh, returns a custom view or self if 'self' is the customized views.
69 | var view: UIView {get}
70 |
71 | // Customized inset.
72 | var insets: UIEdgeInsets {set get}
73 |
74 | // Refresh event is executed threshold required y offset, set a value greater than 0.0, the default is 60.0
75 | var trigger: CGFloat {set get}
76 |
77 | // Offset y refresh event executed by this parameter you can customize the animation to perform when you refresh the view of reservations height
78 | var executeIncremental: CGFloat {set get}
79 |
80 | // Current refresh state, default is .pullToRefresh
81 | var state: ESRefreshViewState {set get}
82 |
83 | }
84 |
85 | /**
86 | * ESRefreshImpacter
87 | * Support iPhone7/iPhone7 Plus or later feedback impact
88 | * You can confirm the ESRefreshImpactProtocol
89 | */
90 | fileprivate class ESRefreshImpacter {
91 | static private var impacter: AnyObject? = {
92 | if #available(iOS 10.0, *) {
93 | if NSClassFromString("UIFeedbackGenerator") != nil {
94 | let generator = UIImpactFeedbackGenerator.init(style: .light)
95 | generator.prepare()
96 | return generator
97 | }
98 | }
99 | return nil
100 | }()
101 |
102 | static public func impact() -> Void {
103 | if #available(iOS 10.0, *) {
104 | if let impacter = impacter as? UIImpactFeedbackGenerator {
105 | impacter.impactOccurred()
106 | }
107 | }
108 | }
109 | }
110 |
111 | public protocol ESRefreshImpactProtocol {}
112 | public extension ESRefreshImpactProtocol {
113 |
114 | func impact() -> Void {
115 | ESRefreshImpacter.impact()
116 | }
117 |
118 | }
119 |
--------------------------------------------------------------------------------
/EasyListViewExample/Vendor/Stevia/Stevia+Baselines.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Stevia+Baselines.swift
3 | // Stevia
4 | //
5 | // Created by Sacha on 09/09/2018.
6 | // Copyright © 2018 Sacha Durand Saint Omer. All rights reserved.
7 | //
8 |
9 | #if canImport(UIKit)
10 | import UIKit
11 |
12 | /** Aligns an array of views by their lastBaselines (on the Y Axis)
13 |
14 | Example Usage:
15 | ```
16 | align(lastBaselines: label1, label2, label3)
17 | ```
18 |
19 | Can also be used directly on horizontal layouts since they return the array of views :
20 | ```
21 | align(lastBaselines: |-label1-label2-label3-|)
22 | ```
23 |
24 | - Returns: The array of views, enabling chaining,
25 |
26 | */
27 | @discardableResult
28 | public func align(lastBaselines views: UIView...) -> [UIView] {
29 | return align(lastBaselines: views)
30 | }
31 |
32 | @discardableResult
33 | public func align(lastBaselines views: [UIView]) -> [UIView] {
34 | for (i, v) in views.enumerated() where views.count > i+1 {
35 | let v2 = views[i+1]
36 | if #available(iOS 9.0, *) {
37 | v.lastBaselineAnchor.constraint(equalTo: v2.lastBaselineAnchor).isActive = true
38 | } else if let spv = v.superview {
39 | let c = constraint(item: v, attribute: .lastBaseline, toItem: v2)
40 | spv.addConstraint(c)
41 | }
42 | }
43 | return views
44 | }
45 |
46 | /** Aligns an array of views by their firstBaselines (on the Y Axis)
47 |
48 | Example Usage:
49 | ```
50 | align(firstBaselines: label1, label2, label3)
51 | ```
52 |
53 | Can also be used directly on horizontal layouts since they return the array of views :
54 | ```
55 | align(firstBaselines: |-label1-label2-label3-|)
56 | ```
57 |
58 | - Returns: The array of views, enabling chaining,
59 |
60 | */
61 | @discardableResult
62 | public func align(firstBaselines views: UIView...) -> [UIView] {
63 | return align(firstBaselines: views)
64 | }
65 |
66 | @discardableResult
67 | public func align(firstBaselines views: [UIView]) -> [UIView] {
68 | for (i, v) in views.enumerated() where views.count > i+1 {
69 | let v2 = views[i+1]
70 | if #available(iOS 9.0, *) {
71 | v.firstBaselineAnchor.constraint(equalTo: v2.firstBaselineAnchor).isActive = true
72 | } else if let spv = v.superview {
73 | let c = constraint(item: v, attribute: .firstBaseline, toItem: v2)
74 | spv.addConstraint(c)
75 | }
76 | }
77 | return views
78 | }
79 | #endif
80 |
--------------------------------------------------------------------------------
/EasyListViewExample/Vendor/Stevia/Stevia+Center.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Stevia+Center.swift
3 | // Stevia
4 | //
5 | // Created by Sacha Durand Saint Omer on 10/02/16.
6 | // Copyright © 2016 Sacha Durand Saint Omer. All rights reserved.
7 | //
8 |
9 | #if canImport(UIKit)
10 | import UIKit
11 |
12 | public extension UIView {
13 |
14 | /**
15 | Centers the view in its container.
16 |
17 | ```
18 | button.centerInContainer()
19 | ```
20 |
21 | - Returns: Itself, enabling chaining,
22 |
23 | */
24 | @discardableResult
25 | func centerInContainer() -> Self {
26 | if let spv = superview {
27 | alignCenter(self, with: spv)
28 | }
29 | return self
30 | }
31 |
32 | /**
33 | Centers the view horizontally (X axis) in its container.
34 |
35 | ```
36 | button.centerHorizontally()
37 | button.centerHorizontally(offset: 40)
38 | ```
39 |
40 | - Returns: Itself, enabling chaining,
41 |
42 | */
43 | @discardableResult
44 | func centerHorizontally(offset: Double = 0) -> Self {
45 | if let spv = superview {
46 | align(.vertical, v1: self, with: spv, offset: offset)
47 | }
48 | return self
49 | }
50 |
51 | /**
52 | Centers the view horizontally (X axis) in its container.
53 |
54 | ```
55 | button.centerHorizontally()
56 | button.centerHorizontally(offset: 40)
57 | ```
58 |
59 | - Returns: Itself, enabling chaining,
60 |
61 | */
62 | @discardableResult
63 | func centerHorizontally(offset: CGFloat) -> Self {
64 | centerHorizontally(offset: Double(offset))
65 | }
66 |
67 | /**
68 | Centers the view horizontally (X axis) in its container.
69 |
70 | ```
71 | button.centerHorizontally()
72 | button.centerHorizontally(offset: 40)
73 | ```
74 |
75 | - Returns: Itself, enabling chaining,
76 |
77 | */
78 | @discardableResult
79 | func centerHorizontally(offset: Int) -> Self {
80 | centerHorizontally(offset: Double(offset))
81 | }
82 |
83 | /**
84 | Centers the view vertically (Y axis) in its container.
85 |
86 | ```
87 | button.centerVertically()
88 | button.centerVertically(offset: 40)
89 | ```
90 |
91 | - Returns: Itself, enabling chaining,
92 |
93 | */
94 | @discardableResult
95 | func centerVertically(offset: Double = 0) -> Self {
96 | if let spv = superview {
97 | align(.horizontal, v1: self, with: spv, offset: offset)
98 | }
99 | return self
100 | }
101 |
102 | /**
103 | Centers the view vertically (Y axis) in its container.
104 |
105 | ```
106 | button.centerVertically()
107 | button.centerVertically(offset: 40)
108 | ```
109 |
110 | - Returns: Itself, enabling chaining,
111 |
112 | */
113 | @discardableResult
114 | func centerVertically(offset: CGFloat) -> Self {
115 | centerVertically(offset: Double(offset))
116 | }
117 |
118 | /**
119 | Centers the view vertically (Y axis) in its container.
120 |
121 | ```
122 | button.centerVertically()
123 | button.centerVertically(offset: 40)
124 | ```
125 |
126 | - Returns: Itself, enabling chaining,
127 |
128 | */
129 | @discardableResult
130 | func centerVertically(offset: Int) -> Self {
131 | centerVertically(offset: Double(offset))
132 | }
133 | }
134 | #endif
135 |
--------------------------------------------------------------------------------
/EasyListViewExample/Vendor/Stevia/Stevia+Constraints.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Stevia+Constraints.swift
3 | // LoginNadir
4 | //
5 | // Created by Sacha Durand Saint Omer on 01/10/15.
6 | // Copyright © 2015 Sacha Durand Saint Omer. All rights reserved.
7 | //
8 |
9 | #if canImport(UIKit)
10 | import UIKit
11 |
12 | // MARK: - Shortcut
13 |
14 | public extension UIView {
15 |
16 | /**
17 | Helper for creating and adding NSLayoutConstraint but with default values provided.
18 |
19 | For instance
20 |
21 | addConstraint(item: view1, attribute: .CenterX, toItem: view2)
22 |
23 | is equivalent to
24 |
25 | addConstraint(
26 | NSLayoutConstraint(item: view1,
27 | attribute: .CenterX,
28 | relatedBy: .Equal,
29 | toItem: view2,
30 | attribute: .CenterX,
31 | multiplier: 1,
32 | constant: 0
33 | )
34 | )
35 |
36 | - Returns: The NSLayoutConstraint created.
37 | */
38 | @discardableResult
39 | func addConstraint(item view1: AnyObject,
40 | attribute attr1: NSLayoutConstraint.Attribute,
41 | relatedBy: NSLayoutConstraint.Relation = .equal,
42 | toItem view2: AnyObject? = nil,
43 | attribute attr2: NSLayoutConstraint.Attribute? = nil,
44 | multiplier: Double = 1,
45 | constant: Double = 0) -> NSLayoutConstraint {
46 | let c = constraint(
47 | item: view1, attribute: attr1,
48 | relatedBy: relatedBy,
49 | toItem: view2, attribute: attr2,
50 | multiplier: multiplier, constant: constant)
51 | addConstraint(c)
52 | return c
53 | }
54 | }
55 |
56 | /**
57 | Helper for creating a NSLayoutConstraint but with default values provided.
58 |
59 | For instance
60 |
61 | constraint(item: view1, attribute: .CenterX, toItem: view2)
62 |
63 | is equivalent to
64 |
65 | NSLayoutConstraint(item: view1, attribute: .CenterX,
66 | relatedBy: .Equal,
67 | toItem: view2, attribute: .CenterX,
68 | multiplier: 1, constant: 0)
69 |
70 | - Returns: The NSLayoutConstraint created.
71 | */
72 | func constraint(item view1: AnyObject,
73 | attribute attr1: NSLayoutConstraint.Attribute,
74 | relatedBy: NSLayoutConstraint.Relation = .equal,
75 | toItem view2: AnyObject? = nil,
76 | attribute attr2: NSLayoutConstraint.Attribute? = nil, // Not an attribute??
77 | multiplier: Double = 1,
78 | constant: Double = 0) -> NSLayoutConstraint {
79 | let c = NSLayoutConstraint(item: view1, attribute: attr1,
80 | relatedBy: relatedBy,
81 | toItem: view2, attribute: ((attr2 == nil) ? attr1 : attr2! ),
82 | multiplier: CGFloat(multiplier), constant: CGFloat(constant))
83 | c.priority = UILayoutPriority(rawValue: UILayoutPriority.defaultHigh.rawValue + 1)
84 | return c
85 | }
86 |
87 | public extension UIView {
88 |
89 | /**
90 | Get User added constraints. For making complex changes on layout, we need to remove user added constraints.
91 |
92 | If we remove all constraints, it may return broken layout.
93 |
94 | Use this method as:
95 |
96 | removeConstraints(userAddedConstraints)
97 |
98 | */
99 | var userAddedConstraints: [NSLayoutConstraint] {
100 | return constraints.filter { c in
101 | guard let cId = c.identifier else { return true }
102 | return !cId.contains("UIView-Encapsulated-Layout") && !cId.contains("Margin-guide-constraint")
103 | }
104 | }
105 | }
106 |
107 | // MARK: - Other
108 |
109 | public extension UIView {
110 |
111 | /**
112 | Makes a view follow another view's frame.
113 | For instance if we want a button to be on top of an image :
114 |
115 | ```
116 | button.followEdges(image)
117 | ```
118 | */
119 | func followEdges(_ otherView: UIView) {
120 | if let spv = superview {
121 | let cs = [
122 | constraint(item: self, attribute: .top, toItem: otherView),
123 | constraint(item: self, attribute: .trailing, toItem: otherView),
124 | constraint(item: self, attribute: .bottom, toItem: otherView),
125 | constraint(item: self, attribute: .leading, toItem: otherView)
126 | ]
127 | spv.addConstraints(cs)
128 | }
129 | }
130 |
131 | /**
132 | Enforce a view to keep height and width equal at all times, essentially
133 | forcing it to be a square.
134 |
135 | ```
136 | image.heightEqualsWidth()
137 | ```
138 |
139 | - Returns: Itself, enabling chaining,
140 |
141 | */
142 | @discardableResult
143 | func heightEqualsWidth() -> Self {
144 | if let spv = superview {
145 | let c = constraint(item: self, attribute: .height, toItem: self, attribute: .width)
146 | spv.addConstraint(c)
147 | }
148 | return self
149 | }
150 |
151 | }
152 | #endif
153 |
--------------------------------------------------------------------------------
/EasyListViewExample/Vendor/Stevia/Stevia+Content.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Stevia+Content.swift
3 | // LoginStevia
4 | //
5 | // Created by Sacha Durand Saint Omer on 01/10/15.
6 | // Copyright © 2015 Sacha Durand Saint Omer. All rights reserved.
7 | //
8 |
9 | #if canImport(UIKit)
10 | import UIKit
11 |
12 | public extension UIButton {
13 |
14 | /**
15 | Sets the title of the button for normal State
16 |
17 | Essentially a shortcut for `setTitle("MyText", forState: .Normal)`
18 |
19 | - Returns: Itself for chaining purposes
20 | */
21 | @discardableResult
22 | func text(_ t: String) -> Self {
23 | setTitle(t, for: .normal)
24 | return self
25 | }
26 |
27 | /**
28 | Sets the localized key for the button's title in normal State
29 |
30 | Essentially a shortcut for `setTitle(NSLocalizedString("MyText", comment: "")
31 | , forState: .Normal)`
32 |
33 | - Returns: Itself for chaining purposes
34 | */
35 | @discardableResult
36 | func textKey(_ t: String) -> Self {
37 | text(NSLocalizedString(t, comment: ""))
38 | return self
39 | }
40 |
41 | /**
42 | Sets the image of the button in normal State
43 |
44 | Essentially a shortcut for `setImage(UIImage(named:"X"), forState: .Normal)`
45 |
46 | - Returns: Itself for chaining purposes
47 | */
48 | @discardableResult
49 | func image(_ s: String) -> Self {
50 | setImage(UIImage(named: s), for: .normal)
51 | return self
52 | }
53 | }
54 |
55 | public extension UITextField {
56 | /**
57 | Sets the textfield placeholder but in a chainable fashion
58 | - Returns: Itself for chaining purposes
59 | */
60 | @discardableResult
61 | func placeholder(_ t: String) -> Self {
62 | placeholder = t
63 | return self
64 | }
65 | }
66 |
67 | public extension UILabel {
68 | /**
69 | Sets the label text but in a chainable fashion
70 | - Returns: Itself for chaining purposes
71 | */
72 | @discardableResult
73 | func text(_ t: String) -> Self {
74 | text = t
75 | return self
76 | }
77 |
78 | /**
79 | Sets the label localization key but in a chainable fashion
80 | Essentially a shortcut for `text = NSLocalizedString("X", comment: "")`
81 | - Returns: Itself for chaining purposes
82 | */
83 | @discardableResult
84 | func textKey(_ t: String) -> Self {
85 | text(NSLocalizedString(t, comment: ""))
86 | return self
87 | }
88 | }
89 |
90 | extension UIImageView {
91 | /**
92 | Sets the image of the imageView but in a chainable fashion
93 |
94 | Essentially a shortcut for `image = UIImage(named: "X")`
95 |
96 | - Returns: Itself for chaining purposes
97 | */
98 | @discardableResult
99 | public func image(_ t: String) -> Self {
100 | image = UIImage(named: t)
101 | return self
102 | }
103 | }
104 | #endif
105 |
--------------------------------------------------------------------------------
/EasyListViewExample/Vendor/Stevia/Stevia+DoubleDash.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Stevia+DoubleDash.swift
3 | // Stevia
4 | //
5 | // Created by Sacha Durand Saint Omer on 03/05/16.
6 | // Copyright © 2016 Sacha Durand Saint Omer. All rights reserved.
7 | //
8 |
9 | #if canImport(UIKit)
10 | import UIKit
11 |
12 | infix operator -- :AdditionPrecedence
13 |
14 |
15 | @available(*, deprecated, renamed: "⁃")
16 | @discardableResult
17 | public func -- (left: UIView, right: Double) -> PartialConstraint {
18 | return left-right
19 | }
20 |
21 | @available(*, deprecated, renamed: "⁃")
22 | public func -- (left: UIView, right: CGFloat) -> PartialConstraint {
23 | return left--Double(right)
24 | }
25 |
26 | @available(*, deprecated, renamed: "⁃")
27 | public func -- (left: UIView, right: Int) -> PartialConstraint {
28 | return left--Double(right)
29 | }
30 |
31 | @available(*, deprecated, renamed: "⁃")
32 | @discardableResult
33 | public func -- (left: SideConstraint, right: UIView) -> UIView {
34 | return left-right
35 | }
36 |
37 | @available(*, deprecated, renamed: "⁃")
38 | @discardableResult
39 | public func -- (left: [UIView], right: SideConstraint) -> [UIView] {
40 | return left-right
41 | }
42 |
43 | @available(*, deprecated, renamed: "⁃")
44 | @discardableResult
45 | public func -- (left: UIView, right: SideConstraint) -> UIView {
46 | return left-right
47 | }
48 |
49 | @available(*, deprecated, renamed: "⁃")
50 | @discardableResult
51 | public func -- (left: PartialConstraint, right: UIView) -> [UIView] {
52 | return left-right
53 | }
54 |
55 | @available(*, deprecated, renamed: "⁃")
56 | @discardableResult
57 | public func -- (left: UIView, right: UIView) -> [UIView] {
58 | return left-right
59 | }
60 |
61 | @available(*, deprecated, renamed: "⁃")
62 | @discardableResult
63 | public func -- (left: [UIView], right: Double) -> PartialConstraint {
64 | return left-right
65 | }
66 |
67 | @available(*, deprecated, renamed: "⁃")
68 | @discardableResult
69 | public func -- (left: [UIView], right: CGFloat) -> PartialConstraint {
70 | return left--Double(right)
71 | }
72 |
73 | @available(*, deprecated, renamed: "⁃")
74 | @discardableResult
75 | public func -- (left: [UIView], right: Int) -> PartialConstraint {
76 | return left--Double(right)
77 | }
78 |
79 | @available(*, deprecated, renamed: "⁃")
80 | @discardableResult
81 | public func -- (left: [UIView], right: UIView) -> [UIView] {
82 | return left-right
83 | }
84 |
85 | @available(*, deprecated, renamed: "⁃")
86 | @discardableResult
87 | public func -- (left: UIView, right: String) -> Space {
88 | return left-right
89 | }
90 |
91 | @available(*, deprecated, renamed: "⁃")
92 | @discardableResult
93 | public func -- (left: [UIView], right: String) -> Space {
94 | return left-right
95 | }
96 |
97 | @available(*, deprecated, renamed: "⁃")
98 | @discardableResult
99 | public func -- (left: Space, right: UIView) -> [UIView] {
100 | return left-right
101 | }
102 |
103 | @available(*, deprecated, renamed: "⁃")
104 | @discardableResult
105 | public func -- (left: UIView,
106 | right: SteviaFlexibleMargin) -> PartialFlexibleConstraint {
107 | return left-right
108 | }
109 |
110 | @available(*, deprecated, renamed: "⁃")
111 | @discardableResult
112 | public func -- (left: [UIView],
113 | right: SteviaFlexibleMargin) -> PartialFlexibleConstraint {
114 | return left-right
115 | }
116 |
117 | @available(*, deprecated, renamed: "⁃")
118 | @discardableResult
119 | public func -- (left: PartialFlexibleConstraint, right: UIView) -> [UIView] {
120 | return left-right
121 | }
122 |
123 | @available(*, deprecated, renamed: "⁃")
124 | @discardableResult
125 | public func -- (left: SteviaLeftFlexibleMargin, right: UIView) -> UIView {
126 | return left-right
127 | }
128 |
129 | @available(*, deprecated, renamed: "⁃")
130 | @discardableResult
131 | public func -- (left: UIView, right: SteviaRightFlexibleMargin) -> UIView {
132 | return left-right
133 | }
134 |
135 | @available(*, deprecated, renamed: "⁃")
136 | @discardableResult
137 | public func -- (left: [UIView], right: SteviaRightFlexibleMargin) -> [UIView] {
138 | return left-right
139 | }
140 | #endif
141 |
142 | /// Hyphen Bullet operator is introduced to remove the ambiguity with the language "-" (minus) sign operator.
143 | /// this ambiguity can make tu build times exponentially longer in big layout blocks.
144 |
145 |
146 | #if canImport(UIKit)
147 | import UIKit
148 |
149 | infix operator ⁃ :AdditionPrecedence
150 |
151 | @discardableResult
152 | public func ⁃ (left: UIView, right: Double) -> PartialConstraint {
153 | return left-right
154 | }
155 |
156 | public func ⁃ (left: UIView, right: CGFloat) -> PartialConstraint {
157 | return left⁃Double(right)
158 | }
159 |
160 | public func ⁃ (left: UIView, right: Int) -> PartialConstraint {
161 | return left⁃Double(right)
162 | }
163 |
164 | @discardableResult
165 | public func ⁃ (left: SideConstraint, right: UIView) -> UIView {
166 | return left-right
167 | }
168 |
169 | @discardableResult
170 | public func ⁃ (left: [UIView], right: SideConstraint) -> [UIView] {
171 | return left-right
172 | }
173 |
174 | @discardableResult
175 | public func ⁃ (left: UIView, right: SideConstraint) -> UIView {
176 | return left-right
177 | }
178 |
179 | @discardableResult
180 | public func ⁃ (left: PartialConstraint, right: UIView) -> [UIView] {
181 | return left-right
182 | }
183 |
184 | @discardableResult
185 | public func ⁃ (left: UIView, right: UIView) -> [UIView] {
186 | return left-right
187 | }
188 |
189 | @discardableResult
190 | public func ⁃ (left: [UIView], right: Double) -> PartialConstraint {
191 | return left-right
192 | }
193 |
194 | @discardableResult
195 | public func ⁃ (left: [UIView], right: CGFloat) -> PartialConstraint {
196 | return left⁃Double(right)
197 | }
198 |
199 | @discardableResult
200 | public func ⁃ (left: [UIView], right: Int) -> PartialConstraint {
201 | return left⁃Double(right)
202 | }
203 |
204 | @discardableResult
205 | public func ⁃ (left: [UIView], right: UIView) -> [UIView] {
206 | return left-right
207 | }
208 |
209 | @discardableResult
210 | public func ⁃ (left: UIView, right: String) -> Space {
211 | return left-right
212 | }
213 |
214 | @discardableResult
215 | public func ⁃ (left: [UIView], right: String) -> Space {
216 | return left-right
217 | }
218 |
219 | @discardableResult
220 | public func ⁃ (left: Space, right: UIView) -> [UIView] {
221 | return left-right
222 | }
223 |
224 | @discardableResult
225 | public func ⁃ (left: UIView,
226 | right: SteviaFlexibleMargin) -> PartialFlexibleConstraint {
227 | return left-right
228 | }
229 |
230 | @discardableResult
231 | public func ⁃ (left: [UIView],
232 | right: SteviaFlexibleMargin) -> PartialFlexibleConstraint {
233 | return left-right
234 | }
235 |
236 | @discardableResult
237 | public func ⁃ (left: PartialFlexibleConstraint, right: UIView) -> [UIView] {
238 | return left-right
239 | }
240 |
241 | @discardableResult
242 | public func ⁃ (left: SteviaLeftFlexibleMargin, right: UIView) -> UIView {
243 | return left-right
244 | }
245 |
246 | @discardableResult
247 | public func ⁃ (left: UIView, right: SteviaRightFlexibleMargin) -> UIView {
248 | return left-right
249 | }
250 |
251 | @discardableResult
252 | public func ⁃ (left: [UIView], right: SteviaRightFlexibleMargin) -> [UIView] {
253 | return left-right
254 | }
255 | #endif
256 |
--------------------------------------------------------------------------------
/EasyListViewExample/Vendor/Stevia/Stevia+Fill.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Stevia+Fill.swift
3 | // Stevia
4 | //
5 | // Created by Sacha Durand Saint Omer on 10/02/16.
6 | // Copyright © 2016 Sacha Durand Saint Omer. All rights reserved.
7 | //
8 |
9 | #if canImport(UIKit)
10 | import UIKit
11 |
12 | public extension UIView {
13 |
14 | /**
15 | Adds the constraints needed for the view to fill its `superview`.
16 | A padding can be used to apply equal spaces between the view and its superview
17 | */
18 | @discardableResult
19 | func fillContainer(padding: Double = 0) -> Self {
20 | fillHorizontally(padding: padding)
21 | fillVertically(padding: padding)
22 | return self
23 | }
24 |
25 | /**
26 | Adds the constraints needed for the view to fill its `superview`.
27 | A padding can be used to apply equal spaces between the view and its superview
28 | */
29 | @discardableResult
30 | func fillContainer(padding: CGFloat) -> Self {
31 | fillContainer(padding: Double(padding))
32 | }
33 |
34 | /**
35 | Adds the constraints needed for the view to fill its `superview`.
36 | A padding can be used to apply equal spaces between the view and its superview
37 | */
38 | @discardableResult
39 | func fillContainer(padding: Int) -> Self {
40 | fillContainer(padding: Double(padding))
41 | }
42 |
43 | /**
44 | Adds the constraints needed for the view to fill its `superview` Vertically.
45 | A padding can be used to apply equal spaces between the view and its superview
46 | */
47 | @discardableResult
48 | func fillVertically(padding: Double = 0) -> Self {
49 | fill(.vertical, points: padding)
50 | }
51 |
52 | /**
53 | Adds the constraints needed for the view to fill its `superview` Vertically.
54 | A padding can be used to apply equal spaces between the view and its superview
55 | */
56 | @discardableResult
57 | func fillVertically(padding: CGFloat) -> Self {
58 | fillVertically(padding: Double(padding))
59 | }
60 |
61 | /**
62 | Adds the constraints needed for the view to fill its `superview` Vertically.
63 | A padding can be used to apply equal spaces between the view and its superview
64 | */
65 | @discardableResult
66 | func fillVertically(padding: Int) -> Self {
67 | fillVertically(padding: Double(padding))
68 | }
69 |
70 | /**
71 | Adds the constraints needed for the view to fill its `superview` Horizontally.
72 | A padding can be used to apply equal spaces between the view and its superview
73 | */
74 | @discardableResult
75 | func fillHorizontally(padding: Double = 0) -> Self {
76 | fill(.horizontal, points: padding)
77 | }
78 |
79 | /**
80 | Adds the constraints needed for the view to fill its `superview` Horizontally.
81 | A padding can be used to apply equal spaces between the view and its superview
82 | */
83 | @discardableResult
84 | func fillHorizontally(padding: CGFloat) -> Self {
85 | fillHorizontally(padding: Double(padding))
86 | }
87 |
88 | /**
89 | Adds the constraints needed for the view to fill its `superview` Horizontally.
90 | A padding can be used to apply equal spaces between the view and its superview
91 | */
92 | @discardableResult
93 | func fillHorizontally(padding: Int) -> Self {
94 | fillHorizontally(padding: Double(padding))
95 | }
96 |
97 | fileprivate func fill(_ axis: NSLayoutConstraint.Axis, points: Double = 0) -> Self {
98 | let a: NSLayoutConstraint.Attribute = axis == .vertical ? .top : .leading
99 | let b: NSLayoutConstraint.Attribute = axis == .vertical ? .bottom : .trailing
100 | if let spv = superview {
101 | let c1 = constraint(item: self, attribute: a, toItem: spv, constant: points)
102 | let c2 = constraint(item: self, attribute: b, toItem: spv, constant: -points)
103 | spv.addConstraints([c1, c2])
104 | }
105 | return self
106 | }
107 | }
108 | #endif
109 |
--------------------------------------------------------------------------------
/EasyListViewExample/Vendor/Stevia/Stevia+FlexibleMargin.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Stevia+FlexibleMargin.swift
3 | // Stevia
4 | //
5 | // Created by Sacha Durand Saint Omer on 10/07/16.
6 | // Copyright © 2016 Sacha Durand Saint Omer. All rights reserved.
7 | //
8 |
9 | #if canImport(UIKit)
10 | import UIKit
11 |
12 | prefix operator >=
13 | @discardableResult
14 | public prefix func >= (p: Double) -> SteviaFlexibleMargin {
15 | SteviaFlexibleMargin(points: p, relation: .greaterThanOrEqual)
16 | }
17 |
18 | @discardableResult
19 | public prefix func >= (p: CGFloat) -> SteviaFlexibleMargin {
20 | >=Double(p)
21 | }
22 |
23 | @discardableResult
24 | public prefix func >= (p: Int) -> SteviaFlexibleMargin {
25 | >=Double(p)
26 | }
27 |
28 | prefix operator <=
29 | @discardableResult
30 | public prefix func <= (p: Double) -> SteviaFlexibleMargin {
31 | SteviaFlexibleMargin(points: p, relation: .lessThanOrEqual)
32 | }
33 |
34 | @discardableResult
35 | public prefix func <= (p: CGFloat) -> SteviaFlexibleMargin {
36 | <=Double(p)
37 | }
38 |
39 | @discardableResult
40 | public prefix func <= (p: Int) -> SteviaFlexibleMargin {
41 | <=Double(p)
42 | }
43 |
44 | public struct SteviaFlexibleMargin {
45 | var points: Double!
46 | var relation: NSLayoutConstraint.Relation!
47 | }
48 |
49 | public struct PartialFlexibleConstraint {
50 | var fm: SteviaFlexibleMargin!
51 | var view1: UIView?
52 | var views: [UIView]?
53 | }
54 |
55 | @discardableResult
56 | public func - (left: UIView,
57 | right: SteviaFlexibleMargin) -> PartialFlexibleConstraint {
58 | return PartialFlexibleConstraint(fm: right, view1: left, views: nil)
59 | }
60 |
61 | @discardableResult
62 | public func - (left: [UIView],
63 | right: SteviaFlexibleMargin) -> PartialFlexibleConstraint {
64 | return PartialFlexibleConstraint(fm: right, view1: nil, views: left)
65 | }
66 |
67 | @discardableResult
68 | public func - (left: PartialFlexibleConstraint, right: UIView) -> [UIView] {
69 | if let views = left.views {
70 | if let spv = right.superview {
71 | let c = constraint(item: right, attribute: .leading,
72 | relatedBy: left.fm.relation, toItem: views.last,
73 | attribute: .trailing,
74 | constant: left.fm.points)
75 | spv.addConstraint(c)
76 | }
77 | return views + [right]
78 | } else {
79 | if let spv = right.superview {
80 | let c = constraint(item: right, attribute: .leading,
81 | relatedBy: left.fm.relation, toItem: left.view1!,
82 | attribute: .trailing,
83 | constant: left.fm.points)
84 | spv.addConstraint(c)
85 | }
86 | return [left.view1!, right]
87 | }
88 | }
89 |
90 | // Left Flexible margin
91 |
92 | public struct SteviaLeftFlexibleMargin {
93 | let fm: SteviaFlexibleMargin
94 | }
95 |
96 | @discardableResult
97 | public prefix func |- (fm: SteviaFlexibleMargin) -> SteviaLeftFlexibleMargin {
98 | return SteviaLeftFlexibleMargin(fm: fm)
99 | }
100 |
101 | @discardableResult
102 | public func - (left: SteviaLeftFlexibleMargin, right: UIView) -> UIView {
103 | if let spv = right.superview {
104 | let c = constraint(item: right, attribute: .leading,
105 | relatedBy: left.fm.relation, toItem: spv,
106 | attribute: .leading,
107 | constant: left.fm.points)
108 | spv.addConstraint(c)
109 | }
110 | return right
111 | }
112 |
113 | // Right Flexible margin
114 |
115 | public struct SteviaRightFlexibleMargin {
116 | let fm: SteviaFlexibleMargin
117 | }
118 |
119 | @discardableResult
120 | public postfix func -| (fm: SteviaFlexibleMargin) -> SteviaRightFlexibleMargin {
121 | return SteviaRightFlexibleMargin(fm: fm)
122 | }
123 |
124 | @discardableResult
125 | public func - (left: UIView, right: SteviaRightFlexibleMargin) -> UIView {
126 | if let spv = left.superview {
127 | let c = constraint(item: spv, attribute: .trailing,
128 | relatedBy: right.fm.relation, toItem: left,
129 | attribute: .trailing,
130 | constant: right.fm.points)
131 | spv.addConstraint(c)
132 | }
133 | return left
134 | }
135 |
136 | @discardableResult
137 | public func - (left: [UIView], right: SteviaRightFlexibleMargin) -> [UIView] {
138 | if let spv = left.last!.superview {
139 | let c = constraint(item: spv, attribute: .trailing,
140 | relatedBy: right.fm.relation,
141 | toItem: left.last!,
142 | attribute: .trailing,
143 | constant: right.fm.points)
144 | spv.addConstraint(c)
145 | }
146 | return left
147 | }
148 | #endif
149 |
--------------------------------------------------------------------------------
/EasyListViewExample/Vendor/Stevia/Stevia+GetConstraint.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Stevia+GetConstraint.swift
3 | // Stevia
4 | //
5 | // Created by Sacha Durand Saint Omer on 12/03/16.
6 | // Copyright © 2016 Sacha Durand Saint Omer. All rights reserved.
7 | //
8 |
9 | #if canImport(UIKit)
10 | import UIKit
11 |
12 | public extension UIView {
13 |
14 | /** Gets the left constraint if found.
15 |
16 | Example Usage for changing left margin of a label :
17 | ```
18 | label.leftConstraint?.constant = 10
19 |
20 | // Animate if needed
21 | UIView.animateWithDuration(0.3, animations:layoutIfNeeded)
22 | ```
23 | - Returns: The left NSLayoutConstraint if found.
24 | */
25 | var leftConstraint: NSLayoutConstraint? {
26 | return constraintForView(self, attribute: .left)
27 | }
28 |
29 | /** Gets the right constraint if found.
30 |
31 | Example Usage for changing right margin of a label :
32 |
33 | ```
34 | label.rightConstraint?.constant = 10
35 |
36 | // Animate if needed
37 | UIView.animateWithDuration(0.3, animations:layoutIfNeeded)
38 | ```
39 | - Returns: The right NSLayoutConstraint if found.
40 | */
41 | var rightConstraint: NSLayoutConstraint? {
42 | return constraintForView(self, attribute: .right)
43 | }
44 |
45 | /** Gets the top constraint if found.
46 |
47 | Example Usage for changing top margin of a label :
48 |
49 | ```
50 | label.topConstraint?.constant = 10
51 |
52 | // Animate if needed
53 | UIView.animateWithDuration(0.3, animations:layoutIfNeeded)
54 | ```
55 | - Returns: The top NSLayoutConstraint if found.
56 | */
57 | var topConstraint: NSLayoutConstraint? {
58 | return constraintForView(self, attribute: .top)
59 | }
60 |
61 | /** Gets the bottom constraint if found.
62 |
63 | Example Usage for changing bottom margin of a label :
64 |
65 | ```
66 | label.bottomConstraint?.constant = 10
67 |
68 | // Animate if needed
69 | UIView.animateWithDuration(0.3, animations:layoutIfNeeded)
70 | ```
71 | - Returns: The bottom NSLayoutConstraint if found.
72 | */
73 | var bottomConstraint: NSLayoutConstraint? {
74 | return constraintForView(self, attribute: .bottom)
75 | }
76 |
77 | /** Gets the height constraint if found.
78 |
79 | Example Usage for changing height property of a label :
80 |
81 | ```
82 | label.heightConstraint?.constant = 10
83 |
84 | // Animate if needed
85 | UIView.animateWithDuration(0.3, animations:layoutIfNeeded)
86 | ```
87 | - Returns: The height NSLayoutConstraint if found.
88 | */
89 | var heightConstraint: NSLayoutConstraint? {
90 | return constraintForView(self, attribute: .height)
91 | }
92 |
93 | /** Gets the width constraint if found.
94 |
95 | Example Usage for changing width property of a label :
96 |
97 | ```
98 | label.widthConstraint?.constant = 10
99 |
100 | // Animate if needed
101 | UIView.animateWithDuration(0.3, animations:layoutIfNeeded)
102 | ```
103 | - Returns: The width NSLayoutConstraint if found.
104 | */
105 | var widthConstraint: NSLayoutConstraint? {
106 | return constraintForView(self, attribute: .width)
107 | }
108 |
109 | /** Gets the trailing constraint if found.
110 |
111 | Example Usage for changing width property of a label :
112 |
113 | ```
114 | label.trailingConstraint?.constant = 10
115 |
116 | // Animate if needed
117 | UIView.animateWithDuration(0.3, animations:layoutIfNeeded)
118 | ```
119 | - Returns: The trailing NSLayoutConstraint if found.
120 | */
121 | var trailingConstraint: NSLayoutConstraint? {
122 | return constraintForView(self, attribute: .trailing)
123 | }
124 |
125 | /** Gets the leading constraint if found.
126 |
127 | Example Usage for changing width property of a label :
128 |
129 | ```
130 | label.leadingConstraint?.constant = 10
131 |
132 | // Animate if needed
133 | UIView.animateWithDuration(0.3, animations:layoutIfNeeded)
134 | ```
135 | - Returns: The leading NSLayoutConstraint if found.
136 | */
137 | var leadingConstraint: NSLayoutConstraint? {
138 | return constraintForView(self, attribute: .leading)
139 | }
140 |
141 | /** Gets the centerX constraint if found.
142 |
143 | Example Usage for changing width property of a label :
144 |
145 | ```
146 | label.centerXConstraint?.constant = 10
147 |
148 | // Animate if needed
149 | UIView.animateWithDuration(0.3, animations:layoutIfNeeded)
150 | ```
151 | - Returns: The width NSLayoutConstraint if found.
152 | */
153 | var centerXConstraint: NSLayoutConstraint? {
154 | return constraintForView(self, attribute: .centerX)
155 | }
156 |
157 | /** Gets the centerY constraint if found.
158 |
159 | Example Usage for changing width property of a label :
160 |
161 | ```
162 | label.centerYConstraint?.constant = 10
163 |
164 | // Animate if needed
165 | UIView.animateWithDuration(0.3, animations:layoutIfNeeded)
166 | ```
167 | - Returns: The width NSLayoutConstraint if found.
168 | */
169 | var centerYConstraint: NSLayoutConstraint? {
170 | return constraintForView(self, attribute: .centerY)
171 | }
172 | }
173 |
174 | func constraintForView(_ v: UIView, attribute: NSLayoutConstraint.Attribute) -> NSLayoutConstraint? {
175 |
176 | func lookForConstraint(in view: UIView?) -> NSLayoutConstraint? {
177 | guard let constraints = view?.constraints else {
178 | return nil
179 | }
180 | for c in constraints {
181 | if let fi = c.firstItem as? NSObject, fi == v && c.firstAttribute == attribute {
182 | return c
183 | } else if let si = c.secondItem as? NSObject, si == v && c.secondAttribute == attribute {
184 | return c
185 | }
186 | }
187 | return nil
188 | }
189 |
190 | // Width and height constraints added via widthAnchor/heightAnchors are
191 | // added on the view itself.
192 | if (attribute == .width || attribute == .height) {
193 | return lookForConstraint(in: v.superview) ?? lookForConstraint(in: v)
194 | }
195 |
196 | // Look for constraint on superview.
197 | return lookForConstraint(in: v.superview)
198 | }
199 | #endif
200 |
--------------------------------------------------------------------------------
/EasyListViewExample/Vendor/Stevia/Stevia+Hierarchy.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Stevia+Hierarchy.swift
3 | // LoginStevia
4 | //
5 | // Created by Sacha Durand Saint Omer on 01/10/15.
6 | // Copyright © 2015 Sacha Durand Saint Omer. All rights reserved.
7 | //
8 |
9 | #if canImport(UIKit)
10 | import UIKit
11 |
12 | @_functionBuilder public struct SubviewsBuilder {
13 | public static func buildBlock(_ content: UIView...) -> [UIView] {
14 | return content
15 | }
16 | }
17 |
18 | public extension UIView {
19 |
20 | @available(*, deprecated, renamed: "subviews")
21 | @discardableResult
22 | func sv(_ subViews: UIView...) -> UIView {
23 | subviews(subViews)
24 | }
25 |
26 | /**
27 | Defines the view hierachy for the view.
28 |
29 | Esentially, this is just a shortcut to `addSubview`
30 | and 'translatesAutoresizingMaskIntoConstraints = false'
31 |
32 |
33 |
34 | ```
35 | class MyView: UIView {
36 |
37 | let email = UITextField()
38 | let password = UITextField()
39 | let login = UIButton()
40 |
41 | convenience init() {
42 | self.init(frame: CGRect.zero)
43 |
44 | subviews(
45 | email,
46 | password,
47 | login
48 | )
49 | ...
50 |
51 | }
52 | }
53 |
54 | ```
55 |
56 | - Returns: Itself to enable nested layouts.
57 | */
58 | @discardableResult
59 | func subviews(_ subViews: UIView...) -> UIView {
60 | subviews(subViews)
61 | }
62 |
63 | /**
64 | Defines the view hierachy for the view.
65 |
66 | Esentially, this is just a shortcut to `addSubview`
67 | and 'translatesAutoresizingMaskIntoConstraints = false'
68 |
69 |
70 |
71 | ```
72 | class MyView: UIView {
73 |
74 | let email = UITextField()
75 | let password = UITextField()
76 | let login = UIButton()
77 |
78 | convenience init() {
79 | self.init(frame: CGRect.zero)
80 |
81 | subviews {
82 | email
83 | password
84 | login
85 | }
86 | ...
87 |
88 | }
89 | }
90 |
91 | ```
92 |
93 | - Returns: Itself to enable nested layouts.
94 | */
95 | @discardableResult
96 | func subviews(@SubviewsBuilder content: () -> [UIView]) -> UIView {
97 | subviews(content())
98 | }
99 |
100 | /**
101 | Defines the view hierachy for the view.
102 |
103 | Esentially, this is just a shortcut to `addSubview`
104 | and 'translatesAutoresizingMaskIntoConstraints = false'
105 |
106 |
107 |
108 | ```
109 | class MyView: UIView {
110 |
111 | let email = UITextField()
112 | let password = UITextField()
113 | let login = UIButton()
114 |
115 | convenience init() {
116 | self.init(frame: CGRect.zero)
117 |
118 | subviews {
119 | email
120 | password
121 | login
122 | }
123 | ...
124 |
125 | }
126 | }
127 |
128 | ```
129 |
130 | - Returns: Itself to enable nested layouts.
131 | */
132 | @discardableResult
133 | func subviews(@SubviewsBuilder content: () -> UIView) -> UIView {
134 | let subview = content()
135 | subviews(subview)
136 | return self
137 | }
138 |
139 | /**
140 | Defines the view hierachy for the view.
141 |
142 | Esentially, this is just a shortcut to `addSubview`
143 | and 'translatesAutoresizingMaskIntoConstraints = false'
144 |
145 |
146 | ```
147 | class MyView: UIView {
148 |
149 | let email = UITextField()
150 | let password = UITextField()
151 | let login = UIButton()
152 |
153 | convenience init() {
154 | self.init(frame: CGRect.zero)
155 |
156 | sv(
157 | email,
158 | password,
159 | login
160 | )
161 | ...
162 |
163 | }
164 | }
165 |
166 | ```
167 |
168 | - Returns: Itself to enable nested layouts.
169 | */
170 | @objc
171 | @available(*, deprecated, renamed: "subviews")
172 | @discardableResult
173 | func sv(_ subViews: [UIView]) -> UIView {
174 | subviews(subViews)
175 | }
176 |
177 |
178 | @discardableResult
179 | @objc
180 | func subviews(_ subViews: [UIView]) -> UIView {
181 | for sv in subViews {
182 | addSubview(sv)
183 | sv.translatesAutoresizingMaskIntoConstraints = false
184 | }
185 | return self
186 | }
187 | }
188 |
189 | public extension UITableViewCell {
190 |
191 | /**
192 | Defines the view hierachy for the view.
193 |
194 | Esentially, this is just a shortcut to `contentView.addSubview`
195 | and 'translatesAutoresizingMaskIntoConstraints = false'
196 |
197 | ```
198 | class NotificationCell: UITableViewCell {
199 |
200 | var avatar = UIImageView()
201 | var name = UILabel()
202 | var followButton = UIButton()
203 |
204 | required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) }
205 | override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
206 | super.init(style: style, reuseIdentifier: reuseIdentifier) {
207 |
208 | sv(
209 | avatar,
210 | name,
211 | followButton
212 | )
213 | ...
214 |
215 | }
216 | }
217 | ```
218 |
219 | - Returns: Itself to enable nested layouts.
220 | */
221 | @available(*, deprecated, renamed: "subviews")
222 | @discardableResult
223 | override func sv(_ subViews: [UIView]) -> UIView {
224 | contentView.subviews(subViews)
225 | }
226 |
227 | /**
228 | Defines the view hierachy for the view.
229 |
230 | Esentially, this is just a shortcut to `contentView.addSubview`
231 | and 'translatesAutoresizingMaskIntoConstraints = false'
232 |
233 | ```
234 | class NotificationCell: UITableViewCell {
235 |
236 | var avatar = UIImageView()
237 | var name = UILabel()
238 | var followButton = UIButton()
239 |
240 | required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) }
241 | override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
242 | super.init(style: style, reuseIdentifier: reuseIdentifier) {
243 |
244 | subviews(
245 | avatar,
246 | name,
247 | followButton
248 | )
249 | ...
250 |
251 | }
252 | }
253 | ```
254 |
255 | - Returns: Itself to enable nested layouts.
256 | */
257 | @discardableResult
258 | override func subviews(_ subViews: [UIView]) -> UIView {
259 | contentView.subviews(subViews)
260 | }
261 | }
262 |
263 | public extension UICollectionViewCell {
264 | /**
265 | Defines the view hierachy for the view.
266 |
267 | Esentially, this is just a shortcut to `contentView.addSubview`
268 | and 'translatesAutoresizingMaskIntoConstraints = false'
269 |
270 | ```
271 | class PhotoCollectionViewCell: UICollectionViewCell {
272 |
273 | var avatar = UIImageView()
274 | var name = UILabel()
275 | var followButton = UIButton()
276 |
277 |
278 | required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") }
279 | override init(frame: CGRect) {
280 | super.init(frame: frame)
281 |
282 | subviews(
283 | avatar,
284 | name,
285 | followButton
286 | )
287 | ...
288 |
289 | }
290 | }
291 | ```
292 |
293 | - Returns: Itself to enable nested layouts.
294 | */
295 | @available(*, deprecated, renamed: "subviews")
296 | @discardableResult
297 | override func sv(_ subViews: [UIView]) -> UIView {
298 | contentView.subviews(subViews)
299 | }
300 |
301 | @discardableResult
302 | override func subviews(_ subViews: [UIView]) -> UIView {
303 | contentView.subviews(subViews)
304 | }
305 | }
306 |
307 |
308 | public extension UIStackView {
309 |
310 | @discardableResult
311 | func arrangedSubviews(@SubviewsBuilder content: () -> [UIView]) -> UIView {
312 | arrangedSubviews(content())
313 | }
314 |
315 | @discardableResult
316 | func arrangedSubviews(@SubviewsBuilder content: () -> UIView) -> UIView {
317 | arrangedSubviews([content()])
318 | }
319 |
320 | @discardableResult
321 | private func arrangedSubviews(_ subViews: UIView...) -> UIView {
322 | arrangedSubviews(subViews)
323 | }
324 |
325 | @discardableResult
326 | func arrangedSubviews(_ subViews: [UIView]) -> UIView {
327 | subViews.forEach { addArrangedSubview($0) }
328 | return self
329 | }
330 | }
331 |
332 | #endif
333 |
--------------------------------------------------------------------------------
/EasyListViewExample/Vendor/Stevia/Stevia+LayoutAnchors.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Stevia+LayoutAnchors.swift
3 | // Stevia
4 | //
5 | // Created by Sacha DSO on 09/10/2017.
6 | // Copyright © 2017 Sacha Durand Saint Omer. All rights reserved.
7 | //
8 |
9 | #if canImport(UIKit)
10 | import UIKit
11 |
12 | @available(iOS 9.0, *)
13 | public struct SteviaLayoutYAxisAnchor {
14 | let anchor: NSLayoutYAxisAnchor
15 | let constant: Double
16 |
17 | init(anchor: NSLayoutYAxisAnchor, constant: Double = 0) {
18 | self.anchor = anchor
19 | self.constant = constant
20 | }
21 | }
22 |
23 | @available(iOS 9.0, *)
24 | public struct SteviaLayoutXAxisAnchor {
25 | let anchor: NSLayoutXAxisAnchor
26 | let constant: Double
27 |
28 | init(anchor: NSLayoutXAxisAnchor, constant: Double = 0) {
29 | self.anchor = anchor
30 | self.constant = constant
31 | }
32 | }
33 |
34 | @available(iOS 9.0, *)
35 | public extension UILayoutGuide {
36 |
37 | var Top: SteviaLayoutYAxisAnchor {
38 | return SteviaLayoutYAxisAnchor(anchor: topAnchor)
39 | }
40 |
41 | var Bottom: SteviaLayoutYAxisAnchor {
42 | return SteviaLayoutYAxisAnchor(anchor: bottomAnchor)
43 | }
44 |
45 | var Left: SteviaLayoutXAxisAnchor {
46 | return SteviaLayoutXAxisAnchor(anchor: leftAnchor)
47 | }
48 |
49 | var Right: SteviaLayoutXAxisAnchor {
50 | return SteviaLayoutXAxisAnchor(anchor: rightAnchor)
51 | }
52 |
53 | var Leading: SteviaLayoutXAxisAnchor {
54 | return SteviaLayoutXAxisAnchor(anchor: leadingAnchor)
55 | }
56 |
57 | var Trailing: SteviaLayoutXAxisAnchor {
58 | return SteviaLayoutXAxisAnchor(anchor: trailingAnchor)
59 | }
60 |
61 | var CenterX: SteviaLayoutXAxisAnchor {
62 | return SteviaLayoutXAxisAnchor(anchor: centerXAnchor)
63 | }
64 |
65 | var CenterY: SteviaLayoutYAxisAnchor {
66 | return SteviaLayoutYAxisAnchor(anchor: centerYAnchor)
67 | }
68 | }
69 |
70 | @available(iOS 9.0, *)
71 | @discardableResult
72 | public func == (left: SteviaAttribute, right: SteviaLayoutYAxisAnchor) -> NSLayoutConstraint {
73 |
74 | var constraint = NSLayoutConstraint()
75 |
76 | if left.attribute == .top {
77 | constraint = left.view.topAnchor.constraint(equalTo: right.anchor, constant: CGFloat(right.constant))
78 | }
79 |
80 | if left.attribute == .bottom {
81 | constraint = left.view.bottomAnchor.constraint(equalTo: right.anchor, constant: CGFloat(right.constant))
82 | }
83 |
84 | if left.attribute == .centerY {
85 | constraint = left.view.centerYAnchor.constraint(equalTo: right.anchor, constant: CGFloat(right.constant))
86 | }
87 |
88 | constraint.isActive = true
89 | return constraint
90 | }
91 |
92 | @available(iOS 9.0, *)
93 | @discardableResult
94 | public func == (left: SteviaAttribute, right: SteviaLayoutXAxisAnchor) -> NSLayoutConstraint {
95 |
96 | var constraint = NSLayoutConstraint()
97 |
98 | if left.attribute == .left {
99 | constraint = left.view.leftAnchor.constraint(equalTo: right.anchor, constant: CGFloat(right.constant))
100 | }
101 |
102 | if left.attribute == .right {
103 | constraint = left.view.rightAnchor.constraint(equalTo: right.anchor, constant: CGFloat(right.constant))
104 | }
105 |
106 | if left.attribute == .leading {
107 | constraint = left.view.leadingAnchor.constraint(equalTo: right.anchor, constant: CGFloat(right.constant))
108 | }
109 |
110 | if left.attribute == .trailing {
111 | constraint = left.view.trailingAnchor.constraint(equalTo: right.anchor, constant: CGFloat(right.constant))
112 | }
113 |
114 | if left.attribute == .centerX {
115 | constraint = left.view.centerXAnchor.constraint(equalTo: right.anchor, constant: CGFloat(right.constant))
116 | }
117 |
118 | constraint.isActive = true
119 | return constraint
120 | }
121 |
122 | // SteviaLayoutYAxisAnchor
123 |
124 | @available(iOS 9.0, *)
125 | @discardableResult
126 | public func + (left: SteviaLayoutYAxisAnchor, right: Double) -> SteviaLayoutYAxisAnchor {
127 | return SteviaLayoutYAxisAnchor(anchor: left.anchor, constant: right)
128 | }
129 |
130 | @available(iOS 9.0, *)
131 | @discardableResult
132 | public func - (left: SteviaLayoutYAxisAnchor, right: Double) -> SteviaLayoutYAxisAnchor {
133 | return SteviaLayoutYAxisAnchor(anchor: left.anchor, constant: -right)
134 | }
135 |
136 | @available(iOS 9.0, *)
137 | @discardableResult
138 | public func + (left: SteviaLayoutXAxisAnchor, right: Double) -> SteviaLayoutXAxisAnchor {
139 | return SteviaLayoutXAxisAnchor(anchor: left.anchor, constant: right)
140 | }
141 |
142 | @available(iOS 9.0, *)
143 | @discardableResult
144 | public func - (left: SteviaLayoutXAxisAnchor, right: Double) -> SteviaLayoutXAxisAnchor {
145 | return SteviaLayoutXAxisAnchor(anchor: left.anchor, constant: -right)
146 | }
147 |
148 | // UILayoutSupport
149 |
150 | @available(iOS 9.0, *)
151 | public extension UILayoutSupport {
152 |
153 | var Top: SteviaLayoutYAxisAnchor {
154 | return SteviaLayoutYAxisAnchor(anchor: topAnchor)
155 | }
156 |
157 | var Bottom: SteviaLayoutYAxisAnchor {
158 | return SteviaLayoutYAxisAnchor(anchor: bottomAnchor)
159 | }
160 | }
161 | #endif
162 |
--------------------------------------------------------------------------------
/EasyListViewExample/Vendor/Stevia/Stevia+Notifications.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Stevia+Notifications.swift
3 | // LoginStevia
4 | //
5 | // Created by Sacha Durand Saint Omer on 12/10/15.
6 | // Copyright © 2015 Sacha Durand Saint Omer. All rights reserved.
7 | //
8 |
9 | #if canImport(UIKit)
10 | import UIKit
11 |
12 | public extension NSObject {
13 |
14 | func on(_ event: String, _ callback:@escaping () -> Void) {
15 | _ = NotificationCenter.default.addObserver(forName: NSNotification.Name(rawValue: event),
16 | object: nil,
17 | queue: nil) { _ in
18 | callback()
19 | }
20 | }
21 | }
22 | #endif
23 |
--------------------------------------------------------------------------------
/EasyListViewExample/Vendor/Stevia/Stevia+Operators.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Stevia+Operators.swift
3 | // Stevia
4 | //
5 | // Created by Sacha Durand Saint Omer on 10/02/16.
6 | // Copyright © 2016 Sacha Durand Saint Omer. All rights reserved.
7 | //
8 |
9 | #if canImport(UIKit)
10 | import UIKit
11 |
12 | prefix operator |
13 | @discardableResult
14 | public prefix func | (p: UIView) -> UIView {
15 | p.leading(0)
16 | }
17 |
18 | postfix operator |
19 | @discardableResult
20 | public postfix func | (p: UIView) -> UIView {
21 | p.trailing(0)
22 | }
23 |
24 | infix operator ~ : HeightPrecedence
25 |
26 | precedencegroup HeightPrecedence {
27 | lowerThan: AdditionPrecedence
28 | }
29 |
30 | @discardableResult
31 | public func ~ (left: UIView, right: Double) -> UIView {
32 | left.height(right)
33 | }
34 |
35 | @discardableResult
36 | public func ~ (left: UIView, right: CGFloat) -> UIView {
37 | left ~ Double(right)
38 | }
39 |
40 | @discardableResult
41 | public func ~ (left: UIView, right: Int) -> UIView {
42 | left ~ Double(right)
43 | }
44 |
45 | @discardableResult
46 | public func ~ (left: UIView, right: SteviaPercentage) -> UIView {
47 | left.height(right)
48 | }
49 |
50 | @discardableResult
51 | public func ~ (left: UIView, right: SteviaFlexibleMargin) -> UIView {
52 | left.height(right)
53 | }
54 |
55 | @discardableResult
56 | public func ~ (left: [UIView], right: Double) -> [UIView] {
57 | for l in left { l.height(right) }
58 | return left
59 | }
60 |
61 | @discardableResult
62 | public func ~ (left: [UIView], right: CGFloat) -> [UIView] {
63 | left ~ Double(right)
64 | }
65 |
66 | @discardableResult
67 | public func ~ (left: [UIView], right: Int) -> [UIView] {
68 | left ~ Double(right)
69 | }
70 |
71 | @discardableResult
72 | public func ~ (left: [UIView], right: SteviaFlexibleMargin) -> [UIView] {
73 | for l in left { l.height(right) }
74 | return left
75 | }
76 |
77 | prefix operator |-
78 | @discardableResult
79 | public prefix func |- (p: Double) -> SideConstraint {
80 | var s = SideConstraint()
81 | s.constant = p
82 | return s
83 | }
84 |
85 | @discardableResult
86 | public prefix func |- (p: CGFloat) -> SideConstraint {
87 | |-Double(p)
88 | }
89 |
90 | @discardableResult
91 | public prefix func |- (p: Int) -> SideConstraint {
92 | |-Double(p)
93 | }
94 |
95 | @discardableResult
96 | public prefix func |- (v: UIView) -> UIView {
97 | v.leading(8)
98 | }
99 |
100 | postfix operator -|
101 | @discardableResult
102 | public postfix func -| (p: Double) -> SideConstraint {
103 | var s = SideConstraint()
104 | s.constant = p
105 | return s
106 | }
107 |
108 | @discardableResult
109 | public postfix func -| (p: CGFloat) -> SideConstraint {
110 | Double(p)-|
111 | }
112 |
113 | @discardableResult
114 | public postfix func -| (p: Int) -> SideConstraint {
115 | Double(p)-|
116 | }
117 |
118 | @discardableResult
119 | public postfix func -| (v: UIView) -> UIView {
120 | v.trailing(8)
121 | }
122 |
123 | public struct SideConstraint {
124 | var constant: Double!
125 | }
126 |
127 | public struct PartialConstraint {
128 | var view1: UIView!
129 | var constant: Double!
130 | var views: [UIView]?
131 | }
132 |
133 | @discardableResult
134 | public func - (left: UIView, right: Double) -> PartialConstraint {
135 | var p = PartialConstraint()
136 | p.view1 = left
137 | p.constant = right
138 | return p
139 | }
140 |
141 | @discardableResult
142 | public func - (left: UIView, right: CGFloat) -> PartialConstraint {
143 | left-Double(right)
144 | }
145 |
146 | @discardableResult
147 | public func - (left: UIView, right: Int) -> PartialConstraint {
148 | left-Double(right)
149 | }
150 |
151 | // Side Constraints
152 |
153 | @discardableResult
154 | public func - (left: SideConstraint, right: UIView) -> UIView {
155 | if let spv = right.superview {
156 | let c = constraint(item: right, attribute: .leading,
157 | toItem: spv, attribute: .leading,
158 | constant: left.constant)
159 | spv.addConstraint(c)
160 | }
161 | return right
162 | }
163 |
164 | @discardableResult
165 | public func - (left: [UIView], right: SideConstraint) -> [UIView] {
166 | let lastView = left[left.count-1]
167 | if let spv = lastView.superview {
168 | let c = constraint(item: lastView, attribute: .trailing,
169 | toItem: spv, attribute: .trailing,
170 | constant: -right.constant)
171 | spv.addConstraint(c)
172 | }
173 | return left
174 | }
175 |
176 | @discardableResult
177 | public func - (left: UIView, right: SideConstraint) -> UIView {
178 | if let spv = left.superview {
179 | let c = constraint(item: left, attribute: .trailing,
180 | toItem: spv, attribute: .trailing,
181 | constant: -right.constant)
182 | spv.addConstraint(c)
183 | }
184 | return left
185 | }
186 |
187 | @discardableResult
188 | public func - (left: PartialConstraint, right: UIView) -> [UIView] {
189 | if let views = left.views {
190 | if let spv = right.superview {
191 | let lastView = views[views.count-1]
192 | let c = constraint(item: lastView, attribute: .trailing,
193 | toItem: right, attribute: .leading,
194 | constant: -left.constant)
195 | spv.addConstraint(c)
196 | }
197 |
198 | return views + [right]
199 | } else {
200 | // were at the end?? nooope?/?
201 | if let spv = right.superview {
202 | let c = constraint(item: left.view1, attribute: .trailing,
203 | toItem: right, attribute: .leading,
204 | constant: -left.constant)
205 | spv.addConstraint(c)
206 | }
207 | return [left.view1, right]
208 | }
209 | }
210 |
211 | @discardableResult
212 | public func - (left: UIView, right: UIView) -> [UIView] {
213 | if let spv = left.superview {
214 | let c = constraint(item: right, attribute: .leading,
215 | toItem: left, attribute: .trailing,
216 | constant: 8)
217 | spv.addConstraint(c)
218 | }
219 | return [left, right]
220 | }
221 |
222 | @discardableResult
223 | public func - (left: [UIView], right: Double) -> PartialConstraint {
224 | var p = PartialConstraint()
225 | p.constant = right
226 | p.views = left
227 | return p
228 | }
229 |
230 | @discardableResult
231 | public func - (left: [UIView], right: CGFloat) -> PartialConstraint {
232 | left-Double(right)
233 | }
234 |
235 | @discardableResult
236 | public func - (left: [UIView], right: Int) -> PartialConstraint {
237 | left-Double(right)
238 | }
239 |
240 |
241 | @discardableResult
242 | public func - (left: [UIView], right: UIView) -> [UIView] {
243 | let lastView = left[left.count-1]
244 | if let spv = lastView.superview {
245 | let c = constraint(item: lastView, attribute: .trailing,
246 | toItem: right, attribute: .leading,
247 | constant: -8)
248 | spv.addConstraint(c)
249 | }
250 | return left + [right]
251 | }
252 |
253 | //// Test space in Horizointal layout ""
254 | public struct Space {
255 | var previousViews: [UIView]!
256 | }
257 |
258 | @discardableResult
259 | public func - (left: UIView, right: String) -> Space {
260 | Space(previousViews: [left])
261 | }
262 |
263 | @discardableResult
264 | public func - (left: [UIView], right: String) -> Space {
265 | Space(previousViews: left)
266 | }
267 |
268 | @discardableResult
269 | public func - (left: Space, right: UIView) -> [UIView] {
270 | var va = left.previousViews
271 | va?.append(right)
272 | return va!
273 | }
274 | #endif
275 |
--------------------------------------------------------------------------------
/EasyListViewExample/Vendor/Stevia/Stevia+Percentage.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Stevia+Percentage.swift
3 | // Stevia
4 | //
5 | // Created by Sacha Durand Saint Omer on 21/01/2017.
6 | // Copyright © 2017 Sacha Durand Saint Omer. All rights reserved.
7 | //
8 |
9 | #if canImport(UIKit)
10 | import UIKit
11 |
12 | public struct SteviaPercentage {
13 | let value: Double
14 | }
15 |
16 | postfix operator %
17 | public postfix func % (v: Double) -> SteviaPercentage {
18 | SteviaPercentage(value: Double(v))
19 | }
20 |
21 | public postfix func % (v: CGFloat) -> SteviaPercentage {
22 | Double(v)%
23 | }
24 |
25 | public postfix func % (v: Int) -> SteviaPercentage {
26 | Double(v)%
27 | }
28 |
29 | public extension UIView {
30 |
31 | /**
32 | Adds an Autolayout constraint for sizing the view.
33 |
34 | ```
35 | image.size(100)
36 | image.size(100%)
37 |
38 | // is equivalent to
39 |
40 | image.width(100).height(100)
41 | ```
42 |
43 | - Returns: Itself, enabling chaining,
44 |
45 | */
46 | @discardableResult
47 | func size(_ p: SteviaPercentage) -> Self {
48 | width(p)
49 | height(p)
50 | return self
51 | }
52 |
53 | /**
54 | Adds an Autolayout constraint for setting the view's width.
55 |
56 | ```
57 | image.width(100)
58 | image.width(<=100)
59 | image.width(>=100)
60 | image.width(100%)
61 | ```
62 |
63 | - Returns: Itself, enabling chaining,
64 |
65 | */
66 | @discardableResult
67 | func width(_ p: SteviaPercentage) -> Self {
68 | if let spv = superview {
69 | Width == p.value % spv.Width
70 | }
71 | return self
72 | }
73 |
74 | /**
75 | Adds an Autolayout constraint for setting the view's height.
76 |
77 | ```
78 | image.height(100)
79 |
80 | // is equivalent to
81 |
82 | image ~ 100
83 |
84 | // Flexible margins
85 | image.height(<=100)
86 | image.height(>=100)
87 | image.height(100%)
88 | ```
89 |
90 | - Returns: Itself, enabling chaining,
91 |
92 | */
93 | @discardableResult
94 | func height(_ p: SteviaPercentage) -> Self {
95 | if let spv = superview {
96 | Height == p.value % spv.Height
97 | }
98 | return self
99 | }
100 |
101 | /** Sets the top margin for a view.
102 |
103 | Example Usage :
104 |
105 | label.top(20)
106 | label.top(<=20)
107 | label.top(>=20)
108 | label.top(20%)
109 |
110 | - Returns: Itself for chaining purposes
111 | */
112 | @discardableResult
113 | func top(_ p: SteviaPercentage) -> Self {
114 | if let spv = superview {
115 | Top == p.value % spv.Bottom
116 | }
117 | return self
118 | }
119 |
120 | /** Sets the left margin for a view.
121 |
122 | Example Usage :
123 |
124 | label.left(20)
125 | label.left(<=20)
126 | label.left(>=20)
127 | label.left(20%)
128 |
129 | - Returns: Itself for chaining purposes
130 | */
131 | @discardableResult
132 | func left(_ p: SteviaPercentage) -> Self {
133 | if let spv = superview {
134 | Left == p.value % spv.Right
135 | }
136 | return self
137 | }
138 |
139 | /** Sets the right margin for a view.
140 |
141 | Example Usage :
142 |
143 | label.right(20)
144 | label.right(<=20)
145 | label.right(>=20)
146 | label.right(20%)
147 |
148 | - Returns: Itself for chaining purposes
149 | */
150 | @discardableResult
151 | func right(_ p: SteviaPercentage) -> Self {
152 | if let spv = superview {
153 | if p.value == 100 {
154 | Right == spv.Left
155 | } else {
156 | Right == (100 - p.value) % spv.Right
157 | }
158 | }
159 | return self
160 | }
161 |
162 | /** Sets the bottom margin for a view.
163 |
164 | Example Usage :
165 |
166 | label.bottom(20)
167 | label.bottom(<=20)
168 | label.bottom(>=20)
169 | label.bottom(20%)
170 |
171 | - Returns: Itself for chaining purposes
172 | */
173 | @discardableResult
174 | func bottom(_ p: SteviaPercentage) -> Self {
175 | if let spv = superview {
176 | if p.value == 100 {
177 | Bottom == spv.Top
178 | } else {
179 | Bottom == (100 - p.value) % spv.Bottom
180 | }
181 | }
182 | return self
183 | }
184 |
185 | /** Sets the leading margin for a view.
186 |
187 | Example Usage :
188 |
189 | label.leading(20)
190 | label.leading(<=20)
191 | label.leading(>=20)
192 | label.leading(20%)
193 |
194 | - Returns: itself for chaining purposes
195 | */
196 | @discardableResult
197 | func leading(_ p: SteviaPercentage) -> UIView {
198 | // Percent based (multipliers) with leading or trailing attributes
199 | // are not available so we introduce an intermediary layout guide.
200 | // |-[layoutGuide(== x % width)-[view]
201 | // RTL version: [view]-[layoutGuide(== x % width)-|
202 | if let spv = superview {
203 | let lg = UILayoutGuide()
204 | spv.addLayoutGuide(lg)
205 | let constraints = [
206 | lg.widthAnchor.constraint(equalTo: spv.widthAnchor, multiplier: CGFloat(p.value / 100.0)),
207 | lg.leadingAnchor.constraint(equalTo: spv.leadingAnchor),
208 | leadingAnchor.constraint(equalTo: lg.trailingAnchor)
209 | ]
210 | constraints.forEach { $0.isActive = true }
211 | }
212 | return self
213 | }
214 |
215 | /** Sets the trailing margin for a view.
216 |
217 | Example Usage :
218 |
219 | label.trailing(20)
220 | label.trailing(<=20)
221 | label.trailing(>=20)
222 | label.trailing(20%)
223 |
224 | - Returns: itself for chaining purposes
225 | */
226 | @discardableResult
227 | func trailing(_ p: SteviaPercentage) -> UIView {
228 |
229 | // Percent based (multipliers) with leading or trailing attributes
230 | // are not available so we introduce an intermediary layout guide.
231 | // |-[layoutGuide(== x % width)-[view]
232 | // RTL version: [view]-[layoutGuide(== x % width)-|
233 | if let spv = superview {
234 | let lg = UILayoutGuide()
235 | spv.addLayoutGuide(lg)
236 | let constraints = [
237 | lg.widthAnchor.constraint(equalTo: spv.widthAnchor, multiplier: CGFloat(p.value / Double(100))),
238 | lg.trailingAnchor.constraint(equalTo: spv.trailingAnchor),
239 | trailingAnchor.constraint(equalTo: lg.leadingAnchor)
240 | ]
241 | constraints.forEach { $0.isActive = true }
242 | }
243 | return self
244 | }
245 | }
246 | #endif
247 |
--------------------------------------------------------------------------------
/EasyListViewExample/Vendor/Stevia/Stevia+Stacks.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Stevia+Stacks.swift
3 | // Stevia
4 | //
5 | // Created by Sacha Durand Saint Omer on 10/02/16.
6 | // Copyright © 2016 Sacha Durand Saint Omer. All rights reserved.
7 | //
8 |
9 | #if canImport(UIKit)
10 | import UIKit
11 |
12 | //enum SteviaLayoutItemType {
13 | //
14 | //}
15 |
16 | public protocol SteviaLayoutItem {
17 | var any: Any {get}
18 | }
19 |
20 | extension SteviaLayoutItem {
21 | public var any: Any { self }
22 | }
23 | extension UIView: SteviaLayoutItem {}
24 | extension Int: SteviaLayoutItem {}
25 | extension Double: SteviaLayoutItem {}
26 | extension CGFloat: SteviaLayoutItem {}
27 | extension String: SteviaLayoutItem {}
28 |
29 | extension FlexibleSpace: SteviaLayoutItem {}
30 | extension SteviaFlexibleMargin: SteviaLayoutItem {}
31 | extension SteviaPercentage: SteviaLayoutItem {}
32 | extension Array: SteviaLayoutItem where Element: UIView {}
33 |
34 | public struct FlexibleSpace {
35 | public init() {}
36 | }
37 |
38 | @_functionBuilder public struct SteviaLayoutBuilder {
39 | public static func buildBlock(_ content: SteviaLayoutItem...) -> [SteviaLayoutItem] {
40 | return content
41 | }
42 | }
43 |
44 | public extension UIView {
45 | @discardableResult
46 | func layout(@SteviaLayoutBuilder content: () -> [SteviaLayoutItem]) -> UIView {
47 | let subviews = content()
48 | let anys = subviews.map { $0.any }
49 | layout(anys)
50 | return self
51 | }
52 | }
53 |
54 | public extension UIView {
55 |
56 | /**
57 |
58 | Lays out the views on both axis.
59 |
60 | Note that this is not needed for Horizontal only layouts.
61 |
62 | `layout` is primarily for laying out views vertically but horizontal statements
63 | are supported, making it perfect for describing a layout in one single statement.
64 |
65 | ```
66 | layout(
67 | 100,
68 | |-email-| ~ 80,
69 | 8,
70 | |-password-forgot-| ~ 80,
71 | >=20,
72 | |login| ~ 80,
73 | 0
74 | )
75 | ```
76 | */
77 | // @available(*, deprecated, message: "Use Layout { } function builder instead.")
78 | @discardableResult
79 | func layout(_ objects: Any...) -> [UIView] {
80 | return layout(objects)
81 | }
82 |
83 | @discardableResult
84 | func layout(_ objects: [Any]) -> [UIView] {
85 | var previousMargin: Double?
86 | var previousFlexibleMargin: SteviaFlexibleMargin?
87 | var previousPercentMargin: SteviaPercentage?
88 |
89 | for (i, o) in objects.enumerated() {
90 |
91 | func viewLogic(_ v: UIView) {
92 | if let pm = previousMargin {
93 | if i == 1 {
94 | v.top(pm) // only if first view
95 | } else {
96 | if let vx = objects[i-2] as? UIView {
97 | vx.stackV(m: pm, v: v)
98 | } else if let va = objects[i-2] as? [UIView] {
99 | va.first!.stackV(m: pm, v: v)
100 | }
101 | }
102 | previousMargin = nil
103 | } else if let pfm = previousFlexibleMargin {
104 | if i == 1 {
105 | v.top(pfm) // only if first view
106 | } else {
107 | if let vx = objects[i-2] as? UIView {
108 | addConstraint(
109 | item: v, attribute: .top,
110 | relatedBy: pfm.relation,
111 | toItem: vx, attribute: .bottom,
112 | multiplier: 1, constant: pfm.points
113 | )
114 | } else if let va = objects[i-2] as? [UIView] {
115 | addConstraint(
116 | item: v, attribute: .top,
117 | relatedBy: pfm.relation,
118 | toItem: va.first!, attribute: .bottom,
119 | multiplier: 1, constant: pfm.points
120 | )
121 | }
122 | }
123 | previousFlexibleMargin = nil
124 | } else if let ppm = previousPercentMargin {
125 | if i == 1 {
126 | v.top(ppm) // only if first view
127 | } else {
128 | if let vx = objects[i-2] as? UIView {
129 | // Add layout guide to suport %-based spaces.
130 | let percent = ppm.value / 100
131 |
132 | let lg = UILayoutGuide()
133 | addLayoutGuide(lg)
134 | NSLayoutConstraint.activate([
135 | lg.topAnchor.constraint(equalTo: vx.bottomAnchor),
136 | lg.heightAnchor.constraint(equalTo: heightAnchor, multiplier: CGFloat(percent)),
137 | v.topAnchor.constraint(equalTo: lg.bottomAnchor)
138 | ])
139 | }
140 | }
141 | previousPercentMargin = nil
142 | } else {
143 | tryStackViewVerticallyWithPreviousView(v, index: i, objects: objects)
144 | }
145 | }
146 |
147 | switch o {
148 | case let v as UIView:
149 | viewLogic(v)
150 | case is Int, is Double, is CGFloat:
151 | let m = doubleMarginFromObject(o)
152 | previousMargin = m // Store margin for next pass
153 | if i != 0 && i == (objects.count - 1) {
154 | //Last Margin, Bottom
155 | if let previousView = objects[i-1] as? UIView {
156 | previousView.bottom(m)
157 | } else if let va = objects[i-1] as? [UIView] {
158 | va.first!.bottom(m)
159 | }
160 | }
161 | case let fm as SteviaFlexibleMargin:
162 | previousFlexibleMargin = fm // Store margin for next pass
163 | if i != 0 && i == (objects.count - 1) {
164 | //Last Margin, Bottom
165 | if let previousView = objects[i-1] as? UIView {
166 | previousView.bottom(fm)
167 | } else if let va = objects[i-1] as? [UIView] {
168 | va.first!.bottom(fm)
169 | }
170 | }
171 | case let pm as SteviaPercentage:
172 | previousPercentMargin = pm // Store margin for next pass
173 | if i != 0 && i == (objects.count - 1) {
174 | //Last Margin, Bottom
175 | if let previousView = objects[i-1] as? UIView {
176 | previousView.bottom(pm)
177 | } else if let va = objects[i-1] as? [UIView] {
178 | va.first!.bottom(pm)
179 | }
180 | }
181 | case _ as String:() //Do nothin' !
182 | case let a as [UIView]:
183 | align(horizontally: a)
184 | let v = a.first!
185 | viewLogic(v)
186 | default: ()
187 | }
188 | }
189 | return objects.map {$0 as? UIView }.compactMap {$0}
190 | }
191 |
192 | fileprivate func doubleMarginFromObject(_ o: Any) -> Double {
193 | var m: Double = 0
194 | if let i = o as? Int {
195 | m = Double(i)
196 | } else if let d = o as? Double {
197 | m = d
198 | } else if let cg = o as? CGFloat {
199 | m = Double(cg)
200 | }
201 | return m
202 | }
203 |
204 | fileprivate func tryStackViewVerticallyWithPreviousView(_ view: UIView,
205 | index: Int, objects: [Any]) {
206 | if let pv = previousViewFromIndex(index, objects: objects) {
207 | pv.stackV(v: view)
208 | }
209 | }
210 |
211 | fileprivate func previousViewFromIndex(_ index: Int, objects: [Any]) -> UIView? {
212 | if index != 0 {
213 | if let previousView = objects[index-1] as? UIView {
214 | return previousView
215 | }
216 | }
217 | return nil
218 | }
219 |
220 | @discardableResult
221 | fileprivate func stackV(m points: Double = 0, v: UIView) -> UIView {
222 | return stack(.vertical, points: points, v: v)
223 | }
224 |
225 | fileprivate func stack(_ axis: NSLayoutConstraint.Axis,
226 | points: Double = 0, v: UIView) -> UIView {
227 | let a: NSLayoutConstraint.Attribute = axis == .vertical ? .top : .left
228 | let b: NSLayoutConstraint.Attribute = axis == .vertical ? .bottom : .right
229 | if let spv = superview {
230 | let c = constraint(item: v, attribute: a, toItem: self, attribute: b, constant: points)
231 | spv.addConstraint(c)
232 | }
233 | return v
234 | }
235 | }
236 | #endif
237 |
--------------------------------------------------------------------------------
/EasyListViewExample/Vendor/Stevia/Stevia+Style.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Stevia+Style.swift
3 | // LoginStevia
4 | //
5 | // Created by Sacha Durand Saint Omer on 01/10/15.
6 | // Copyright © 2015 Sacha Durand Saint Omer. All rights reserved.
7 | //
8 |
9 | #if canImport(UIKit)
10 | import UIKit
11 |
12 | public extension UIAppearance {
13 |
14 | /** Applies a styling block on an element.
15 |
16 | Example Usage:
17 |
18 | ```
19 | button.style { b in
20 | b.A = X
21 | b.B = Y
22 | b.C = Z
23 | }
24 | ```
25 |
26 | Handy for reusing styles :
27 | ```
28 | button.style(buttonStyle)
29 |
30 | // later
31 | func buttonStyle(b: UIButton) {
32 | ..styling code
33 | }
34 | ```
35 |
36 | - Returns: Itself for chaining purposes
37 |
38 | */
39 | @discardableResult
40 | func style(_ styleClosure: (Self) -> Void) -> Self {
41 | styleClosure(self)
42 | return self
43 | }
44 | }
45 | #endif
46 |
--------------------------------------------------------------------------------
/Kapture1.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moliya/EasyListView/de589ce71c695759034df8f28d8b7b4168b4168e/Kapture1.gif
--------------------------------------------------------------------------------
/Kapture2.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moliya/EasyListView/de589ce71c695759034df8f28d8b7b4168b4168e/Kapture2.gif
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 moliya
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.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version:5.0
2 | // The swift-tools-version declares the minimum version of Swift required to build this package.
3 |
4 | import PackageDescription
5 |
6 | let package = Package(
7 | name: "EasyListView",
8 | platforms: [
9 | .iOS(.v9)
10 | ],
11 | products: [
12 | .library(name: "EasyListView", targets: ["EasyListView"])
13 | ],
14 | dependencies: [
15 | .package(url: "https://github.com/moliya/EasyCompatible.git", from: "1.0.0")
16 | ],
17 | targets: [
18 | .target(name: "EasyListView", dependencies: ["EasyCompatible"], path: "Sources")
19 | ],
20 | swiftLanguageVersions: [.v5]
21 | )
22 |
--------------------------------------------------------------------------------
/Podfile:
--------------------------------------------------------------------------------
1 | source 'https://cdn.cocoapods.org/'
2 | use_frameworks!
3 | platform :ios, '9.0'
4 |
5 | target 'EasyListViewExample' do
6 | pod 'EasyListView', :path => './'
7 | end
8 |
--------------------------------------------------------------------------------
/Podfile.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - EasyCompatible (1.0)
3 | - EasyListView (1.3.0):
4 | - EasyCompatible
5 |
6 | DEPENDENCIES:
7 | - EasyListView (from `./`)
8 |
9 | SPEC REPOS:
10 | trunk:
11 | - EasyCompatible
12 |
13 | EXTERNAL SOURCES:
14 | EasyListView:
15 | :path: "./"
16 |
17 | SPEC CHECKSUMS:
18 | EasyCompatible: 571e10cf69d018b820f65cd6b99f76573313db6e
19 | EasyListView: ac1b3dcd19ef3fb5b2ea48b74b91a8171cc723e8
20 |
21 | PODFILE CHECKSUM: 85d9c6237193f3c7e89929aa9b3c1282aad65d38
22 |
23 | COCOAPODS: 1.11.3
24 |
--------------------------------------------------------------------------------
/Pods/EasyCompatible/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 moliya
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 |
--------------------------------------------------------------------------------
/Pods/EasyCompatible/README.md:
--------------------------------------------------------------------------------
1 | # EasyCompatible
2 | 一个基础依赖
3 |
--------------------------------------------------------------------------------
/Pods/EasyCompatible/Sources/EasyCompatible.swift:
--------------------------------------------------------------------------------
1 | //
2 | // EasyCompatible.swift
3 | // EasyCompatible
4 | //
5 | // Created by carefree on 2022/3/25.
6 | // Copyright © 2019 Carefree. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | // MARK: - 命名空间
12 | public struct EasyExtension {
13 | public let base: Base
14 | public init(_ base: Base) {
15 | self.base = base
16 | }
17 | }
18 |
19 | public protocol EasyCompatible: AnyObject { }
20 |
21 | public protocol EasyCompatibleValue {}
22 |
23 | extension EasyCompatible {
24 | public var easy: EasyExtension {
25 | get { return EasyExtension(self) }
26 | set { }
27 | }
28 | }
29 |
30 | extension EasyCompatibleValue {
31 | public var easy: EasyExtension {
32 | get { return EasyExtension(self) }
33 | set { }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/Pods/Local Podspecs/EasyListView.podspec.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "EasyListView",
3 | "version": "1.3.0",
4 | "summary": "快速搭建静态及可重用列表",
5 | "homepage": "https://github.com/moliya/EasyListView",
6 | "license": "MIT",
7 | "authors": {
8 | "Carefree": "946715806@qq.com"
9 | },
10 | "source": {
11 | "git": "https://github.com/moliya/EasyListView.git",
12 | "tag": "1.3.0"
13 | },
14 | "requires_arc": true,
15 | "platforms": {
16 | "ios": "9.0"
17 | },
18 | "swift_versions": "5.0",
19 | "dependencies": {
20 | "EasyCompatible": [
21 |
22 | ]
23 | },
24 | "source_files": "Sources/**/*",
25 | "swift_version": "5.0"
26 | }
27 |
--------------------------------------------------------------------------------
/Pods/Manifest.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - EasyCompatible (1.0)
3 | - EasyListView (1.3.0):
4 | - EasyCompatible
5 |
6 | DEPENDENCIES:
7 | - EasyListView (from `./`)
8 |
9 | SPEC REPOS:
10 | trunk:
11 | - EasyCompatible
12 |
13 | EXTERNAL SOURCES:
14 | EasyListView:
15 | :path: "./"
16 |
17 | SPEC CHECKSUMS:
18 | EasyCompatible: 571e10cf69d018b820f65cd6b99f76573313db6e
19 | EasyListView: ac1b3dcd19ef3fb5b2ea48b74b91a8171cc723e8
20 |
21 | PODFILE CHECKSUM: 85d9c6237193f3c7e89929aa9b3c1282aad65d38
22 |
23 | COCOAPODS: 1.11.3
24 |
--------------------------------------------------------------------------------
/Pods/Target Support Files/EasyCompatible/EasyCompatible-Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | ${EXECUTABLE_NAME}
9 | CFBundleIdentifier
10 | ${PRODUCT_BUNDLE_IDENTIFIER}
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | ${PRODUCT_NAME}
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | ${CURRENT_PROJECT_VERSION}
23 | NSPrincipalClass
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/Pods/Target Support Files/EasyCompatible/EasyCompatible-dummy.m:
--------------------------------------------------------------------------------
1 | #import
2 | @interface PodsDummy_EasyCompatible : NSObject
3 | @end
4 | @implementation PodsDummy_EasyCompatible
5 | @end
6 |
--------------------------------------------------------------------------------
/Pods/Target Support Files/EasyCompatible/EasyCompatible-prefix.pch:
--------------------------------------------------------------------------------
1 | #ifdef __OBJC__
2 | #import
3 | #else
4 | #ifndef FOUNDATION_EXPORT
5 | #if defined(__cplusplus)
6 | #define FOUNDATION_EXPORT extern "C"
7 | #else
8 | #define FOUNDATION_EXPORT extern
9 | #endif
10 | #endif
11 | #endif
12 |
13 |
--------------------------------------------------------------------------------
/Pods/Target Support Files/EasyCompatible/EasyCompatible-umbrella.h:
--------------------------------------------------------------------------------
1 | #ifdef __OBJC__
2 | #import
3 | #else
4 | #ifndef FOUNDATION_EXPORT
5 | #if defined(__cplusplus)
6 | #define FOUNDATION_EXPORT extern "C"
7 | #else
8 | #define FOUNDATION_EXPORT extern
9 | #endif
10 | #endif
11 | #endif
12 |
13 |
14 | FOUNDATION_EXPORT double EasyCompatibleVersionNumber;
15 | FOUNDATION_EXPORT const unsigned char EasyCompatibleVersionString[];
16 |
17 |
--------------------------------------------------------------------------------
/Pods/Target Support Files/EasyCompatible/EasyCompatible.debug.xcconfig:
--------------------------------------------------------------------------------
1 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
2 | CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/EasyCompatible
3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
4 | LIBRARY_SEARCH_PATHS = $(inherited) "${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift
5 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
6 | PODS_BUILD_DIR = ${BUILD_DIR}
7 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
8 | PODS_ROOT = ${SRCROOT}
9 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/EasyCompatible
10 | PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates
11 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
12 | SKIP_INSTALL = YES
13 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES
14 |
--------------------------------------------------------------------------------
/Pods/Target Support Files/EasyCompatible/EasyCompatible.modulemap:
--------------------------------------------------------------------------------
1 | framework module EasyCompatible {
2 | umbrella header "EasyCompatible-umbrella.h"
3 |
4 | export *
5 | module * { export * }
6 | }
7 |
--------------------------------------------------------------------------------
/Pods/Target Support Files/EasyCompatible/EasyCompatible.release.xcconfig:
--------------------------------------------------------------------------------
1 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
2 | CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/EasyCompatible
3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
4 | LIBRARY_SEARCH_PATHS = $(inherited) "${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift
5 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
6 | PODS_BUILD_DIR = ${BUILD_DIR}
7 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
8 | PODS_ROOT = ${SRCROOT}
9 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/EasyCompatible
10 | PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates
11 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
12 | SKIP_INSTALL = YES
13 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES
14 |
--------------------------------------------------------------------------------
/Pods/Target Support Files/EasyListView/EasyListView-Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | ${EXECUTABLE_NAME}
9 | CFBundleIdentifier
10 | ${PRODUCT_BUNDLE_IDENTIFIER}
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | ${PRODUCT_NAME}
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.3.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | ${CURRENT_PROJECT_VERSION}
23 | NSPrincipalClass
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/Pods/Target Support Files/EasyListView/EasyListView-dummy.m:
--------------------------------------------------------------------------------
1 | #import
2 | @interface PodsDummy_EasyListView : NSObject
3 | @end
4 | @implementation PodsDummy_EasyListView
5 | @end
6 |
--------------------------------------------------------------------------------
/Pods/Target Support Files/EasyListView/EasyListView-prefix.pch:
--------------------------------------------------------------------------------
1 | #ifdef __OBJC__
2 | #import
3 | #else
4 | #ifndef FOUNDATION_EXPORT
5 | #if defined(__cplusplus)
6 | #define FOUNDATION_EXPORT extern "C"
7 | #else
8 | #define FOUNDATION_EXPORT extern
9 | #endif
10 | #endif
11 | #endif
12 |
13 |
--------------------------------------------------------------------------------
/Pods/Target Support Files/EasyListView/EasyListView-umbrella.h:
--------------------------------------------------------------------------------
1 | #ifdef __OBJC__
2 | #import
3 | #else
4 | #ifndef FOUNDATION_EXPORT
5 | #if defined(__cplusplus)
6 | #define FOUNDATION_EXPORT extern "C"
7 | #else
8 | #define FOUNDATION_EXPORT extern
9 | #endif
10 | #endif
11 | #endif
12 |
13 |
14 | FOUNDATION_EXPORT double EasyListViewVersionNumber;
15 | FOUNDATION_EXPORT const unsigned char EasyListViewVersionString[];
16 |
17 |
--------------------------------------------------------------------------------
/Pods/Target Support Files/EasyListView/EasyListView.debug.xcconfig:
--------------------------------------------------------------------------------
1 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
2 | CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/EasyListView
3 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/EasyCompatible"
4 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
5 | LIBRARY_SEARCH_PATHS = $(inherited) "${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift
6 | OTHER_LDFLAGS = $(inherited) -framework "EasyCompatible"
7 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
8 | PODS_BUILD_DIR = ${BUILD_DIR}
9 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
10 | PODS_ROOT = ${SRCROOT}
11 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/..
12 | PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates
13 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
14 | SKIP_INSTALL = YES
15 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES
16 |
--------------------------------------------------------------------------------
/Pods/Target Support Files/EasyListView/EasyListView.modulemap:
--------------------------------------------------------------------------------
1 | framework module EasyListView {
2 | umbrella header "EasyListView-umbrella.h"
3 |
4 | export *
5 | module * { export * }
6 | }
7 |
--------------------------------------------------------------------------------
/Pods/Target Support Files/EasyListView/EasyListView.release.xcconfig:
--------------------------------------------------------------------------------
1 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
2 | CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/EasyListView
3 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/EasyCompatible"
4 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
5 | LIBRARY_SEARCH_PATHS = $(inherited) "${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift
6 | OTHER_LDFLAGS = $(inherited) -framework "EasyCompatible"
7 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
8 | PODS_BUILD_DIR = ${BUILD_DIR}
9 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
10 | PODS_ROOT = ${SRCROOT}
11 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/..
12 | PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates
13 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
14 | SKIP_INSTALL = YES
15 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES
16 |
--------------------------------------------------------------------------------
/Pods/Target Support Files/Pods-EasyListViewExample/Pods-EasyListViewExample-Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | ${EXECUTABLE_NAME}
9 | CFBundleIdentifier
10 | ${PRODUCT_BUNDLE_IDENTIFIER}
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | ${PRODUCT_NAME}
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | ${CURRENT_PROJECT_VERSION}
23 | NSPrincipalClass
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/Pods/Target Support Files/Pods-EasyListViewExample/Pods-EasyListViewExample-acknowledgements.markdown:
--------------------------------------------------------------------------------
1 | # Acknowledgements
2 | This application makes use of the following third party libraries:
3 |
4 | ## EasyCompatible
5 |
6 | MIT License
7 |
8 | Copyright (c) 2022 moliya
9 |
10 | Permission is hereby granted, free of charge, to any person obtaining a copy
11 | of this software and associated documentation files (the "Software"), to deal
12 | in the Software without restriction, including without limitation the rights
13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14 | copies of the Software, and to permit persons to whom the Software is
15 | furnished to do so, subject to the following conditions:
16 |
17 | The above copyright notice and this permission notice shall be included in all
18 | copies or substantial portions of the Software.
19 |
20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26 | SOFTWARE.
27 |
28 |
29 | ## EasyListView
30 |
31 | MIT License
32 |
33 | Copyright (c) 2020 moliya
34 |
35 | Permission is hereby granted, free of charge, to any person obtaining a copy
36 | of this software and associated documentation files (the "Software"), to deal
37 | in the Software without restriction, including without limitation the rights
38 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
39 | copies of the Software, and to permit persons to whom the Software is
40 | furnished to do so, subject to the following conditions:
41 |
42 | The above copyright notice and this permission notice shall be included in all
43 | copies or substantial portions of the Software.
44 |
45 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
46 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
47 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
48 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
49 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
50 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
51 | SOFTWARE.
52 |
53 | Generated by CocoaPods - https://cocoapods.org
54 |
--------------------------------------------------------------------------------
/Pods/Target Support Files/Pods-EasyListViewExample/Pods-EasyListViewExample-acknowledgements.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreferenceSpecifiers
6 |
7 |
8 | FooterText
9 | This application makes use of the following third party libraries:
10 | Title
11 | Acknowledgements
12 | Type
13 | PSGroupSpecifier
14 |
15 |
16 | FooterText
17 | MIT License
18 |
19 | Copyright (c) 2022 moliya
20 |
21 | Permission is hereby granted, free of charge, to any person obtaining a copy
22 | of this software and associated documentation files (the "Software"), to deal
23 | in the Software without restriction, including without limitation the rights
24 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
25 | copies of the Software, and to permit persons to whom the Software is
26 | furnished to do so, subject to the following conditions:
27 |
28 | The above copyright notice and this permission notice shall be included in all
29 | copies or substantial portions of the Software.
30 |
31 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
32 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
33 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
34 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
35 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
36 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
37 | SOFTWARE.
38 |
39 | License
40 | MIT
41 | Title
42 | EasyCompatible
43 | Type
44 | PSGroupSpecifier
45 |
46 |
47 | FooterText
48 | MIT License
49 |
50 | Copyright (c) 2020 moliya
51 |
52 | Permission is hereby granted, free of charge, to any person obtaining a copy
53 | of this software and associated documentation files (the "Software"), to deal
54 | in the Software without restriction, including without limitation the rights
55 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
56 | copies of the Software, and to permit persons to whom the Software is
57 | furnished to do so, subject to the following conditions:
58 |
59 | The above copyright notice and this permission notice shall be included in all
60 | copies or substantial portions of the Software.
61 |
62 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
63 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
64 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
65 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
66 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
67 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
68 | SOFTWARE.
69 |
70 | License
71 | MIT
72 | Title
73 | EasyListView
74 | Type
75 | PSGroupSpecifier
76 |
77 |
78 | FooterText
79 | Generated by CocoaPods - https://cocoapods.org
80 | Title
81 |
82 | Type
83 | PSGroupSpecifier
84 |
85 |
86 | StringsTable
87 | Acknowledgements
88 | Title
89 | Acknowledgements
90 |
91 |
92 |
--------------------------------------------------------------------------------
/Pods/Target Support Files/Pods-EasyListViewExample/Pods-EasyListViewExample-dummy.m:
--------------------------------------------------------------------------------
1 | #import
2 | @interface PodsDummy_Pods_EasyListViewExample : NSObject
3 | @end
4 | @implementation PodsDummy_Pods_EasyListViewExample
5 | @end
6 |
--------------------------------------------------------------------------------
/Pods/Target Support Files/Pods-EasyListViewExample/Pods-EasyListViewExample-frameworks-Debug-input-files.xcfilelist:
--------------------------------------------------------------------------------
1 | ${PODS_ROOT}/Target Support Files/Pods-EasyListViewExample/Pods-EasyListViewExample-frameworks.sh
2 | ${BUILT_PRODUCTS_DIR}/EasyCompatible/EasyCompatible.framework
3 | ${BUILT_PRODUCTS_DIR}/EasyListView/EasyListView.framework
--------------------------------------------------------------------------------
/Pods/Target Support Files/Pods-EasyListViewExample/Pods-EasyListViewExample-frameworks-Debug-output-files.xcfilelist:
--------------------------------------------------------------------------------
1 | ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/EasyCompatible.framework
2 | ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/EasyListView.framework
--------------------------------------------------------------------------------
/Pods/Target Support Files/Pods-EasyListViewExample/Pods-EasyListViewExample-frameworks-Release-input-files.xcfilelist:
--------------------------------------------------------------------------------
1 | ${PODS_ROOT}/Target Support Files/Pods-EasyListViewExample/Pods-EasyListViewExample-frameworks.sh
2 | ${BUILT_PRODUCTS_DIR}/EasyCompatible/EasyCompatible.framework
3 | ${BUILT_PRODUCTS_DIR}/EasyListView/EasyListView.framework
--------------------------------------------------------------------------------
/Pods/Target Support Files/Pods-EasyListViewExample/Pods-EasyListViewExample-frameworks-Release-output-files.xcfilelist:
--------------------------------------------------------------------------------
1 | ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/EasyCompatible.framework
2 | ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/EasyListView.framework
--------------------------------------------------------------------------------
/Pods/Target Support Files/Pods-EasyListViewExample/Pods-EasyListViewExample-umbrella.h:
--------------------------------------------------------------------------------
1 | #ifdef __OBJC__
2 | #import
3 | #else
4 | #ifndef FOUNDATION_EXPORT
5 | #if defined(__cplusplus)
6 | #define FOUNDATION_EXPORT extern "C"
7 | #else
8 | #define FOUNDATION_EXPORT extern
9 | #endif
10 | #endif
11 | #endif
12 |
13 |
14 | FOUNDATION_EXPORT double Pods_EasyListViewExampleVersionNumber;
15 | FOUNDATION_EXPORT const unsigned char Pods_EasyListViewExampleVersionString[];
16 |
17 |
--------------------------------------------------------------------------------
/Pods/Target Support Files/Pods-EasyListViewExample/Pods-EasyListViewExample.debug.xcconfig:
--------------------------------------------------------------------------------
1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES
2 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
3 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/EasyCompatible" "${PODS_CONFIGURATION_BUILD_DIR}/EasyListView"
4 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
5 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/EasyCompatible/EasyCompatible.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/EasyListView/EasyListView.framework/Headers"
6 | LD_RUNPATH_SEARCH_PATHS = $(inherited) /usr/lib/swift '@executable_path/Frameworks' '@loader_path/Frameworks'
7 | LIBRARY_SEARCH_PATHS = $(inherited) "${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift
8 | OTHER_LDFLAGS = $(inherited) -framework "EasyCompatible" -framework "EasyListView"
9 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
10 | PODS_BUILD_DIR = ${BUILD_DIR}
11 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
12 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/.
13 | PODS_ROOT = ${SRCROOT}/Pods
14 | PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates
15 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES
16 |
--------------------------------------------------------------------------------
/Pods/Target Support Files/Pods-EasyListViewExample/Pods-EasyListViewExample.modulemap:
--------------------------------------------------------------------------------
1 | framework module Pods_EasyListViewExample {
2 | umbrella header "Pods-EasyListViewExample-umbrella.h"
3 |
4 | export *
5 | module * { export * }
6 | }
7 |
--------------------------------------------------------------------------------
/Pods/Target Support Files/Pods-EasyListViewExample/Pods-EasyListViewExample.release.xcconfig:
--------------------------------------------------------------------------------
1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES
2 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
3 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/EasyCompatible" "${PODS_CONFIGURATION_BUILD_DIR}/EasyListView"
4 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
5 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/EasyCompatible/EasyCompatible.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/EasyListView/EasyListView.framework/Headers"
6 | LD_RUNPATH_SEARCH_PATHS = $(inherited) /usr/lib/swift '@executable_path/Frameworks' '@loader_path/Frameworks'
7 | LIBRARY_SEARCH_PATHS = $(inherited) "${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift
8 | OTHER_LDFLAGS = $(inherited) -framework "EasyCompatible" -framework "EasyListView"
9 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
10 | PODS_BUILD_DIR = ${BUILD_DIR}
11 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
12 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/.
13 | PODS_ROOT = ${SRCROOT}/Pods
14 | PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates
15 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES
16 |
--------------------------------------------------------------------------------
/Sources/ObjC/EasyListObjC.swift:
--------------------------------------------------------------------------------
1 | //
2 | // EasyListObjC.swift
3 | // EasyListViewExample
4 | //
5 | // Created by carefree on 2020/8/18.
6 | // Copyright © 2020 carefree. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | @objc open class EasyListObjCAttributes: NSObject {
12 | fileprivate var attributes: EasyListAttributes
13 |
14 | init(_ attributes: EasyListAttributes) {
15 | self.attributes = attributes
16 | super.init()
17 | }
18 | }
19 |
20 | @objc public extension EasyListObjCAttributes {
21 | /**
22 | 设置唯一标识
23 |
24 | * parameter identifier: 标识字符串
25 |
26 | * returns: 自定义配置项
27 | */
28 | var identifier: (String) -> EasyListObjCAttributes {
29 | return { identifier in
30 | self.attributes.identifier(identifier)
31 | return self
32 | }
33 | }
34 |
35 | /**
36 | 设置内间距
37 |
38 | * parameter insets: 内间距
39 |
40 | * returns: 自定义配置项
41 | */
42 | var insets: (UIEdgeInsets) -> EasyListObjCAttributes {
43 | return { insets in
44 | self.attributes.insets(insets)
45 | return self
46 | }
47 | }
48 |
49 | /**
50 | 设置与上一元素的间距
51 |
52 | * parameter spacing: 间距
53 |
54 | * returns: 自定义配置项
55 | */
56 | var spacing: (CGFloat) -> EasyListObjCAttributes {
57 | return { spacing in
58 | self.attributes.spacing(spacing)
59 | return self
60 | }
61 | }
62 |
63 | /**
64 | 设置超出部分是否裁剪
65 |
66 | * parameter clipsToBounds: 是否裁剪
67 |
68 | * returns: 自定义配置项
69 | */
70 | var clipsToBounds: (Bool) -> EasyListObjCAttributes {
71 | return { clipsToBounds in
72 | self.attributes.clipsToBounds(clipsToBounds)
73 | return self
74 | }
75 | }
76 | }
77 |
78 | @available(*, unavailable)
79 | @objc public extension UIScrollView {
80 | // MARK: - Coordinator
81 | var easy_coordinator: EasyListCoordinator {
82 | get {
83 | return easy.coordinator
84 | }
85 | set {
86 | easy.coordinator = newValue
87 | }
88 | }
89 |
90 | // MARK: - Append
91 | /**
92 | 添加一个视图元素
93 |
94 | * parameter view: 视图
95 |
96 | * returns: 自定义配置项
97 | */
98 | @discardableResult
99 | func easy_appendView(_ view: UIView) -> EasyListObjCAttributes {
100 | let attributes = easy.appendView(view)
101 | return EasyListObjCAttributes(attributes)
102 | }
103 |
104 | /**
105 | 添加一个视图元素
106 |
107 | * parameter block: 视图block
108 |
109 | * returns: 自定义配置项
110 | */
111 | @discardableResult
112 | func easy_appendViewBy(_ block: () -> UIView) -> EasyListObjCAttributes {
113 | let attributes = easy.appendView(block)
114 | return EasyListObjCAttributes(attributes)
115 | }
116 |
117 | // MARK: - Insert
118 | /**
119 | 在目标之后插入一个视图元素
120 |
121 | * parameter view: 视图
122 | * parameter element: 前一个视图元素,可以是UIView,也可以是视图唯一标识
123 |
124 | * returns: 自定义配置项
125 | */
126 | @discardableResult
127 | func easy_insertView(_ view: UIView, after element: Any) -> EasyListObjCAttributes {
128 | let attributes = easy.insertView(view, after: element)
129 | return EasyListObjCAttributes(attributes)
130 | }
131 |
132 | /**
133 | 在目标之后插入一个视图元素
134 |
135 | * parameter block: 视图block
136 | * parameter element: 前一个视图元素,可以是UIView,也可以是视图唯一标识
137 |
138 | * returns: 自定义配置项
139 | */
140 | @discardableResult
141 | func easy_insertViewBy(_ block: () -> UIView, after element: Any) -> EasyListObjCAttributes {
142 | let attributes = easy.insertView(block, after: element)
143 | return EasyListObjCAttributes(attributes)
144 | }
145 |
146 | /**
147 | 在目标之前插入一个视图元素
148 |
149 | * parameter view: 视图
150 | * parameter element: 后一个视图元素,可以是UIView,也可以是视图唯一标识
151 |
152 | * returns: 自定义配置项
153 | */
154 | @discardableResult
155 | func easy_insertView(_ view: UIView, before element: Any) -> EasyListObjCAttributes {
156 | let attributes = easy.insertView(view, before: element)
157 | return EasyListObjCAttributes(attributes)
158 | }
159 |
160 | /**
161 | 在目标之前插入一个视图元素
162 |
163 | * parameter block: 视图block
164 | * parameter element: 后一个视图元素,可以是UIView,也可以是视图唯一标识
165 |
166 | * returns: 自定义配置项
167 | */
168 | @discardableResult
169 | func easy_insertViewBy(_ block: () -> UIView, before element: Any) -> EasyListObjCAttributes {
170 | let attributes = easy.insertView(block, before: element)
171 | return EasyListObjCAttributes(attributes)
172 | }
173 |
174 | // MARK: - Delete
175 | /**
176 | 删除一个视图元素
177 |
178 | * parameter view: 要删除的视图,可以是UIView,也可以是视图的唯一标识
179 | */
180 | func easy_deleteView(_ view: Any) {
181 | easy.deleteView(view)
182 | }
183 |
184 | /**
185 | 删除一个视图元素
186 |
187 | * parameter view: 要删除的视图,可以是UIView,也可以是视图的唯一标识
188 | * parameter completion: 删除完成后的回调
189 | */
190 | func easy_deleteView(_ view: Any, completion: (() -> Void)?) {
191 | easy.deleteView(view, completion: completion)
192 | }
193 |
194 | /**
195 | 删除所有视图元素
196 |
197 | */
198 | func easy_deleteAll() {
199 | easy.deleteAll()
200 | }
201 |
202 | // MARK: - BatchUpdate
203 | /**
204 | 开始批量更新操作
205 |
206 | * Note: 需和endUpdates成对使用。
207 | */
208 | func easy_beginUpdates() {
209 | easy.beginUpdates()
210 | }
211 |
212 | /**
213 | 开始批量更新操作
214 |
215 | * parameter option: 更新方式
216 | * Note: 需和endUpdates成对使用。
217 | */
218 | func easy_beginUpdates(option: EasyListUpdateOption) {
219 | easy.beginUpdates(option: option)
220 | }
221 |
222 | /**
223 | 完成批量更新操作
224 |
225 | */
226 | func easy_endUpdates() {
227 | easy.endUpdates()
228 | }
229 |
230 | /**
231 | 完成批量更新操作
232 |
233 | * parameter completion: 更新完成后的回调
234 | */
235 | func easy_endUpdates(completion: (() -> Void)?) {
236 | easy.endUpdates(completion)
237 | }
238 |
239 | // MARK: - Disposable
240 | /**
241 | 生成自释放元素
242 |
243 | * parameter maker: 闭包
244 |
245 | * returns: 生成的视图
246 | */
247 | func easy_disposableView(maker: @escaping () -> UIView) -> UIView {
248 | return easy.disposableView(with: maker)
249 | }
250 |
251 | /**
252 | 刷新数据
253 |
254 | */
255 | func easy_reloadDisposableData() {
256 | easy.reloadDisposableData()
257 | }
258 |
259 | /**
260 | 触发自释放机制
261 |
262 | */
263 | func easy_triggerDisposable() {
264 | easy.triggerDisposable()
265 | }
266 |
267 | // MARK: - Getter
268 | /**
269 | 获取视图元素
270 |
271 | * parameter identifier: 视图唯一标识
272 |
273 | * returns: 找到的视图
274 | */
275 | func easy_getElement(identifier: String) -> AnyObject? {
276 | return easy.getElement(identifier: identifier)
277 | }
278 |
279 | /**
280 | 获取指定下标的自释放元素
281 |
282 | * parameter index: 下标
283 |
284 | * returns: 找到的视图
285 | */
286 | func easy_getDisposableElementAtIndex(_ index: Int) -> AnyObject? {
287 | return easy.getDisposableElement(at: index)
288 | }
289 |
290 | /**
291 | 获取所有可视的自释放元素
292 |
293 | * returns: 找到的视图集合
294 | */
295 | var easy_visibleDisposableElements: [AnyObject] {
296 | return easy.visibleDisposableElements
297 | }
298 | }
299 |
--------------------------------------------------------------------------------
/Sources/ObjC/EasyListObjCDeprecated.swift:
--------------------------------------------------------------------------------
1 | //
2 | // EasyListObjCDeprecated.swift
3 | // EasyListViewExample
4 | //
5 | // Created by carefree on 2022/10/8.
6 | // Copyright © 2022 carefree. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | @available(*, unavailable)
12 | @objc public extension UIScrollView {
13 | // MARK: - Append
14 | @available(*, deprecated, message: "Please use easy_appendView: instead.")
15 | func easy_appendView(_ view: UIView, spacing: CGFloat) {
16 | easy.append(view, spacing: spacing)
17 | }
18 |
19 | @available(*, deprecated, message: "Please use easy_appendViewBy: instead.")
20 | func easy_appendViewBy(_ block: () -> UIView, spacing: CGFloat) {
21 | easy.append(block(), spacing: spacing)
22 | }
23 |
24 | @available(*, deprecated, message: "Please use easy_appendView: instead.")
25 | func easy_appendView(_ view: UIView, forIdentifier identifier: String, spacing: CGFloat) {
26 | easy.append(view, for: identifier, spacing: spacing)
27 | }
28 |
29 | @available(*, deprecated, message: "Please use easy_appendViewBy: instead.")
30 | func easy_appendViewBy(_ block: () -> UIView, forIdentifier identifier: String, spacing: CGFloat) {
31 | easy.append(block(), for: identifier, spacing: spacing)
32 | }
33 |
34 | @available(*, deprecated, message: "Please use easy_appendView: instead.")
35 | func easy_appendView(_ view: UIView, forIdentifier identifier: String, withInsets insets: UIEdgeInsets) {
36 | easy.append(view, with: insets, for: identifier)
37 | }
38 |
39 | @available(*, deprecated, message: "Please use easy_appendViewBy: instead.")
40 | func easy_appendViewBy(_ block: () -> UIView, forIdentifier identifier: String, withInsets insets: UIEdgeInsets) {
41 | easy.append(block(), with: insets, for: identifier)
42 | }
43 |
44 | // MARK: - Insert
45 | @available(*, deprecated, message: "Please use easy_insertView:after: instead.")
46 | func easy_insertView(_ view: UIView, after element: Any, withInsets insets: UIEdgeInsets) {
47 | easy.insert(view, after: element, with: insets)
48 | }
49 |
50 | @available(*, deprecated, message: "Please use easy_insertView:after: instead.")
51 | func easy_insertViewBy(_ block: () -> UIView, after element: Any, withInsets insets: UIEdgeInsets) {
52 | easy.insert(block(), after: element, with: insets)
53 | }
54 |
55 | @available(*, deprecated, message: "Please use easy_insertView:after: instead.")
56 | func easy_insertView(_ view: UIView, after element: Any, withInsets insets: UIEdgeInsets, forIdentifier identifier: String) {
57 | easy.insert(view, after: element, with: insets, for: identifier)
58 | }
59 |
60 | @available(*, deprecated, message: "Please use easy_insertView:after: instead.")
61 | func easy_insertViewBy(_ block: () -> UIView, after element: Any, withInsets insets: UIEdgeInsets, forIdentifier identifier: String) {
62 | easy.insert(block(), after: element, with: insets, for: identifier)
63 | }
64 |
65 | @available(*, deprecated, message: "Please use easy_insertView:after: instead.")
66 | func easy_insertView(_ view: UIView, after element: Any, withInsets insets: UIEdgeInsets, forIdentifier identifier: String, completion: (() -> Void)?) {
67 | easy.insert(view, after: element, with: insets, for: identifier, completion: completion)
68 | }
69 |
70 | @available(*, deprecated, message: "Please use easy_insertView:after: instead.")
71 | func easy_insertViewBy(_ block: () -> UIView, after element: Any, withInsets insets: UIEdgeInsets, forIdentifier identifier: String, completion: (() -> Void)?) {
72 | easy.insert(block(), after: element, with: insets, for: identifier, completion: completion)
73 | }
74 |
75 | @available(*, deprecated, message: "Please use easy_insertView:before: instead.")
76 | func easy_insertView(_ view: UIView, before element: Any, withInsets insets: UIEdgeInsets) {
77 | easy.insert(view, before: element, with: insets)
78 | }
79 |
80 | @available(*, deprecated, message: "Please use easy_insertView:before: instead.")
81 | func easy_insertViewBy(_ block: () -> UIView, before element: Any, withInsets insets: UIEdgeInsets) {
82 | easy.insert(block(), before: element, with: insets)
83 | }
84 |
85 | @available(*, deprecated, message: "Please use easy_insertView:before: instead.")
86 | func easy_insertView(_ view: UIView, before element: Any, withInsets insets: UIEdgeInsets, forIdentifier identifier: String) {
87 | easy.insert(view, before: element, with: insets, for: identifier)
88 | }
89 |
90 | @available(*, deprecated, message: "Please use easy_insertView:before: instead.")
91 | func easy_insertViewBy(_ block: () -> UIView, before element: Any, withInsets insets: UIEdgeInsets, forIdentifier identifier: String) {
92 | easy.insert(block(), before: element, with: insets, for: identifier)
93 | }
94 |
95 | @available(*, deprecated, message: "Please use easy_insertView:before: instead.")
96 | func easy_insertView(_ view: UIView, before element: Any, withInsets insets: UIEdgeInsets, forIdentifier identifier: String, completion: (() -> Void)?) {
97 | easy.insert(view, before: element, with: insets, for: identifier, completion: completion)
98 | }
99 |
100 | @available(*, deprecated, message: "Please use easy_insertView:before: instead.")
101 | func easy_insertViewBy(_ block: () -> UIView, before element: Any, withInsets insets: UIEdgeInsets, forIdentifier identifier: String, completion: (() -> Void)?) {
102 | easy.insert(block(), before: element, with: insets, for: identifier, completion: completion)
103 | }
104 |
105 | // MARK: - Delete
106 | @available(*, deprecated, message: "Please use easy_deleteView: instead.")
107 | func easy_deleteElement(_ element: Any) {
108 | easy.delete(element)
109 | }
110 |
111 | @available(*, deprecated, message: "Please use easy_deleteView:completion: instead.")
112 | func easy_deleteElement(_ element: Any, completion: (() -> Void)?) {
113 | easy.delete(element, completion: completion)
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/Sources/Swift/EasyListAppend.swift:
--------------------------------------------------------------------------------
1 | //
2 | // EasyListAppend.swift
3 | // EasyListViewExample
4 | //
5 | // Created by carefree on 2022/10/7.
6 | // Copyright © 2022 carefree. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import EasyCompatible
11 |
12 | public extension EasyExtension where Base: UIScrollView {
13 | /**
14 | 添加一个视图元素
15 |
16 | * parameter viewClosure: 视图闭包
17 |
18 | * returns: 自定义配置项
19 | */
20 | @discardableResult
21 | func appendView(_ viewClosure: () -> UIView) -> EasyListAttributes {
22 | return appendView(viewClosure())
23 | }
24 |
25 | /**
26 | 添加一个视图元素
27 |
28 | * parameter view: 视图
29 |
30 | * returns: 自定义配置项
31 | */
32 | @discardableResult
33 | func appendView(_ view: UIView) -> EasyListAttributes {
34 | let scrollView = base
35 |
36 | //移除旧的约束
37 | searchConstraintsIn(scrollView, with: [
38 | .first(scrollView, .bottom),
39 | .second(scrollView, .bottom)
40 | ]).forEach { $0.isActive = false }
41 | //添加子视图
42 | var contentView = EasyListContentView()
43 | var isDisposable = false
44 | if let disposableView = view as? EasyListContentView {
45 | //动态元素
46 | contentView = disposableView
47 | isDisposable = true
48 | } else {
49 | //静态元素
50 | var staticView = view
51 | if let cell = staticView as? UITableViewCell {
52 | staticView = cell.contentView
53 | coordinator.cells[contentView] = cell
54 | }
55 | staticView.translatesAutoresizingMaskIntoConstraints = false
56 | contentView.addSubview(staticView)
57 | }
58 |
59 | guard let view = contentView.subviews.first else {
60 | return EasyListAttributes()
61 | }
62 |
63 | var insets = coordinator.globalEdgeInsets
64 | insets.top = insets.top + coordinator.globalSpacing
65 |
66 | let leadingConstraint = addConstraint(for: contentView, item1: view, attr1: .leading, item2: contentView, attr2: .leading, constant: insets.left)
67 | let trailingConstraint = addConstraint(for: contentView, item1: view, attr1: .trailing, item2: contentView, attr2: .trailing, constant: -insets.right)
68 | let topConstraint = addConstraint(for: contentView, item1: view, attr1: .top, item2: contentView, attr2: .top, constant: insets.top)
69 | let bottomConstraint = addConstraint(for: contentView, item1: view, attr1: .bottom, item2: contentView, attr2: .bottom, constant: -insets.bottom)
70 |
71 | contentView.clipsToBounds = coordinator.globalClipsToBounds
72 | contentView.translatesAutoresizingMaskIntoConstraints = false
73 | scrollView.addSubview(contentView)
74 |
75 | addConstraint(for: scrollView, item1: scrollView, attr1: .width, item2: contentView, attr2: .width)
76 | addConstraint(for: scrollView, item1: contentView, attr1: .leading, item2: scrollView, attr2: .leading)
77 | addConstraint(for: scrollView, item1: contentView, attr1: .trailing, item2: scrollView, attr2: .trailing)
78 | addConstraint(for: scrollView, item1: contentView, attr1: .bottom, item2: scrollView, attr2: .bottom)
79 | if let lastView = coordinator.elements.last?.view {
80 | addConstraint(for: scrollView, item1: contentView, attr1: .top, item2: lastView, attr2: .bottom)
81 | } else {
82 | addConstraint(for: scrollView, item1: contentView, attr1: .top, item2: scrollView, attr2: .top)
83 | }
84 |
85 | var element = Element(view: contentView, insets: insets)
86 | coordinator.elements.append(element)
87 | if isDisposable {
88 | coordinator.disposableElements.append(Element(view: contentView, insets: insets))
89 | }
90 |
91 | if coordinator.onBatchUpdate {
92 | element.inserting = true
93 | }
94 |
95 | var attributes = EasyListAttributes()
96 | attributes.coordinator = coordinator
97 | attributes.element = element
98 | attributes.leadingConstraint = leadingConstraint
99 | attributes.trailingConstraint = trailingConstraint
100 | attributes.topConstraint = topConstraint
101 | attributes.bottomConstraint = bottomConstraint
102 |
103 | return attributes
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/Sources/Swift/EasyListAppendDeprecated.swift:
--------------------------------------------------------------------------------
1 | //
2 | // EasyListAppendDeprecated.swift
3 | // EasyListViewExample
4 | //
5 | // Created by carefree on 2022/10/7.
6 | // Copyright © 2022 carefree. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import EasyCompatible
11 |
12 | public extension EasyExtension where Base: UIScrollView {
13 | /**
14 | 添加一个视图元素
15 |
16 | * parameter viewOrClosure: 视图或闭包
17 | * parameter spacing: 与上一个视图的间距
18 | */
19 | @available(*, deprecated, renamed: "appendView(_:)", message: "Please use appendView(_:) instead.")
20 | func append(_ viewOrClosure: Any, spacing: CGFloat = 0) {
21 | var inset = coordinator.globalEdgeInsets
22 | inset.top = spacing
23 | append(viewOrClosure, with: inset)
24 | }
25 |
26 | /**
27 | 添加一个视图元素
28 |
29 | * parameter viewOrClosure: 视图或闭包
30 | * parameter identifier: 视图唯一标识
31 | * parameter spacing: 与上一个视图的间距
32 | */
33 | @available(*, deprecated, renamed: "appendView(_:)", message: "Please use appendView(_:) instead.")
34 | func append(_ viewOrClosure: Any, for identifier: String = "", spacing: CGFloat = 0) {
35 | var inset = coordinator.globalEdgeInsets
36 | inset.top = spacing
37 | append(viewOrClosure, with: inset, for: identifier)
38 | }
39 |
40 | /**
41 | 添加一个视图元素
42 |
43 | * parameter viewOrClosure: 视图或闭包
44 | * parameter insets: 视图自定义的间距
45 | * parameter identifier: 视图唯一标识
46 | */
47 | @available(*, deprecated, renamed: "appendView(_:)", message: "Please use appendView(_:) instead.")
48 | func append(_ viewOrClosure: Any, with insets: UIEdgeInsets, for identifier: String = "") {
49 | let scrollView = base
50 |
51 | //移除旧的约束
52 | searchConstraintsIn(scrollView, with: [
53 | .first(scrollView, .bottom),
54 | .second(scrollView, .bottom)
55 | ]).forEach { $0.isActive = false }
56 | //添加子视图
57 | var contentView = EasyListContentView()
58 | var isDisposable = false
59 | if let disposableView = viewOrClosure as? EasyListContentView {
60 | //动态元素
61 | contentView = disposableView
62 | isDisposable = true
63 | } else if let staticView = viewOrClosure as? UIView {
64 | //静态元素
65 | var view = staticView
66 | if let cell = view as? UITableViewCell {
67 | view = cell.contentView
68 | coordinator.cells[contentView] = cell
69 | }
70 | view.translatesAutoresizingMaskIntoConstraints = false
71 | contentView.addSubview(view)
72 | } else if let closure = viewOrClosure as? () -> UIView {
73 | //闭包
74 | var view = closure()
75 | if let cell = view as? UITableViewCell {
76 | view = cell.contentView
77 | coordinator.cells[contentView] = cell
78 | }
79 | view.translatesAutoresizingMaskIntoConstraints = false
80 | contentView.addSubview(view)
81 | }
82 |
83 | guard let view = contentView.subviews.first else { return }
84 |
85 | addConstraint(for: contentView, item1: view, attr1: .leading, item2: contentView, attr2: .leading, constant: insets.left)
86 | addConstraint(for: contentView, item1: view, attr1: .trailing, item2: contentView, attr2: .trailing, constant: -insets.right)
87 | addConstraint(for: contentView, item1: view, attr1: .top, item2: contentView, attr2: .top, constant: insets.top)
88 | addConstraint(for: contentView, item1: view, attr1: .bottom, item2: contentView, attr2: .bottom, constant: -insets.bottom)
89 |
90 | contentView.clipsToBounds = true
91 | contentView.translatesAutoresizingMaskIntoConstraints = false
92 | scrollView.addSubview(contentView)
93 |
94 | addConstraint(for: scrollView, item1: scrollView, attr1: .width, item2: contentView, attr2: .width)
95 | addConstraint(for: scrollView, item1: contentView, attr1: .leading, item2: scrollView, attr2: .leading)
96 | addConstraint(for: scrollView, item1: contentView, attr1: .trailing, item2: scrollView, attr2: .trailing)
97 | addConstraint(for: scrollView, item1: contentView, attr1: .bottom, item2: scrollView, attr2: .bottom)
98 | if let lastView = coordinator.elements.last?.view {
99 | addConstraint(for: scrollView, item1: contentView, attr1: .top, item2: lastView, attr2: .bottom)
100 | } else {
101 | addConstraint(for: scrollView, item1: contentView, attr1: .top, item2: scrollView, attr2: .top)
102 | }
103 |
104 | var element = Element(view: contentView, insets: insets, identifier: identifier)
105 | coordinator.elements.append(element)
106 | if isDisposable {
107 | coordinator.disposableElements.append(Element(view: contentView, insets: insets, identifier: identifier))
108 | }
109 |
110 | if coordinator.onBatchUpdate {
111 | element.inserting = true
112 | }
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/Sources/Swift/EasyListAttributes.swift:
--------------------------------------------------------------------------------
1 | //
2 | // EasyListAttributes.swift
3 | // EasyListView
4 | //
5 | // Created by carefree on 2022/10/9.
6 | //
7 |
8 | import UIKit
9 |
10 | public struct EasyListAttributes {
11 | internal var coordinator: EasyListCoordinator?
12 | internal var element: Element?
13 | internal var leadingConstraint: NSLayoutConstraint?
14 | internal var trailingConstraint: NSLayoutConstraint?
15 | internal var topConstraint: NSLayoutConstraint?
16 | internal var bottomConstraint: NSLayoutConstraint?
17 | }
18 |
19 | public extension EasyListAttributes {
20 | /**
21 | 设置唯一标识
22 |
23 | * parameter identifier: 标识字符串
24 |
25 | * returns: 自定义配置项
26 | */
27 | @discardableResult
28 | func identifier(_ identifier: String) -> Self {
29 | if let coordinator = coordinator, let element = element {
30 | if let index = coordinator.elements.firstIndex(of: element) {
31 | var newElement = coordinator.elements[index]
32 | newElement.identifier = identifier
33 | coordinator.elements[index] = newElement
34 | }
35 | if let index = coordinator.disposableElements.firstIndex(of: element) {
36 | var newElement = coordinator.elements[index]
37 | newElement.identifier = identifier
38 | coordinator.elements[index] = newElement
39 | }
40 | }
41 |
42 | return self
43 | }
44 |
45 | /**
46 | 设置内间距
47 |
48 | * parameter insets: 内间距
49 |
50 | * returns: 自定义配置项
51 | */
52 | @discardableResult
53 | func insets(_ insets: UIEdgeInsets) -> Self {
54 | leadingConstraint?.constant = insets.left
55 | trailingConstraint?.constant = -insets.right
56 | topConstraint?.constant = insets.top
57 | bottomConstraint?.constant = -insets.bottom
58 | return self
59 | }
60 |
61 | /**
62 | 设置与上一元素的间距
63 |
64 | * parameter spacing: 间距
65 |
66 | * returns: 自定义配置项
67 | */
68 | @discardableResult
69 | func spacing(_ spacing: CGFloat) -> Self {
70 | topConstraint?.constant = spacing
71 | return self
72 | }
73 |
74 | /**
75 | 设置超出部分是否裁剪
76 |
77 | * parameter clipsToBounds: 是否裁剪
78 |
79 | * returns: 自定义配置项
80 | */
81 | @discardableResult
82 | func clipsToBounds(_ clipsToBounds: Bool) -> Self {
83 | element?.view.clipsToBounds = clipsToBounds
84 | return self
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/Sources/Swift/EasyListConstraint.swift:
--------------------------------------------------------------------------------
1 | //
2 | // EasyListConstraint.swift
3 | // EasyListViewExample
4 | //
5 | // Created by carefree on 2020/8/6.
6 | // Copyright © 2020 carefree. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | // 添加约束
12 | @discardableResult
13 | internal func addConstraint(for view: UIView,
14 | item1: AnyObject,
15 | attr1: NSLayoutConstraint.Attribute,
16 | item2: AnyObject? = nil,
17 | attr2: NSLayoutConstraint.Attribute? = nil,
18 | constant: CGFloat = 0) -> NSLayoutConstraint {
19 | let c = NSLayoutConstraint(
20 | item: item1,
21 | attribute: attr1,
22 | relatedBy: .equal,
23 | toItem: item2,
24 | attribute: ((attr2 == nil) ? attr1 : attr2! ),
25 | multiplier: 1,
26 | constant: constant
27 | )
28 | c.priority = UILayoutPriority(rawValue: UILayoutPriority.defaultHigh.rawValue + 1)
29 | view.addConstraint(c)
30 | return c
31 | }
32 |
--------------------------------------------------------------------------------
/Sources/Swift/EasyListCoordinator.swift:
--------------------------------------------------------------------------------
1 | //
2 | // EasyListCoordinator.swift
3 | // EasyListViewExample
4 | //
5 | // Created by carefree on 2020/8/8.
6 | // Copyright © 2020 carefree. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | @objc(EasyListUpdateOption)
12 | public enum EasyListUpdateOption: Int {
13 | case animatedLayout = 0 //带动画的布局更新
14 | case onlyLayout //无动画的布局更新
15 | case noLayout //不进行布局
16 | }
17 |
18 | internal struct Element: Equatable {
19 | var view: EasyListContentView
20 | var insets: UIEdgeInsets = .zero
21 | var identifier: String?
22 | var deleting: Bool = false
23 | var inserting: Bool = false
24 |
25 | static func == (lhs: Self, rhs: Self) -> Bool {
26 | return lhs.view == rhs.view
27 | }
28 | }
29 |
30 | @objcMembers
31 | open class EasyListCoordinator: NSObject {
32 |
33 | public weak private(set) var scrollView: UIScrollView?
34 |
35 | //全局内边距
36 | public var globalEdgeInsets: UIEdgeInsets = .zero
37 | //全局元素间距
38 | public var globalSpacing: CGFloat = 0
39 | //超出部分裁剪,默认为true
40 | public var globalClipsToBounds: Bool = true
41 | //动画持续时间(包括插入、删除),为0则无动画
42 | public var animationDuration: TimeInterval = 0.3
43 |
44 | internal var elements = [Element]()
45 | internal var disposableElements = [Element]()
46 | internal var cells = [EasyListContentView: UITableViewCell]()
47 | internal var onBatchUpdate = false
48 | internal var batchUpdateOption: EasyListUpdateOption = .animatedLayout
49 |
50 | @objc(initWithScrollView:)
51 | public init(with scrollView: UIScrollView) {
52 | self.scrollView = scrollView
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/Sources/Swift/EasyListDelete.swift:
--------------------------------------------------------------------------------
1 | //
2 | // EasyListDelete.swift
3 | // EasyListViewExample
4 | //
5 | // Created by carefree on 2022/10/7.
6 | // Copyright © 2022 carefree. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import EasyCompatible
11 |
12 | public extension EasyExtension where Base: UIScrollView {
13 | /**
14 | 删除一个视图元素
15 |
16 | * parameter view: 要删除的视图,可以是UIView,也可以是视图的唯一标识
17 | * parameter completion: 删除完成后的回调
18 | */
19 | func deleteView(_ view: Any, completion: (() -> Void)? = nil) {
20 | let elements = coordinator.elements
21 |
22 | var targetView: UIView?
23 | if let string = view as? String {
24 | targetView = elements.first { $0.identifier == string }?.view
25 | }
26 | if let cell = view as? UITableViewCell {
27 | targetView = coordinator.cells.first { $0.value == cell }?.key
28 | } else if let view = view as? UIView {
29 | targetView = elements.first { $0.view == view.superview }?.view
30 | }
31 | assert(targetView != nil, "invalid element")
32 |
33 | coordinator.elements = coordinator.elements.map {
34 | var tmp = $0
35 | if targetView == tmp.view && !tmp.deleting {
36 | tmp.deleting = true
37 | }
38 | return tmp
39 | }
40 |
41 | if coordinator.onBatchUpdate {
42 | completion?()
43 | return
44 | }
45 |
46 | animateDeletion(completion: {
47 | self.reloadDisposableIfNeed()
48 | completion?()
49 | }, duration: coordinator.animationDuration)
50 | }
51 |
52 | /**
53 | 删除所有视图元素
54 |
55 | */
56 | func deleteAll() {
57 | coordinator.elements.forEach {
58 | $0.view.removeFromSuperview()
59 | }
60 | coordinator.elements.removeAll()
61 | coordinator.disposableElements.removeAll()
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/Sources/Swift/EasyListDeleteDeprecated.swift:
--------------------------------------------------------------------------------
1 | //
2 | // EasyListDeleteDeprecated.swift
3 | // EasyListView
4 | //
5 | // Created by carefree on 2022/10/10.
6 | //
7 |
8 | import UIKit
9 | import EasyCompatible
10 |
11 | public extension EasyExtension where Base: UIScrollView {
12 | /**
13 | 删除一个视图元素
14 |
15 | * parameter element: 要删除的视图,可以是UIView,也可以是视图的唯一标识
16 | * parameter completion: 删除完成后的回调
17 | */
18 | @available(*, deprecated, renamed: "deleteView(_:completion:)", message: "Please use deleteView(_:completion:) instead.")
19 | func delete(_ element: Any, completion: (() -> Void)? = nil) {
20 | let elements = coordinator.elements
21 |
22 | var targetView: UIView?
23 | if let string = element as? String {
24 | targetView = elements.first { $0.identifier == string }?.view
25 | }
26 | if let cell = element as? UITableViewCell {
27 | targetView = coordinator.cells.first { $0.value == cell }?.key
28 | } else if let view = element as? UIView {
29 | targetView = elements.first { $0.view == view.superview }?.view
30 | }
31 | assert(targetView != nil, "invalid element")
32 |
33 | coordinator.elements = coordinator.elements.map {
34 | var tmp = $0
35 | if targetView == tmp.view && !tmp.deleting {
36 | tmp.deleting = true
37 | }
38 | return tmp
39 | }
40 |
41 | if coordinator.onBatchUpdate {
42 | completion?()
43 | return
44 | }
45 |
46 | animateDeletion(completion: {
47 | self.reloadDisposableIfNeed()
48 | completion?()
49 | }, duration: coordinator.animationDuration)
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/Sources/Swift/EasyListDisposable.swift:
--------------------------------------------------------------------------------
1 | //
2 | // EasyListDisposable.swift
3 | // EasyListViewExample
4 | //
5 | // Created by carefree on 2022/10/8.
6 | // Copyright © 2022 carefree. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import EasyCompatible
11 |
12 | public extension EasyExtension where Base: UIScrollView {
13 | /**
14 | 生成自释放元素
15 |
16 | * parameter maker: 闭包
17 |
18 | * returns: 生成的视图
19 | */
20 | func disposableView(with maker: @escaping () -> UIView) -> UIView {
21 | let contentView = EasyListContentView()
22 | var view = maker()
23 | if let cell = view as? UITableViewCell {
24 | view = cell.contentView
25 | coordinator.cells[contentView] = cell
26 | }
27 | view.translatesAutoresizingMaskIntoConstraints = false
28 | contentView.addSubview(view)
29 | contentView.disposableMaker = maker
30 |
31 | return contentView
32 | }
33 |
34 | /**
35 | 刷新数据
36 |
37 | */
38 | func reloadDisposableData() {
39 | coordinator.disposableElements.forEach {
40 | $0.view.subviews.first?.removeFromSuperview()
41 | }
42 | reloadDisposableIfNeed()
43 | }
44 |
45 | /**
46 | 触发自释放机制
47 |
48 | */
49 | func triggerDisposable() {
50 | let scrollView = base
51 |
52 | let offset = scrollView.contentOffset.y
53 | let range = scrollView.frame.height
54 | let minY = offset - range
55 | let maxY = offset + range + range
56 |
57 | coordinator.disposableElements.forEach { element in
58 | let contentView = element.view
59 | let frame = contentView.frame
60 | guard let maker = contentView.disposableMaker else { return }
61 |
62 | if frame.maxY >= minY && frame.minY <= maxY {
63 | //在可视范围内
64 | if contentView.subviews.count == 0 {
65 | //恢复子视图
66 | var view = maker()
67 | if let cell = view as? UITableViewCell {
68 | view = cell.contentView
69 | coordinator.cells[contentView] = cell
70 | }
71 | view.translatesAutoresizingMaskIntoConstraints = false
72 | contentView.addSubview(view)
73 | addConstraint(for: contentView, item1: view, attr1: .leading, item2: contentView, attr2: .leading, constant: element.insets.left)
74 | addConstraint(for: contentView, item1: view, attr1: .trailing, item2: contentView, attr2: .trailing, constant: -element.insets.right)
75 | addConstraint(for: contentView, item1: view, attr1: .top, item2: contentView, attr2: .top, constant: element.insets.top)
76 | addConstraint(for: contentView, item1: view, attr1: .bottom, item2: contentView, attr2: .bottom, constant: -element.insets.bottom)
77 | //删除height约束
78 | contentView.constraints.first { $0.firstAttribute == .height }?.isActive = false
79 | }
80 | } else {
81 | //不在可视范围内
82 | if contentView.subviews.count > 0 {
83 | //移除子视图,回收内存
84 | addConstraint(for: contentView, item1: contentView, attr1: .height, item2: nil, attr2: .notAnAttribute, constant: frame.height)
85 | coordinator.cells.removeValue(forKey: contentView)
86 | contentView.subviews.forEach { $0.removeFromSuperview() }
87 | }
88 | }
89 | }
90 | }
91 |
92 | /**
93 | 获取指定下标的自释放元素
94 |
95 | * parameter index: 下标
96 |
97 | * returns: 找到的视图
98 | */
99 | func getDisposableElement(at index: Int) -> UIView? {
100 | if index >= coordinator.disposableElements.count {
101 | return nil
102 | }
103 | let contentView = coordinator.disposableElements[index].view
104 | if let cell = coordinator.cells[contentView] {
105 | return cell
106 | }
107 | return contentView.subviews.first
108 | }
109 |
110 | /**
111 | 获取所有可视的自释放元素
112 |
113 | * returns: 找到的视图集合
114 | */
115 | var visibleDisposableElements: [UIView] {
116 | return coordinator.disposableElements.compactMap {
117 | let contentView = $0.view
118 | if let cell = coordinator.cells[contentView] {
119 | return cell
120 | }
121 | return contentView.subviews.first
122 | }
123 | }
124 | }
125 |
--------------------------------------------------------------------------------
/Sources/Swift/EasyListView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // EasyListView.swift
3 | // EasyListViewExample
4 | //
5 | // Created by carefree on 2020/6/11.
6 | // Copyright © 2020 carefree. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | @objcMembers
12 | open class EasyListView: UIScrollView {
13 |
14 | private var observation: NSKeyValueObservation?
15 |
16 | // MARK: - Lifecycle
17 | public required init?(coder: NSCoder) {
18 | super.init(coder: coder)
19 | commonInit()
20 | }
21 |
22 | public override init(frame: CGRect) {
23 | super.init(frame: frame)
24 | commonInit()
25 | }
26 |
27 | deinit {
28 | observation?.invalidate()
29 | observation = nil
30 | }
31 |
32 | // MARK: - Private
33 | private func commonInit() {
34 | observation = self.observe(\.contentOffset, options: [.initial, .new]) {[weak self] _, _ in
35 | self?.easy.triggerDisposable()
36 | }
37 | }
38 | }
39 |
40 | internal class EasyListContentView: UIView {
41 |
42 | var disposableMaker: (() -> UIView)? = nil
43 |
44 | }
45 |
--------------------------------------------------------------------------------