├── .gitignore
├── BBSwiftUIKit.podspec
├── BBSwiftUIKit.xcworkspace
├── contents.xcworkspacedata
└── xcshareddata
│ └── IDEWorkspaceChecks.plist
├── BBSwiftUIKit
├── BBSwiftUIKit.xcodeproj
│ ├── project.pbxproj
│ └── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
└── BBSwiftUIKit
│ ├── BBPageControl.swift
│ ├── BBScrollView.swift
│ ├── BBSwiftUIKit.h
│ ├── BBTableView.swift
│ └── Info.plist
├── BBSwiftUIKitDemo
├── BBSwiftUIKitDemo.xcodeproj
│ ├── project.pbxproj
│ └── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
└── BBSwiftUIKitDemo
│ ├── AppDelegate.swift
│ ├── Assets.xcassets
│ ├── AppIcon.appiconset
│ │ └── Contents.json
│ └── Contents.json
│ ├── Base.lproj
│ └── LaunchScreen.storyboard
│ ├── ContentView.swift
│ ├── CycleView.swift
│ ├── CycleViewExample.swift
│ ├── Info.plist
│ ├── PageControlExample.swift
│ ├── Preview Content
│ └── Preview Assets.xcassets
│ │ └── Contents.json
│ ├── SceneDelegate.swift
│ ├── ScrollViewExample.swift
│ ├── ScrollViewExample2.swift
│ ├── ScrollViewExample3.swift
│ ├── ScrollViewExample4.swift
│ └── TableViewExample.swift
├── LICENSE
├── Package.swift
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | #
3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
4 |
5 | ## Build generated
6 | build/
7 | DerivedData/
8 |
9 | ## Various settings
10 | *.pbxuser
11 | !default.pbxuser
12 | *.mode1v3
13 | !default.mode1v3
14 | *.mode2v3
15 | !default.mode2v3
16 | *.perspectivev3
17 | !default.perspectivev3
18 | xcuserdata/
19 |
20 | ## Other
21 | *.moved-aside
22 | *.xccheckout
23 | *.xcscmblueprint
24 |
25 | ## Obj-C/Swift specific
26 | *.hmap
27 | *.ipa
28 | *.dSYM.zip
29 | *.dSYM
30 |
31 | ## Playgrounds
32 | timeline.xctimeline
33 | playground.xcworkspace
34 |
35 | # Swift Package Manager
36 | #
37 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
38 | # Packages/
39 | # Package.pins
40 | # Package.resolved
41 | .build/
42 | .swiftpm
43 |
44 | # CocoaPods
45 |
46 | Pods/
47 |
48 | # Carthage
49 | #
50 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
51 | # Carthage/Checkouts
52 |
53 | Carthage/Build
54 |
55 | # fastlane
56 | #
57 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
58 | # screenshots whenever they are needed.
59 | # For more information about the recommended setup visit:
60 | # https://docs.fastlane.tools/best-practices/source-control/#source-control
61 |
62 | fastlane/report.xml
63 | fastlane/Preview.html
64 | fastlane/screenshots/**/*.png
65 | fastlane/test_output
66 |
67 | # Code Injection
68 | #
69 | # After new code Injection tools there's a generated folder /iOSInjectionProject
70 | # https://github.com/johnno1962/injectionforxcode
71 |
72 | iOSInjectionProject/
73 |
74 | # macOS
75 |
76 | .DS_Store
77 |
--------------------------------------------------------------------------------
/BBSwiftUIKit.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |s|
2 |
3 | s.name = 'BBSwiftUIKit'
4 | s.version = '0.1.0'
5 | s.summary = 'A SwiftUI library with powerful UIKit features.'
6 |
7 | s.description = <<-DESC
8 | Get and set scroll view `contentOffset`.
9 | Set scroll view `isPagingEnabled`, `bounces` and other properties.
10 | Pull down or up table view to refresh data or load more data.
11 | Reload table view data or rows.
12 | Scroll table view to the specific row.
13 | Get and set page control `currentPage`.
14 | DESC
15 |
16 | s.homepage = 'https://github.com/Silence-GitHub/BBSwiftUIKit'
17 |
18 | s.license = { :type => 'MIT', :file => 'LICENSE' }
19 |
20 | s.author = { 'Kaibo Lu' => 'lukaibolkb@gmail.com' }
21 |
22 | s.platform = :ios, '13.0'
23 |
24 | s.swift_version = '5.0'
25 |
26 | s.source = { :git => 'https://github.com/Silence-GitHub/BBSwiftUIKit.git', :tag => s.version }
27 |
28 | s.requires_arc = true
29 |
30 | s.source_files = 'BBSwiftUIKit/BBSwiftUIKit/*.{h,swift}'
31 |
32 | end
33 |
--------------------------------------------------------------------------------
/BBSwiftUIKit.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/BBSwiftUIKit.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/BBSwiftUIKit/BBSwiftUIKit.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 50;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 4A140AE1246B302F00863A16 /* BBSwiftUIKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 4A140ADF246B302F00863A16 /* BBSwiftUIKit.h */; settings = {ATTRIBUTES = (Public, ); }; };
11 | 4A140AE8246B305700863A16 /* BBScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A140AE7246B305700863A16 /* BBScrollView.swift */; };
12 | 4A140B3B246B6CC900863A16 /* BBPageControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A140B3A246B6CC900863A16 /* BBPageControl.swift */; };
13 | 4A9245E9246E0C3200BA48AF /* BBTableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A9245E8246E0C3200BA48AF /* BBTableView.swift */; };
14 | /* End PBXBuildFile section */
15 |
16 | /* Begin PBXFileReference section */
17 | 4A140ADC246B302F00863A16 /* BBSwiftUIKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = BBSwiftUIKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
18 | 4A140ADF246B302F00863A16 /* BBSwiftUIKit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BBSwiftUIKit.h; sourceTree = ""; };
19 | 4A140AE0246B302F00863A16 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
20 | 4A140AE7246B305700863A16 /* BBScrollView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BBScrollView.swift; sourceTree = ""; };
21 | 4A140B3A246B6CC900863A16 /* BBPageControl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BBPageControl.swift; sourceTree = ""; };
22 | 4A9245E8246E0C3200BA48AF /* BBTableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BBTableView.swift; sourceTree = ""; };
23 | /* End PBXFileReference section */
24 |
25 | /* Begin PBXFrameworksBuildPhase section */
26 | 4A140AD9246B302F00863A16 /* Frameworks */ = {
27 | isa = PBXFrameworksBuildPhase;
28 | buildActionMask = 2147483647;
29 | files = (
30 | );
31 | runOnlyForDeploymentPostprocessing = 0;
32 | };
33 | /* End PBXFrameworksBuildPhase section */
34 |
35 | /* Begin PBXGroup section */
36 | 4A140AD2246B302F00863A16 = {
37 | isa = PBXGroup;
38 | children = (
39 | 4A140ADE246B302F00863A16 /* BBSwiftUIKit */,
40 | 4A140ADD246B302F00863A16 /* Products */,
41 | );
42 | sourceTree = "";
43 | };
44 | 4A140ADD246B302F00863A16 /* Products */ = {
45 | isa = PBXGroup;
46 | children = (
47 | 4A140ADC246B302F00863A16 /* BBSwiftUIKit.framework */,
48 | );
49 | name = Products;
50 | sourceTree = "";
51 | };
52 | 4A140ADE246B302F00863A16 /* BBSwiftUIKit */ = {
53 | isa = PBXGroup;
54 | children = (
55 | 4A140ADF246B302F00863A16 /* BBSwiftUIKit.h */,
56 | 4A140AE7246B305700863A16 /* BBScrollView.swift */,
57 | 4A140B3A246B6CC900863A16 /* BBPageControl.swift */,
58 | 4A9245E8246E0C3200BA48AF /* BBTableView.swift */,
59 | 4A140AE0246B302F00863A16 /* Info.plist */,
60 | );
61 | path = BBSwiftUIKit;
62 | sourceTree = "";
63 | };
64 | /* End PBXGroup section */
65 |
66 | /* Begin PBXHeadersBuildPhase section */
67 | 4A140AD7246B302F00863A16 /* Headers */ = {
68 | isa = PBXHeadersBuildPhase;
69 | buildActionMask = 2147483647;
70 | files = (
71 | 4A140AE1246B302F00863A16 /* BBSwiftUIKit.h in Headers */,
72 | );
73 | runOnlyForDeploymentPostprocessing = 0;
74 | };
75 | /* End PBXHeadersBuildPhase section */
76 |
77 | /* Begin PBXNativeTarget section */
78 | 4A140ADB246B302F00863A16 /* BBSwiftUIKit */ = {
79 | isa = PBXNativeTarget;
80 | buildConfigurationList = 4A140AE4246B302F00863A16 /* Build configuration list for PBXNativeTarget "BBSwiftUIKit" */;
81 | buildPhases = (
82 | 4A140AD7246B302F00863A16 /* Headers */,
83 | 4A140AD8246B302F00863A16 /* Sources */,
84 | 4A140AD9246B302F00863A16 /* Frameworks */,
85 | 4A140ADA246B302F00863A16 /* Resources */,
86 | );
87 | buildRules = (
88 | );
89 | dependencies = (
90 | );
91 | name = BBSwiftUIKit;
92 | productName = BBSwiftUIKit;
93 | productReference = 4A140ADC246B302F00863A16 /* BBSwiftUIKit.framework */;
94 | productType = "com.apple.product-type.framework";
95 | };
96 | /* End PBXNativeTarget section */
97 |
98 | /* Begin PBXProject section */
99 | 4A140AD3246B302F00863A16 /* Project object */ = {
100 | isa = PBXProject;
101 | attributes = {
102 | LastUpgradeCheck = 1130;
103 | ORGANIZATIONNAME = "Kaibo Lu";
104 | TargetAttributes = {
105 | 4A140ADB246B302F00863A16 = {
106 | CreatedOnToolsVersion = 11.3;
107 | LastSwiftMigration = 1130;
108 | };
109 | };
110 | };
111 | buildConfigurationList = 4A140AD6246B302F00863A16 /* Build configuration list for PBXProject "BBSwiftUIKit" */;
112 | compatibilityVersion = "Xcode 9.3";
113 | developmentRegion = en;
114 | hasScannedForEncodings = 0;
115 | knownRegions = (
116 | en,
117 | Base,
118 | );
119 | mainGroup = 4A140AD2246B302F00863A16;
120 | productRefGroup = 4A140ADD246B302F00863A16 /* Products */;
121 | projectDirPath = "";
122 | projectRoot = "";
123 | targets = (
124 | 4A140ADB246B302F00863A16 /* BBSwiftUIKit */,
125 | );
126 | };
127 | /* End PBXProject section */
128 |
129 | /* Begin PBXResourcesBuildPhase section */
130 | 4A140ADA246B302F00863A16 /* Resources */ = {
131 | isa = PBXResourcesBuildPhase;
132 | buildActionMask = 2147483647;
133 | files = (
134 | );
135 | runOnlyForDeploymentPostprocessing = 0;
136 | };
137 | /* End PBXResourcesBuildPhase section */
138 |
139 | /* Begin PBXSourcesBuildPhase section */
140 | 4A140AD8246B302F00863A16 /* Sources */ = {
141 | isa = PBXSourcesBuildPhase;
142 | buildActionMask = 2147483647;
143 | files = (
144 | 4A140B3B246B6CC900863A16 /* BBPageControl.swift in Sources */,
145 | 4A9245E9246E0C3200BA48AF /* BBTableView.swift in Sources */,
146 | 4A140AE8246B305700863A16 /* BBScrollView.swift in Sources */,
147 | );
148 | runOnlyForDeploymentPostprocessing = 0;
149 | };
150 | /* End PBXSourcesBuildPhase section */
151 |
152 | /* Begin XCBuildConfiguration section */
153 | 4A140AE2246B302F00863A16 /* Debug */ = {
154 | isa = XCBuildConfiguration;
155 | buildSettings = {
156 | ALWAYS_SEARCH_USER_PATHS = NO;
157 | CLANG_ANALYZER_NONNULL = YES;
158 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
159 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
160 | CLANG_CXX_LIBRARY = "libc++";
161 | CLANG_ENABLE_MODULES = YES;
162 | CLANG_ENABLE_OBJC_ARC = YES;
163 | CLANG_ENABLE_OBJC_WEAK = YES;
164 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
165 | CLANG_WARN_BOOL_CONVERSION = YES;
166 | CLANG_WARN_COMMA = YES;
167 | CLANG_WARN_CONSTANT_CONVERSION = YES;
168 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
169 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
170 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
171 | CLANG_WARN_EMPTY_BODY = YES;
172 | CLANG_WARN_ENUM_CONVERSION = YES;
173 | CLANG_WARN_INFINITE_RECURSION = YES;
174 | CLANG_WARN_INT_CONVERSION = YES;
175 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
176 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
177 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
178 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
179 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
180 | CLANG_WARN_STRICT_PROTOTYPES = YES;
181 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
182 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
183 | CLANG_WARN_UNREACHABLE_CODE = YES;
184 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
185 | COPY_PHASE_STRIP = NO;
186 | CURRENT_PROJECT_VERSION = 1;
187 | DEBUG_INFORMATION_FORMAT = dwarf;
188 | ENABLE_STRICT_OBJC_MSGSEND = YES;
189 | ENABLE_TESTABILITY = YES;
190 | GCC_C_LANGUAGE_STANDARD = gnu11;
191 | GCC_DYNAMIC_NO_PIC = NO;
192 | GCC_NO_COMMON_BLOCKS = YES;
193 | GCC_OPTIMIZATION_LEVEL = 0;
194 | GCC_PREPROCESSOR_DEFINITIONS = (
195 | "DEBUG=1",
196 | "$(inherited)",
197 | );
198 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
199 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
200 | GCC_WARN_UNDECLARED_SELECTOR = YES;
201 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
202 | GCC_WARN_UNUSED_FUNCTION = YES;
203 | GCC_WARN_UNUSED_VARIABLE = YES;
204 | IPHONEOS_DEPLOYMENT_TARGET = 13.0;
205 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
206 | MTL_FAST_MATH = YES;
207 | ONLY_ACTIVE_ARCH = YES;
208 | SDKROOT = iphoneos;
209 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
210 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
211 | VERSIONING_SYSTEM = "apple-generic";
212 | VERSION_INFO_PREFIX = "";
213 | };
214 | name = Debug;
215 | };
216 | 4A140AE3246B302F00863A16 /* Release */ = {
217 | isa = XCBuildConfiguration;
218 | buildSettings = {
219 | ALWAYS_SEARCH_USER_PATHS = NO;
220 | CLANG_ANALYZER_NONNULL = YES;
221 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
222 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
223 | CLANG_CXX_LIBRARY = "libc++";
224 | CLANG_ENABLE_MODULES = YES;
225 | CLANG_ENABLE_OBJC_ARC = YES;
226 | CLANG_ENABLE_OBJC_WEAK = YES;
227 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
228 | CLANG_WARN_BOOL_CONVERSION = YES;
229 | CLANG_WARN_COMMA = YES;
230 | CLANG_WARN_CONSTANT_CONVERSION = YES;
231 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
232 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
233 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
234 | CLANG_WARN_EMPTY_BODY = YES;
235 | CLANG_WARN_ENUM_CONVERSION = YES;
236 | CLANG_WARN_INFINITE_RECURSION = YES;
237 | CLANG_WARN_INT_CONVERSION = YES;
238 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
239 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
240 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
241 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
242 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
243 | CLANG_WARN_STRICT_PROTOTYPES = YES;
244 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
245 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
246 | CLANG_WARN_UNREACHABLE_CODE = YES;
247 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
248 | COPY_PHASE_STRIP = NO;
249 | CURRENT_PROJECT_VERSION = 1;
250 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
251 | ENABLE_NS_ASSERTIONS = NO;
252 | ENABLE_STRICT_OBJC_MSGSEND = YES;
253 | GCC_C_LANGUAGE_STANDARD = gnu11;
254 | GCC_NO_COMMON_BLOCKS = YES;
255 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
256 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
257 | GCC_WARN_UNDECLARED_SELECTOR = YES;
258 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
259 | GCC_WARN_UNUSED_FUNCTION = YES;
260 | GCC_WARN_UNUSED_VARIABLE = YES;
261 | IPHONEOS_DEPLOYMENT_TARGET = 13.0;
262 | MTL_ENABLE_DEBUG_INFO = NO;
263 | MTL_FAST_MATH = YES;
264 | SDKROOT = iphoneos;
265 | SWIFT_COMPILATION_MODE = wholemodule;
266 | SWIFT_OPTIMIZATION_LEVEL = "-O";
267 | VALIDATE_PRODUCT = YES;
268 | VERSIONING_SYSTEM = "apple-generic";
269 | VERSION_INFO_PREFIX = "";
270 | };
271 | name = Release;
272 | };
273 | 4A140AE5246B302F00863A16 /* Debug */ = {
274 | isa = XCBuildConfiguration;
275 | buildSettings = {
276 | CLANG_ENABLE_MODULES = YES;
277 | CODE_SIGN_STYLE = Automatic;
278 | DEFINES_MODULE = YES;
279 | DYLIB_COMPATIBILITY_VERSION = 1;
280 | DYLIB_CURRENT_VERSION = 1;
281 | DYLIB_INSTALL_NAME_BASE = "@rpath";
282 | INFOPLIST_FILE = BBSwiftUIKit/Info.plist;
283 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
284 | LD_RUNPATH_SEARCH_PATHS = (
285 | "$(inherited)",
286 | "@executable_path/Frameworks",
287 | "@loader_path/Frameworks",
288 | );
289 | PRODUCT_BUNDLE_IDENTIFIER = Kaibo.BBSwiftUIKit;
290 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
291 | SKIP_INSTALL = YES;
292 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
293 | SWIFT_VERSION = 5.0;
294 | TARGETED_DEVICE_FAMILY = "1,2";
295 | };
296 | name = Debug;
297 | };
298 | 4A140AE6246B302F00863A16 /* Release */ = {
299 | isa = XCBuildConfiguration;
300 | buildSettings = {
301 | CLANG_ENABLE_MODULES = YES;
302 | CODE_SIGN_STYLE = Automatic;
303 | DEFINES_MODULE = YES;
304 | DYLIB_COMPATIBILITY_VERSION = 1;
305 | DYLIB_CURRENT_VERSION = 1;
306 | DYLIB_INSTALL_NAME_BASE = "@rpath";
307 | INFOPLIST_FILE = BBSwiftUIKit/Info.plist;
308 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
309 | LD_RUNPATH_SEARCH_PATHS = (
310 | "$(inherited)",
311 | "@executable_path/Frameworks",
312 | "@loader_path/Frameworks",
313 | );
314 | PRODUCT_BUNDLE_IDENTIFIER = Kaibo.BBSwiftUIKit;
315 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
316 | SKIP_INSTALL = YES;
317 | SWIFT_VERSION = 5.0;
318 | TARGETED_DEVICE_FAMILY = "1,2";
319 | };
320 | name = Release;
321 | };
322 | /* End XCBuildConfiguration section */
323 |
324 | /* Begin XCConfigurationList section */
325 | 4A140AD6246B302F00863A16 /* Build configuration list for PBXProject "BBSwiftUIKit" */ = {
326 | isa = XCConfigurationList;
327 | buildConfigurations = (
328 | 4A140AE2246B302F00863A16 /* Debug */,
329 | 4A140AE3246B302F00863A16 /* Release */,
330 | );
331 | defaultConfigurationIsVisible = 0;
332 | defaultConfigurationName = Release;
333 | };
334 | 4A140AE4246B302F00863A16 /* Build configuration list for PBXNativeTarget "BBSwiftUIKit" */ = {
335 | isa = XCConfigurationList;
336 | buildConfigurations = (
337 | 4A140AE5246B302F00863A16 /* Debug */,
338 | 4A140AE6246B302F00863A16 /* Release */,
339 | );
340 | defaultConfigurationIsVisible = 0;
341 | defaultConfigurationName = Release;
342 | };
343 | /* End XCConfigurationList section */
344 | };
345 | rootObject = 4A140AD3246B302F00863A16 /* Project object */;
346 | }
347 |
--------------------------------------------------------------------------------
/BBSwiftUIKit/BBSwiftUIKit.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/BBSwiftUIKit/BBSwiftUIKit.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/BBSwiftUIKit/BBSwiftUIKit/BBPageControl.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BBPageControl.swift
3 | // BBSwiftUIKit
4 | //
5 | // Created by Kaibo Lu on 5/12/20.
6 | // Copyright © 2020 Kaibo Lu. All rights reserved.
7 | //
8 |
9 | import SwiftUI
10 |
11 | public extension BBPageControl {
12 | func bb_hidesForSinglePage(_ hidesForSinglePage: Bool) -> BBPageControl {
13 | var view = self
14 | view.hidesForSinglePage = hidesForSinglePage
15 | return view
16 | }
17 |
18 | func bb_pageIndicatorTintColor(_ pageIndicatorTintColor: UIColor?) -> BBPageControl {
19 | var view = self
20 | view.pageIndicatorTintColor = pageIndicatorTintColor
21 | return view
22 | }
23 |
24 | func bb_currentPageIndicatorTintColor(_ currentPageIndicatorTintColor: UIColor?) -> BBPageControl {
25 | var view = self
26 | view.currentPageIndicatorTintColor = currentPageIndicatorTintColor
27 | return view
28 | }
29 | }
30 |
31 | public struct BBPageControl: UIViewRepresentable {
32 | @Binding var currentPage: Int
33 | var numberOfPages: Int
34 | var hidesForSinglePage: Bool = false
35 | var pageIndicatorTintColor: UIColor?
36 | var currentPageIndicatorTintColor: UIColor?
37 |
38 | public init(currentPage: Binding, numberOfPages: Int) {
39 | self._currentPage = currentPage
40 | self.numberOfPages = numberOfPages
41 | }
42 |
43 | public func makeUIView(context: Context) -> UIPageControl {
44 | let control = UIPageControl()
45 | control.addTarget(context.coordinator, action: #selector(Coordinator.updateCurrentPage(_:)), for: .valueChanged)
46 | return control
47 | }
48 |
49 | public func updateUIView(_ control: UIPageControl, context: Context) {
50 | control.numberOfPages = numberOfPages
51 | control.currentPage = currentPage // Set `currentPage` after `numberOfPages`
52 | control.hidesForSinglePage = hidesForSinglePage
53 | control.pageIndicatorTintColor = pageIndicatorTintColor
54 | control.currentPageIndicatorTintColor = currentPageIndicatorTintColor
55 | }
56 |
57 | public func makeCoordinator() -> Coordinator { Coordinator(self) }
58 |
59 | public class Coordinator: NSObject {
60 | let parent: BBPageControl
61 |
62 | init(_ view: BBPageControl) { parent = view }
63 |
64 | @objc func updateCurrentPage(_ sender: UIPageControl) {
65 | parent.currentPage = sender.currentPage
66 | }
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/BBSwiftUIKit/BBSwiftUIKit/BBScrollView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BBScrollView.swift
3 | // BBSwiftUIKit
4 | //
5 | // Created by Kaibo Lu on 4/19/20.
6 | // Copyright © 2020 Kaibo Lu. All rights reserved.
7 | //
8 |
9 | import SwiftUI
10 |
11 | public protocol BBUIScrollViewRepresentable {
12 | var contentOffset: CGPoint { get set }
13 | var contentOffsetToScrollAnimated: CGPoint? { get set }
14 | var isPagingEnabled: Bool { get set }
15 | var bounces: Bool { get set }
16 | var alwaysBounceVertical: Bool { get set }
17 | var alwaysBounceHorizontal: Bool { get set }
18 | var showsVerticalScrollIndicator: Bool { get set }
19 | var showsHorizontalScrollIndicator: Bool { get set }
20 |
21 | func updateScrollView(_ scrollView: UIScrollView)
22 | }
23 |
24 | public extension CGPoint {
25 | static let bb_invalidContentOffset = CGPoint(x: CGFloat.greatestFiniteMagnitude, y: CGFloat.greatestFiniteMagnitude)
26 | }
27 |
28 | public extension BBUIScrollViewRepresentable {
29 | func bb_isPagingEnabled(_ isPagingEnabled: Bool) -> Self {
30 | var view = self
31 | view.isPagingEnabled = isPagingEnabled
32 | return view
33 | }
34 |
35 | func bb_bounces(_ bounces: Bool) -> Self {
36 | var view = self
37 | view.bounces = bounces
38 | return view
39 | }
40 |
41 | func bb_alwaysBounceVertical(_ alwaysBounceVertical: Bool) -> Self {
42 | var view = self
43 | view.alwaysBounceVertical = alwaysBounceVertical
44 | return view
45 | }
46 |
47 | func bb_alwaysBounceHorizontal(_ alwaysBounceHorizontal: Bool) -> Self {
48 | var view = self
49 | view.alwaysBounceHorizontal = alwaysBounceHorizontal
50 | return view
51 | }
52 |
53 | func bb_showsVerticalScrollIndicator(_ showsVerticalScrollIndicator: Bool) -> Self {
54 | var view = self
55 | view.showsVerticalScrollIndicator = showsVerticalScrollIndicator
56 | return view
57 | }
58 |
59 | func bb_showsHorizontalScrollIndicator(_ showsHorizontalScrollIndicator: Bool) -> Self {
60 | var view = self
61 | view.showsHorizontalScrollIndicator = showsHorizontalScrollIndicator
62 | return view
63 | }
64 |
65 | func updateScrollView(_ scrollView: UIScrollView) {
66 | scrollView.isPagingEnabled = isPagingEnabled
67 | scrollView.bounces = bounces
68 | scrollView.alwaysBounceVertical = alwaysBounceVertical
69 | scrollView.alwaysBounceHorizontal = alwaysBounceHorizontal
70 | scrollView.showsVerticalScrollIndicator = showsVerticalScrollIndicator
71 | scrollView.showsHorizontalScrollIndicator = showsHorizontalScrollIndicator
72 | }
73 | }
74 |
75 | public extension BBScrollView {
76 | func bb_contentOffset(_ contentOffset: Binding) -> Self {
77 | var view = self
78 | view._contentOffset = contentOffset
79 | return view
80 | }
81 |
82 | func bb_contentOffsetToScrollAnimated(_ contentOffsetToScrollAnimated: Binding) -> Self {
83 | var view = self
84 | view._contentOffsetToScrollAnimated = contentOffsetToScrollAnimated
85 | return view
86 | }
87 | }
88 |
89 | public struct BBScrollView: UIViewRepresentable, BBUIScrollViewRepresentable {
90 | public let axis: Axis.Set
91 | @Binding public var contentOffset: CGPoint
92 | @Binding public var contentOffsetToScrollAnimated: CGPoint?
93 | public var isPagingEnabled: Bool = false
94 | public var bounces: Bool = true
95 | public var alwaysBounceVertical: Bool = false
96 | public var alwaysBounceHorizontal: Bool = false
97 | public var showsVerticalScrollIndicator: Bool = true
98 | public var showsHorizontalScrollIndicator: Bool = true
99 | public let content: () -> Content
100 |
101 | public init(_ axis: Axis.Set,
102 | contentOffset: Binding = .constant(.bb_invalidContentOffset),
103 | @ViewBuilder content: @escaping () -> Content)
104 | {
105 | self.axis = axis
106 | self._contentOffset = contentOffset
107 | self._contentOffsetToScrollAnimated = .constant(nil)
108 | self.content = content
109 | }
110 |
111 | public func makeUIView(context: Context) -> UIScrollView {
112 | let scrollView = _BBScrollView(axis)
113 | scrollView.delegate = context.coordinator
114 |
115 | let host = UIHostingController(rootView: content())
116 | host.view.translatesAutoresizingMaskIntoConstraints = false
117 | context.coordinator.host = host
118 |
119 | scrollView.addSubview(host.view)
120 |
121 | if axis.contains(.horizontal) && axis.contains(.vertical) {
122 | NSLayoutConstraint.activate([
123 | scrollView.leftAnchor.constraint(equalTo: host.view.leftAnchor),
124 | scrollView.rightAnchor.constraint(equalTo: host.view.rightAnchor),
125 | scrollView.topAnchor.constraint(equalTo: host.view.topAnchor),
126 | scrollView.bottomAnchor.constraint(equalTo: host.view.bottomAnchor),
127 | ])
128 | } else if axis.contains(.horizontal) {
129 | NSLayoutConstraint.activate([ scrollView.centerYAnchor.constraint(equalTo: host.view.centerYAnchor) ])
130 | scrollView.setContentHuggingPriority(.defaultHigh, for: .vertical)
131 | } else if axis.contains(.vertical) {
132 | NSLayoutConstraint.activate([ scrollView.centerXAnchor.constraint(equalTo: host.view.centerXAnchor) ])
133 | scrollView.setContentHuggingPriority(.defaultHigh, for: .horizontal)
134 | } else {
135 | assertionFailure("No axis for BBScrollView")
136 | }
137 | return scrollView
138 | }
139 |
140 | public func updateUIView(_ scrollView: UIScrollView, context: Context) {
141 | if let contentOffset = contentOffsetToScrollAnimated {
142 | scrollView.setContentOffset(contentOffset, animated: true)
143 | DispatchQueue.main.async {
144 | self.contentOffsetToScrollAnimated = nil
145 | }
146 | } else if contentOffset != .bb_invalidContentOffset {
147 | scrollView.contentOffset = contentOffset
148 | }
149 |
150 | updateScrollView(scrollView)
151 |
152 | let host = context.coordinator.host!
153 | host.rootView = content()
154 | host.view.setNeedsUpdateConstraints()
155 | scrollView.layoutIfNeeded()
156 | scrollView.contentSize = host.view.frame.size
157 | }
158 |
159 | public func makeCoordinator() -> BBScrollView.Coordinator {
160 | Coordinator(self)
161 | }
162 |
163 | public class Coordinator: NSObject, UIScrollViewDelegate {
164 | let parent: BBScrollView
165 | var host: UIHostingController!
166 |
167 | init(_ view: BBScrollView) { parent = view }
168 |
169 | // MARK: UIScrollViewDelegate
170 |
171 | public func scrollViewDidScroll(_ scrollView: UIScrollView) {
172 | DispatchQueue.main.async { [weak self] in
173 | guard let self = self else { return }
174 | self.parent.contentOffset = scrollView.contentOffset
175 | }
176 | }
177 | }
178 | }
179 |
180 | private class _BBScrollView: UIScrollView {
181 | let axis: Axis.Set
182 |
183 | init(_ axis: Axis.Set) {
184 | self.axis = axis
185 | super.init(frame: .zero)
186 | }
187 |
188 | required init?(coder: NSCoder) {
189 | fatalError("init(coder:) has not been implemented")
190 | }
191 |
192 | override var intrinsicContentSize: CGSize {
193 | if axis.contains(.horizontal) && !axis.contains(.vertical) {
194 | return CGSize(width: UIView.noIntrinsicMetric, height: contentSize.height)
195 | }
196 | if axis.contains(.vertical) && !axis.contains(.horizontal) {
197 | return CGSize(width: contentSize.width, height: UIView.noIntrinsicMetric)
198 | }
199 | return CGSize(width: UIView.noIntrinsicMetric, height: UIView.noIntrinsicMetric)
200 | }
201 | }
202 |
--------------------------------------------------------------------------------
/BBSwiftUIKit/BBSwiftUIKit/BBSwiftUIKit.h:
--------------------------------------------------------------------------------
1 | //
2 | // BBSwiftUIKit.h
3 | // BBSwiftUIKit
4 | //
5 | // Created by Kaibo Lu on 5/12/20.
6 | // Copyright © 2020 Kaibo Lu. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | //! Project version number for BBSwiftUIKit.
12 | FOUNDATION_EXPORT double BBSwiftUIKitVersionNumber;
13 |
14 | //! Project version string for BBSwiftUIKit.
15 | FOUNDATION_EXPORT const unsigned char BBSwiftUIKitVersionString[];
16 |
17 | // In this header, you should import all the public headers of your framework using statements like #import
18 |
19 |
20 |
--------------------------------------------------------------------------------
/BBSwiftUIKit/BBSwiftUIKit/BBTableView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BBTableView.swift
3 | // BBSwiftUIKit
4 | //
5 | // Created by Kaibo Lu on 5/14/20.
6 | // Copyright © 2020 Kaibo Lu. All rights reserved.
7 | //
8 |
9 | import SwiftUI
10 |
11 | public extension CGFloat {
12 | static let bb_invalidBottomSpaceForLoadingMore = CGFloat.greatestFiniteMagnitude
13 | }
14 |
15 | public extension BBTableView {
16 | func bb_reloadData(_ reloadData: Binding) -> Self {
17 | var view = self
18 | view._reloadData = reloadData
19 | return view
20 | }
21 |
22 | func bb_reloadRows(_ rows: Binding<[Int]>, animation: UITableView.RowAnimation) -> Self {
23 | var view = self
24 | view._reloadRows = rows
25 | view.reloadRowsAnimation = animation
26 | return view
27 | }
28 |
29 | func bb_scrollToRow(_ row: Binding, position: UITableView.ScrollPosition, animated: Bool) -> Self {
30 | var view = self
31 | view._scrollToRow = row
32 | view.scrollToRowPosition = position
33 | view.scrollToRowAnimated = animated
34 | return view
35 | }
36 |
37 | func bb_contentOffset(_ contentOffset: Binding) -> Self {
38 | var view = self
39 | view._contentOffset = contentOffset
40 | return view
41 | }
42 |
43 | func bb_contentOffsetToScrollAnimated(_ contentOffsetToScrollAnimated: Binding) -> Self {
44 | var view = self
45 | view._contentOffsetToScrollAnimated = contentOffsetToScrollAnimated
46 | return view
47 | }
48 |
49 | func bb_setupRefreshControl(_ setupRefreshControl: @escaping (UIRefreshControl) -> Void) -> Self {
50 | var view = self
51 | view.setupRefreshControl = setupRefreshControl
52 | return view
53 | }
54 |
55 | func bb_pullDownToRefresh(isRefreshing: Binding, refresh: @escaping () -> Void) -> Self {
56 | var view = self
57 | view._isRefreshing = isRefreshing
58 | view.pullDownToRefresh = refresh
59 | return view
60 | }
61 |
62 | func bb_pullUpToLoadMore(bottomSpace: CGFloat, loadMore: @escaping () -> Void) -> Self {
63 | var view = self
64 | view.bottomSpaceForLoadingMore = bottomSpace
65 | view.pullUpToLoadMore = loadMore
66 | return view
67 | }
68 |
69 | func bb_setupTableView(_ setupTableView: @escaping (UITableView) -> Void) -> Self {
70 | var view = self
71 | view.setupTableView = setupTableView
72 | return view
73 | }
74 |
75 | func bb_updateTableView(_ updateTableView: @escaping (UITableView) -> Void) -> Self {
76 | var view = self
77 | view.updateTableView = updateTableView
78 | return view
79 | }
80 | }
81 |
82 | public struct BBTableView: UIViewControllerRepresentable, BBUIScrollViewRepresentable where Data : RandomAccessCollection, Content : View, Data.Element : Equatable {
83 | let data: Data
84 | let content: (Data.Element) -> Content
85 |
86 | @Binding public var reloadData: Bool
87 |
88 | @Binding public var reloadRows: [Int]
89 | public var reloadRowsAnimation: UITableView.RowAnimation = .automatic
90 |
91 | @Binding public var scrollToRow: Int?
92 | public var scrollToRowPosition: UITableView.ScrollPosition = .none
93 | public var scrollToRowAnimated: Bool = true
94 |
95 | @Binding public var contentOffset: CGPoint
96 | @Binding public var contentOffsetToScrollAnimated: CGPoint?
97 | public var isPagingEnabled: Bool = false
98 | public var bounces: Bool = true
99 | public var alwaysBounceVertical: Bool = false
100 | public var alwaysBounceHorizontal: Bool = false
101 | public var showsVerticalScrollIndicator: Bool = true
102 | public var showsHorizontalScrollIndicator: Bool = true
103 |
104 | @Binding public var isRefreshing: Bool
105 | public var setupRefreshControl: ((UIRefreshControl) -> Void)?
106 | public var pullDownToRefresh: (() -> Void)?
107 | public var bottomSpaceForLoadingMore: CGFloat = .bb_invalidBottomSpaceForLoadingMore
108 | public var pullUpToLoadMore: (() -> Void)?
109 |
110 | public var setupTableView: ((UITableView) -> Void)?
111 | public var updateTableView: ((UITableView) -> Void)?
112 |
113 | public init(_ data: Data, @ViewBuilder content: @escaping (Data.Element) -> Content) {
114 | self.data = data
115 | self.content = content
116 | self._reloadData = .constant(false)
117 | self._reloadRows = .constant([])
118 | self._scrollToRow = .constant(nil)
119 | self._contentOffset = .constant(.bb_invalidContentOffset)
120 | self._contentOffsetToScrollAnimated = .constant(nil)
121 | self._isRefreshing = .constant(false)
122 | }
123 |
124 | public func makeUIViewController(context: Context) -> UIViewController {
125 | _BBTableViewController(self)
126 | }
127 |
128 | public func updateUIViewController(_ viewController: UIViewController, context: Context) {
129 | let vc = viewController as! _BBTableViewController
130 | updateScrollView(vc.tableView)
131 | vc.update(self)
132 | }
133 | }
134 |
135 | private class _BBTableViewController: UIViewController, UITableViewDataSource, UITableViewDelegate where Data: RandomAccessCollection, Content: View, Data.Element: Equatable {
136 | var representable: BBTableView
137 | var tableView: UITableView!
138 |
139 | var data: Data { representable.data }
140 | var content: (Data.Element) -> Content { representable.content }
141 |
142 | init(_ view: BBTableView) {
143 | representable = view
144 | super.init(nibName: nil, bundle: nil)
145 | }
146 |
147 | required init?(coder: NSCoder) {
148 | fatalError("init(coder:) has not been implemented")
149 | }
150 |
151 | override func viewDidLoad() {
152 | super.viewDidLoad()
153 |
154 | tableView = UITableView()
155 | tableView.translatesAutoresizingMaskIntoConstraints = false
156 | tableView.register(_BBTableViewHostCell.self, forCellReuseIdentifier: "cell")
157 | tableView.separatorStyle = .none
158 | tableView.dataSource = self
159 | tableView.delegate = self
160 | view.addSubview(tableView)
161 |
162 | if representable.setupRefreshControl != nil || representable.pullDownToRefresh != nil {
163 | let refreshControl = UIRefreshControl()
164 | representable.setupRefreshControl?(refreshControl)
165 | refreshControl.addTarget(self, action: #selector(pullDownToRefresh), for: .valueChanged)
166 | tableView.refreshControl = refreshControl
167 | }
168 |
169 | NSLayoutConstraint.activate([
170 | view.leftAnchor.constraint(equalTo: tableView.leftAnchor),
171 | view.rightAnchor.constraint(equalTo: tableView.rightAnchor),
172 | view.topAnchor.constraint(equalTo: tableView.topAnchor),
173 | view.bottomAnchor.constraint(equalTo: tableView.bottomAnchor)
174 | ])
175 |
176 | representable.setupTableView?(tableView)
177 | }
178 |
179 | func update(_ newRepresentable: BBTableView) {
180 | if newRepresentable.reloadData {
181 | representable = newRepresentable
182 | tableView.reloadData()
183 |
184 | DispatchQueue.main.async { [weak self] in
185 | guard let self = self else { return }
186 | self.representable.reloadData = false
187 | self.representable.reloadRows.removeAll()
188 | }
189 | } else {
190 | var removals: [IndexPath] = []
191 | var insertions: [IndexPath] = []
192 | let diff = newRepresentable.data.difference(from: data)
193 | for step in diff {
194 | switch step {
195 | case let .remove(i, _, _): removals.append(IndexPath(row: i, section: 0))
196 | case let .insert(i, _, _): insertions.append(IndexPath(row: i, section: 0))
197 | }
198 | }
199 |
200 | representable = newRepresentable
201 |
202 | if !removals.isEmpty || !insertions.isEmpty {
203 | tableView.performBatchUpdates({
204 | tableView.deleteRows(at: removals, with: .automatic)
205 | tableView.insertRows(at: insertions, with: .automatic)
206 | }, completion: nil)
207 | }
208 |
209 | if !representable.reloadRows.isEmpty {
210 | tableView.reloadRows(at: representable.reloadRows.map { IndexPath(row: $0, section: 0) }, with: representable.reloadRowsAnimation)
211 |
212 | DispatchQueue.main.async { [weak self] in
213 | guard let self = self else { return }
214 | self.representable.reloadRows.removeAll()
215 | }
216 | }
217 | }
218 |
219 | if let refreshControl = tableView.refreshControl,
220 | representable.isRefreshing != refreshControl.isRefreshing {
221 | if representable.isRefreshing {
222 | refreshControl.beginRefreshing()
223 | } else {
224 | refreshControl.endRefreshing()
225 | }
226 | }
227 |
228 | representable.updateTableView?(tableView)
229 |
230 | if let row = representable.scrollToRow {
231 | tableView.scrollToRow(at: IndexPath(row: row, section: 0), at: representable.scrollToRowPosition, animated: representable.scrollToRowAnimated)
232 | DispatchQueue.main.async { [weak self] in
233 | guard let self = self else { return }
234 | self.representable.scrollToRow = nil
235 | }
236 | } else if let contentOffset = representable.contentOffsetToScrollAnimated {
237 | tableView.setContentOffset(contentOffset, animated: true)
238 | DispatchQueue.main.async { [weak self] in
239 | guard let self = self else { return }
240 | self.representable.contentOffsetToScrollAnimated = nil
241 | }
242 | } else if representable.contentOffset != .bb_invalidContentOffset {
243 | tableView.contentOffset = representable.contentOffset
244 | }
245 | }
246 |
247 | @objc private func pullDownToRefresh() {
248 | representable.isRefreshing = true
249 | representable.pullDownToRefresh?()
250 | }
251 |
252 | // MARK: UITableViewDataSource
253 |
254 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { data.count }
255 |
256 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
257 | let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! _BBTableViewHostCell
258 | let index = data.index(data.startIndex, offsetBy: indexPath.row)
259 | let view = content(data[index])
260 | cell.update(view, parent: self)
261 | return cell
262 | }
263 |
264 | // MARK: UITableViewDelegate
265 |
266 | func scrollViewDidScroll(_ scrollView: UIScrollView) {
267 | DispatchQueue.main.async { [weak self] in
268 | guard let self = self else { return }
269 | self.representable.contentOffset = scrollView.contentOffset
270 |
271 | if self.representable.bottomSpaceForLoadingMore != .bb_invalidBottomSpaceForLoadingMore,
272 | let loadMore = self.representable.pullUpToLoadMore {
273 | let space = scrollView.contentSize.height - scrollView.contentOffset.y - scrollView.frame.height
274 | if space <= self.representable.bottomSpaceForLoadingMore {
275 | loadMore()
276 | }
277 | }
278 | }
279 | }
280 | }
281 |
282 | private class _BBTableViewHostCell: UITableViewCell {
283 | var host: UIHostingController!
284 |
285 | override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
286 | super.init(style: style, reuseIdentifier: reuseIdentifier)
287 |
288 | selectionStyle = .none
289 | }
290 |
291 | required init?(coder: NSCoder) {
292 | fatalError("init(coder:) has not been implemented")
293 | }
294 |
295 | func update(_ content: Content, parent: UIViewController) {
296 | if host == nil {
297 | host = UIHostingController(rootView: content)
298 | parent.addChild(host)
299 |
300 | host.view.translatesAutoresizingMaskIntoConstraints = false
301 | contentView.addSubview(host.view)
302 |
303 | NSLayoutConstraint.activate([
304 | contentView.leftAnchor.constraint(equalTo: host.view.leftAnchor),
305 | contentView.rightAnchor.constraint(equalTo: host.view.rightAnchor),
306 | contentView.topAnchor.constraint(equalTo: host.view.topAnchor),
307 | contentView.bottomAnchor.constraint(equalTo: host.view.bottomAnchor)
308 | ])
309 |
310 | host.didMove(toParent: parent)
311 | } else {
312 | host.rootView = content
313 | }
314 | host.view.setNeedsUpdateConstraints()
315 | }
316 | }
317 |
--------------------------------------------------------------------------------
/BBSwiftUIKit/BBSwiftUIKit/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 | $(CURRENT_PROJECT_VERSION)
21 |
22 |
23 |
--------------------------------------------------------------------------------
/BBSwiftUIKitDemo/BBSwiftUIKitDemo.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 50;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 4A140B16246B317600863A16 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A140B15246B317600863A16 /* AppDelegate.swift */; };
11 | 4A140B18246B317600863A16 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A140B17246B317600863A16 /* SceneDelegate.swift */; };
12 | 4A140B1C246B317700863A16 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4A140B1B246B317700863A16 /* Assets.xcassets */; };
13 | 4A140B1F246B317700863A16 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4A140B1E246B317700863A16 /* Preview Assets.xcassets */; };
14 | 4A140B22246B317700863A16 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4A140B20246B317700863A16 /* LaunchScreen.storyboard */; };
15 | 4A140B2F246B318A00863A16 /* ScrollViewExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A140B29246B318A00863A16 /* ScrollViewExample.swift */; };
16 | 4A140B30246B318A00863A16 /* ScrollViewExample2.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A140B2A246B318A00863A16 /* ScrollViewExample2.swift */; };
17 | 4A140B31246B318A00863A16 /* ScrollViewExample3.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A140B2B246B318A00863A16 /* ScrollViewExample3.swift */; };
18 | 4A140B32246B318A00863A16 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A140B2C246B318A00863A16 /* ContentView.swift */; };
19 | 4A140B33246B318A00863A16 /* ScrollViewExample4.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A140B2D246B318A00863A16 /* ScrollViewExample4.swift */; };
20 | 4A140B34246B318A00863A16 /* CycleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A140B2E246B318A00863A16 /* CycleView.swift */; };
21 | 4A140B37246B31F800863A16 /* BBSwiftUIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4A140B36246B31F800863A16 /* BBSwiftUIKit.framework */; };
22 | 4A140B38246B31F800863A16 /* BBSwiftUIKit.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 4A140B36246B31F800863A16 /* BBSwiftUIKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
23 | 4A140B3D246B745C00863A16 /* CycleViewExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A140B3C246B745C00863A16 /* CycleViewExample.swift */; };
24 | 4A140B3F246B7CFD00863A16 /* PageControlExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A140B3E246B7CFD00863A16 /* PageControlExample.swift */; };
25 | 4A9245EB246E11EC00BA48AF /* TableViewExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A9245EA246E11EC00BA48AF /* TableViewExample.swift */; };
26 | /* End PBXBuildFile section */
27 |
28 | /* Begin PBXCopyFilesBuildPhase section */
29 | 4A140B39246B31F800863A16 /* Embed Frameworks */ = {
30 | isa = PBXCopyFilesBuildPhase;
31 | buildActionMask = 2147483647;
32 | dstPath = "";
33 | dstSubfolderSpec = 10;
34 | files = (
35 | 4A140B38246B31F800863A16 /* BBSwiftUIKit.framework in Embed Frameworks */,
36 | );
37 | name = "Embed Frameworks";
38 | runOnlyForDeploymentPostprocessing = 0;
39 | };
40 | /* End PBXCopyFilesBuildPhase section */
41 |
42 | /* Begin PBXFileReference section */
43 | 4A140B12246B317600863A16 /* BBSwiftUIKitDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = BBSwiftUIKitDemo.app; sourceTree = BUILT_PRODUCTS_DIR; };
44 | 4A140B15246B317600863A16 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
45 | 4A140B17246B317600863A16 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; };
46 | 4A140B1B246B317700863A16 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
47 | 4A140B1E246B317700863A16 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; };
48 | 4A140B21246B317700863A16 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
49 | 4A140B23246B317700863A16 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
50 | 4A140B29246B318A00863A16 /* ScrollViewExample.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScrollViewExample.swift; sourceTree = ""; };
51 | 4A140B2A246B318A00863A16 /* ScrollViewExample2.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScrollViewExample2.swift; sourceTree = ""; };
52 | 4A140B2B246B318A00863A16 /* ScrollViewExample3.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScrollViewExample3.swift; sourceTree = ""; };
53 | 4A140B2C246B318A00863A16 /* ContentView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; };
54 | 4A140B2D246B318A00863A16 /* ScrollViewExample4.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScrollViewExample4.swift; sourceTree = ""; };
55 | 4A140B2E246B318A00863A16 /* CycleView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CycleView.swift; sourceTree = ""; };
56 | 4A140B36246B31F800863A16 /* BBSwiftUIKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = BBSwiftUIKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
57 | 4A140B3C246B745C00863A16 /* CycleViewExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CycleViewExample.swift; sourceTree = ""; };
58 | 4A140B3E246B7CFD00863A16 /* PageControlExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageControlExample.swift; sourceTree = ""; };
59 | 4A9245EA246E11EC00BA48AF /* TableViewExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TableViewExample.swift; sourceTree = ""; };
60 | /* End PBXFileReference section */
61 |
62 | /* Begin PBXFrameworksBuildPhase section */
63 | 4A140B0F246B317600863A16 /* Frameworks */ = {
64 | isa = PBXFrameworksBuildPhase;
65 | buildActionMask = 2147483647;
66 | files = (
67 | 4A140B37246B31F800863A16 /* BBSwiftUIKit.framework in Frameworks */,
68 | );
69 | runOnlyForDeploymentPostprocessing = 0;
70 | };
71 | /* End PBXFrameworksBuildPhase section */
72 |
73 | /* Begin PBXGroup section */
74 | 4A140B09246B317600863A16 = {
75 | isa = PBXGroup;
76 | children = (
77 | 4A140B14246B317600863A16 /* BBSwiftUIKitDemo */,
78 | 4A140B13246B317600863A16 /* Products */,
79 | 4A140B35246B31F800863A16 /* Frameworks */,
80 | );
81 | sourceTree = "";
82 | };
83 | 4A140B13246B317600863A16 /* Products */ = {
84 | isa = PBXGroup;
85 | children = (
86 | 4A140B12246B317600863A16 /* BBSwiftUIKitDemo.app */,
87 | );
88 | name = Products;
89 | sourceTree = "";
90 | };
91 | 4A140B14246B317600863A16 /* BBSwiftUIKitDemo */ = {
92 | isa = PBXGroup;
93 | children = (
94 | 4A140B15246B317600863A16 /* AppDelegate.swift */,
95 | 4A140B17246B317600863A16 /* SceneDelegate.swift */,
96 | 4A140B2C246B318A00863A16 /* ContentView.swift */,
97 | 4A140B29246B318A00863A16 /* ScrollViewExample.swift */,
98 | 4A140B2A246B318A00863A16 /* ScrollViewExample2.swift */,
99 | 4A140B2B246B318A00863A16 /* ScrollViewExample3.swift */,
100 | 4A140B2D246B318A00863A16 /* ScrollViewExample4.swift */,
101 | 4A140B3E246B7CFD00863A16 /* PageControlExample.swift */,
102 | 4A140B3C246B745C00863A16 /* CycleViewExample.swift */,
103 | 4A140B2E246B318A00863A16 /* CycleView.swift */,
104 | 4A9245EA246E11EC00BA48AF /* TableViewExample.swift */,
105 | 4A140B1B246B317700863A16 /* Assets.xcassets */,
106 | 4A140B20246B317700863A16 /* LaunchScreen.storyboard */,
107 | 4A140B23246B317700863A16 /* Info.plist */,
108 | 4A140B1D246B317700863A16 /* Preview Content */,
109 | );
110 | path = BBSwiftUIKitDemo;
111 | sourceTree = "";
112 | };
113 | 4A140B1D246B317700863A16 /* Preview Content */ = {
114 | isa = PBXGroup;
115 | children = (
116 | 4A140B1E246B317700863A16 /* Preview Assets.xcassets */,
117 | );
118 | path = "Preview Content";
119 | sourceTree = "";
120 | };
121 | 4A140B35246B31F800863A16 /* Frameworks */ = {
122 | isa = PBXGroup;
123 | children = (
124 | 4A140B36246B31F800863A16 /* BBSwiftUIKit.framework */,
125 | );
126 | name = Frameworks;
127 | sourceTree = "";
128 | };
129 | /* End PBXGroup section */
130 |
131 | /* Begin PBXNativeTarget section */
132 | 4A140B11246B317600863A16 /* BBSwiftUIKitDemo */ = {
133 | isa = PBXNativeTarget;
134 | buildConfigurationList = 4A140B26246B317700863A16 /* Build configuration list for PBXNativeTarget "BBSwiftUIKitDemo" */;
135 | buildPhases = (
136 | 4A140B0E246B317600863A16 /* Sources */,
137 | 4A140B0F246B317600863A16 /* Frameworks */,
138 | 4A140B10246B317600863A16 /* Resources */,
139 | 4A140B39246B31F800863A16 /* Embed Frameworks */,
140 | );
141 | buildRules = (
142 | );
143 | dependencies = (
144 | );
145 | name = BBSwiftUIKitDemo;
146 | productName = BBSwiftUIKitDemo;
147 | productReference = 4A140B12246B317600863A16 /* BBSwiftUIKitDemo.app */;
148 | productType = "com.apple.product-type.application";
149 | };
150 | /* End PBXNativeTarget section */
151 |
152 | /* Begin PBXProject section */
153 | 4A140B0A246B317600863A16 /* Project object */ = {
154 | isa = PBXProject;
155 | attributes = {
156 | LastSwiftUpdateCheck = 1130;
157 | LastUpgradeCheck = 1130;
158 | ORGANIZATIONNAME = "Kaibo Lu";
159 | TargetAttributes = {
160 | 4A140B11246B317600863A16 = {
161 | CreatedOnToolsVersion = 11.3;
162 | };
163 | };
164 | };
165 | buildConfigurationList = 4A140B0D246B317600863A16 /* Build configuration list for PBXProject "BBSwiftUIKitDemo" */;
166 | compatibilityVersion = "Xcode 9.3";
167 | developmentRegion = en;
168 | hasScannedForEncodings = 0;
169 | knownRegions = (
170 | en,
171 | Base,
172 | );
173 | mainGroup = 4A140B09246B317600863A16;
174 | productRefGroup = 4A140B13246B317600863A16 /* Products */;
175 | projectDirPath = "";
176 | projectRoot = "";
177 | targets = (
178 | 4A140B11246B317600863A16 /* BBSwiftUIKitDemo */,
179 | );
180 | };
181 | /* End PBXProject section */
182 |
183 | /* Begin PBXResourcesBuildPhase section */
184 | 4A140B10246B317600863A16 /* Resources */ = {
185 | isa = PBXResourcesBuildPhase;
186 | buildActionMask = 2147483647;
187 | files = (
188 | 4A140B22246B317700863A16 /* LaunchScreen.storyboard in Resources */,
189 | 4A140B1F246B317700863A16 /* Preview Assets.xcassets in Resources */,
190 | 4A140B1C246B317700863A16 /* Assets.xcassets in Resources */,
191 | );
192 | runOnlyForDeploymentPostprocessing = 0;
193 | };
194 | /* End PBXResourcesBuildPhase section */
195 |
196 | /* Begin PBXSourcesBuildPhase section */
197 | 4A140B0E246B317600863A16 /* Sources */ = {
198 | isa = PBXSourcesBuildPhase;
199 | buildActionMask = 2147483647;
200 | files = (
201 | 4A140B16246B317600863A16 /* AppDelegate.swift in Sources */,
202 | 4A140B18246B317600863A16 /* SceneDelegate.swift in Sources */,
203 | 4A140B32246B318A00863A16 /* ContentView.swift in Sources */,
204 | 4A9245EB246E11EC00BA48AF /* TableViewExample.swift in Sources */,
205 | 4A140B31246B318A00863A16 /* ScrollViewExample3.swift in Sources */,
206 | 4A140B30246B318A00863A16 /* ScrollViewExample2.swift in Sources */,
207 | 4A140B34246B318A00863A16 /* CycleView.swift in Sources */,
208 | 4A140B2F246B318A00863A16 /* ScrollViewExample.swift in Sources */,
209 | 4A140B3D246B745C00863A16 /* CycleViewExample.swift in Sources */,
210 | 4A140B33246B318A00863A16 /* ScrollViewExample4.swift in Sources */,
211 | 4A140B3F246B7CFD00863A16 /* PageControlExample.swift in Sources */,
212 | );
213 | runOnlyForDeploymentPostprocessing = 0;
214 | };
215 | /* End PBXSourcesBuildPhase section */
216 |
217 | /* Begin PBXVariantGroup section */
218 | 4A140B20246B317700863A16 /* LaunchScreen.storyboard */ = {
219 | isa = PBXVariantGroup;
220 | children = (
221 | 4A140B21246B317700863A16 /* Base */,
222 | );
223 | name = LaunchScreen.storyboard;
224 | sourceTree = "";
225 | };
226 | /* End PBXVariantGroup section */
227 |
228 | /* Begin XCBuildConfiguration section */
229 | 4A140B24246B317700863A16 /* Debug */ = {
230 | isa = XCBuildConfiguration;
231 | buildSettings = {
232 | ALWAYS_SEARCH_USER_PATHS = NO;
233 | CLANG_ANALYZER_NONNULL = YES;
234 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
235 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
236 | CLANG_CXX_LIBRARY = "libc++";
237 | CLANG_ENABLE_MODULES = YES;
238 | CLANG_ENABLE_OBJC_ARC = YES;
239 | CLANG_ENABLE_OBJC_WEAK = YES;
240 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
241 | CLANG_WARN_BOOL_CONVERSION = YES;
242 | CLANG_WARN_COMMA = YES;
243 | CLANG_WARN_CONSTANT_CONVERSION = YES;
244 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
245 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
246 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
247 | CLANG_WARN_EMPTY_BODY = YES;
248 | CLANG_WARN_ENUM_CONVERSION = YES;
249 | CLANG_WARN_INFINITE_RECURSION = YES;
250 | CLANG_WARN_INT_CONVERSION = YES;
251 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
252 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
253 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
254 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
255 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
256 | CLANG_WARN_STRICT_PROTOTYPES = YES;
257 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
258 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
259 | CLANG_WARN_UNREACHABLE_CODE = YES;
260 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
261 | COPY_PHASE_STRIP = NO;
262 | DEBUG_INFORMATION_FORMAT = dwarf;
263 | ENABLE_STRICT_OBJC_MSGSEND = YES;
264 | ENABLE_TESTABILITY = YES;
265 | GCC_C_LANGUAGE_STANDARD = gnu11;
266 | GCC_DYNAMIC_NO_PIC = NO;
267 | GCC_NO_COMMON_BLOCKS = YES;
268 | GCC_OPTIMIZATION_LEVEL = 0;
269 | GCC_PREPROCESSOR_DEFINITIONS = (
270 | "DEBUG=1",
271 | "$(inherited)",
272 | );
273 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
274 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
275 | GCC_WARN_UNDECLARED_SELECTOR = YES;
276 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
277 | GCC_WARN_UNUSED_FUNCTION = YES;
278 | GCC_WARN_UNUSED_VARIABLE = YES;
279 | IPHONEOS_DEPLOYMENT_TARGET = 13.2;
280 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
281 | MTL_FAST_MATH = YES;
282 | ONLY_ACTIVE_ARCH = YES;
283 | SDKROOT = iphoneos;
284 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
285 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
286 | };
287 | name = Debug;
288 | };
289 | 4A140B25246B317700863A16 /* Release */ = {
290 | isa = XCBuildConfiguration;
291 | buildSettings = {
292 | ALWAYS_SEARCH_USER_PATHS = NO;
293 | CLANG_ANALYZER_NONNULL = YES;
294 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
295 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
296 | CLANG_CXX_LIBRARY = "libc++";
297 | CLANG_ENABLE_MODULES = YES;
298 | CLANG_ENABLE_OBJC_ARC = YES;
299 | CLANG_ENABLE_OBJC_WEAK = YES;
300 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
301 | CLANG_WARN_BOOL_CONVERSION = YES;
302 | CLANG_WARN_COMMA = YES;
303 | CLANG_WARN_CONSTANT_CONVERSION = YES;
304 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
305 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
306 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
307 | CLANG_WARN_EMPTY_BODY = YES;
308 | CLANG_WARN_ENUM_CONVERSION = YES;
309 | CLANG_WARN_INFINITE_RECURSION = YES;
310 | CLANG_WARN_INT_CONVERSION = YES;
311 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
312 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
313 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
314 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
315 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
316 | CLANG_WARN_STRICT_PROTOTYPES = YES;
317 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
318 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
319 | CLANG_WARN_UNREACHABLE_CODE = YES;
320 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
321 | COPY_PHASE_STRIP = NO;
322 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
323 | ENABLE_NS_ASSERTIONS = NO;
324 | ENABLE_STRICT_OBJC_MSGSEND = YES;
325 | GCC_C_LANGUAGE_STANDARD = gnu11;
326 | GCC_NO_COMMON_BLOCKS = YES;
327 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
328 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
329 | GCC_WARN_UNDECLARED_SELECTOR = YES;
330 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
331 | GCC_WARN_UNUSED_FUNCTION = YES;
332 | GCC_WARN_UNUSED_VARIABLE = YES;
333 | IPHONEOS_DEPLOYMENT_TARGET = 13.2;
334 | MTL_ENABLE_DEBUG_INFO = NO;
335 | MTL_FAST_MATH = YES;
336 | SDKROOT = iphoneos;
337 | SWIFT_COMPILATION_MODE = wholemodule;
338 | SWIFT_OPTIMIZATION_LEVEL = "-O";
339 | VALIDATE_PRODUCT = YES;
340 | };
341 | name = Release;
342 | };
343 | 4A140B27246B317700863A16 /* Debug */ = {
344 | isa = XCBuildConfiguration;
345 | buildSettings = {
346 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
347 | CODE_SIGN_STYLE = Automatic;
348 | DEVELOPMENT_ASSET_PATHS = "\"BBSwiftUIKitDemo/Preview Content\"";
349 | DEVELOPMENT_TEAM = 9LJHCTN42A;
350 | ENABLE_PREVIEWS = YES;
351 | INFOPLIST_FILE = BBSwiftUIKitDemo/Info.plist;
352 | LD_RUNPATH_SEARCH_PATHS = (
353 | "$(inherited)",
354 | "@executable_path/Frameworks",
355 | );
356 | PRODUCT_BUNDLE_IDENTIFIER = Kaibo.BBSwiftUIKitDemo;
357 | PRODUCT_NAME = "$(TARGET_NAME)";
358 | SWIFT_VERSION = 5.0;
359 | TARGETED_DEVICE_FAMILY = "1,2";
360 | };
361 | name = Debug;
362 | };
363 | 4A140B28246B317700863A16 /* Release */ = {
364 | isa = XCBuildConfiguration;
365 | buildSettings = {
366 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
367 | CODE_SIGN_STYLE = Automatic;
368 | DEVELOPMENT_ASSET_PATHS = "\"BBSwiftUIKitDemo/Preview Content\"";
369 | DEVELOPMENT_TEAM = 9LJHCTN42A;
370 | ENABLE_PREVIEWS = YES;
371 | INFOPLIST_FILE = BBSwiftUIKitDemo/Info.plist;
372 | LD_RUNPATH_SEARCH_PATHS = (
373 | "$(inherited)",
374 | "@executable_path/Frameworks",
375 | );
376 | PRODUCT_BUNDLE_IDENTIFIER = Kaibo.BBSwiftUIKitDemo;
377 | PRODUCT_NAME = "$(TARGET_NAME)";
378 | SWIFT_VERSION = 5.0;
379 | TARGETED_DEVICE_FAMILY = "1,2";
380 | };
381 | name = Release;
382 | };
383 | /* End XCBuildConfiguration section */
384 |
385 | /* Begin XCConfigurationList section */
386 | 4A140B0D246B317600863A16 /* Build configuration list for PBXProject "BBSwiftUIKitDemo" */ = {
387 | isa = XCConfigurationList;
388 | buildConfigurations = (
389 | 4A140B24246B317700863A16 /* Debug */,
390 | 4A140B25246B317700863A16 /* Release */,
391 | );
392 | defaultConfigurationIsVisible = 0;
393 | defaultConfigurationName = Release;
394 | };
395 | 4A140B26246B317700863A16 /* Build configuration list for PBXNativeTarget "BBSwiftUIKitDemo" */ = {
396 | isa = XCConfigurationList;
397 | buildConfigurations = (
398 | 4A140B27246B317700863A16 /* Debug */,
399 | 4A140B28246B317700863A16 /* Release */,
400 | );
401 | defaultConfigurationIsVisible = 0;
402 | defaultConfigurationName = Release;
403 | };
404 | /* End XCConfigurationList section */
405 | };
406 | rootObject = 4A140B0A246B317600863A16 /* Project object */;
407 | }
408 |
--------------------------------------------------------------------------------
/BBSwiftUIKitDemo/BBSwiftUIKitDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/BBSwiftUIKitDemo/BBSwiftUIKitDemo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/BBSwiftUIKitDemo/BBSwiftUIKitDemo/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // BBSwiftUIKitDemo
4 | //
5 | // Created by Kaibo Lu on 4/19/20.
6 | // Copyright © 2020 Kaibo Lu. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | @UIApplicationMain
12 | class AppDelegate: UIResponder, UIApplicationDelegate {
13 |
14 |
15 |
16 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
17 | // Override point for customization after application launch.
18 | return true
19 | }
20 |
21 | // MARK: UISceneSession Lifecycle
22 |
23 | func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
24 | // Called when a new scene session is being created.
25 | // Use this method to select a configuration to create the new scene with.
26 | return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
27 | }
28 |
29 | func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) {
30 | // Called when the user discards a scene session.
31 | // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
32 | // Use this method to release any resources that were specific to the discarded scenes, as they will not return.
33 | }
34 |
35 |
36 | }
37 |
38 |
--------------------------------------------------------------------------------
/BBSwiftUIKitDemo/BBSwiftUIKitDemo/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "20x20",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "20x20",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "29x29",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "29x29",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "40x40",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "40x40",
31 | "scale" : "3x"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "size" : "60x60",
36 | "scale" : "2x"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "size" : "60x60",
41 | "scale" : "3x"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "size" : "20x20",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "size" : "20x20",
51 | "scale" : "2x"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "size" : "29x29",
56 | "scale" : "1x"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "size" : "29x29",
61 | "scale" : "2x"
62 | },
63 | {
64 | "idiom" : "ipad",
65 | "size" : "40x40",
66 | "scale" : "1x"
67 | },
68 | {
69 | "idiom" : "ipad",
70 | "size" : "40x40",
71 | "scale" : "2x"
72 | },
73 | {
74 | "idiom" : "ipad",
75 | "size" : "76x76",
76 | "scale" : "1x"
77 | },
78 | {
79 | "idiom" : "ipad",
80 | "size" : "76x76",
81 | "scale" : "2x"
82 | },
83 | {
84 | "idiom" : "ipad",
85 | "size" : "83.5x83.5",
86 | "scale" : "2x"
87 | },
88 | {
89 | "idiom" : "ios-marketing",
90 | "size" : "1024x1024",
91 | "scale" : "1x"
92 | }
93 | ],
94 | "info" : {
95 | "version" : 1,
96 | "author" : "xcode"
97 | }
98 | }
--------------------------------------------------------------------------------
/BBSwiftUIKitDemo/BBSwiftUIKitDemo/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/BBSwiftUIKitDemo/BBSwiftUIKitDemo/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 |
--------------------------------------------------------------------------------
/BBSwiftUIKitDemo/BBSwiftUIKitDemo/ContentView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ContentView.swift
3 | // BBSwiftUIKitDemo
4 | //
5 | // Created by Kaibo Lu on 4/19/20.
6 | // Copyright © 2020 Kaibo Lu. All rights reserved.
7 | //
8 |
9 | import SwiftUI
10 |
11 | struct MenuItem {
12 | let title: String
13 | let view: Content
14 | }
15 |
16 | extension View {
17 | var toAnyView: AnyView { AnyView(self) }
18 | }
19 |
20 | struct ContentView: View {
21 | let items: [MenuItem] = [
22 | MenuItem(title: "Scroll view", view: ScrollViewExample().toAnyView),
23 | MenuItem(title: "Scroll view 2", view: ScrollViewExample2().toAnyView),
24 | MenuItem(title: "Scroll view 3", view: ScrollViewExample3().toAnyView),
25 | MenuItem(title: "Scroll view 4", view: ScrollViewExample4().toAnyView),
26 | MenuItem(title: "Page control", view: PageControlExample().toAnyView),
27 | MenuItem(title: "Cycle view manual", view: CycleViewExample(autoDisplay: false).toAnyView),
28 | MenuItem(title: "Cycle view auto", view: CycleViewExample(autoDisplay: true).toAnyView),
29 | MenuItem(title: "Table view", view: TableViewExample().toAnyView)
30 | ]
31 |
32 | var body: some View {
33 | NavigationView {
34 | List(items, id: \.title) { item in
35 | NavigationLink(destination: item.view) {
36 | Text(item.title)
37 | }
38 | }
39 | .navigationBarTitle("", displayMode: .inline)
40 | }
41 | }
42 | }
43 |
44 | struct ContentView_Previews: PreviewProvider {
45 | static var previews: some View {
46 | ContentView()
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/BBSwiftUIKitDemo/BBSwiftUIKitDemo/CycleView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CycleView.swift
3 | // BBSwiftUIKitDemo
4 | //
5 | // Created by Kaibo Lu on 5/4/20.
6 | // Copyright © 2020 Kaibo Lu. All rights reserved.
7 | //
8 |
9 | import SwiftUI
10 | import BBSwiftUIKit
11 |
12 | struct CycleView: View {
13 | @ObservedObject private var model: CycleViewModel
14 |
15 | init(names: [String], pageSize: CGSize, autoDisplay: Bool) {
16 | model = CycleViewModel(names: names, pageSize: pageSize, autoDisplay: autoDisplay)
17 | }
18 |
19 | var body: some View {
20 | VStack {
21 | BBScrollView(.horizontal, contentOffset: $model.contentOffset) {
22 | HStack(spacing: 0) {
23 | ForEach(self.model.nameIndexes, id: \.self) { i in
24 | Image(systemName: self.model.names[i])
25 | .resizable()
26 | .scaledToFit()
27 | .frame(width: self.model.pageSize.width, height: self.model.pageSize.height)
28 | }
29 | }
30 | }
31 | .bb_showsHorizontalScrollIndicator(false)
32 | .bb_isPagingEnabled(true)
33 | .bb_contentOffsetToScrollAnimated($model.contentOffsetToScrollAnimated)
34 | .onAppear {
35 | self.model.appear = true
36 | self.model.start()
37 | }
38 | .onDisappear {
39 | self.model.appear = false
40 | self.model.stop()
41 | }
42 |
43 | BBPageControl(currentPage: $model.index, numberOfPages: model.names.count)
44 | .background(Color.black)
45 | }
46 | }
47 | }
48 |
49 | struct CycleView_Previews: PreviewProvider {
50 | static var previews: some View {
51 | CycleView(names: ["sun.max", "moon", "star", "cloud"], pageSize: CGSize(width: UIScreen.main.bounds.width, height: 500), autoDisplay: false)
52 | }
53 | }
54 |
55 | fileprivate class CycleViewModel: ObservableObject {
56 | let names: [String]
57 | let pageSize: CGSize
58 | let autoDisplay: Bool
59 |
60 | var appear: Bool = false
61 | var timer: Timer?
62 |
63 | @Published var index = 0
64 |
65 | @Published var contentOffset: CGPoint {
66 | didSet {
67 | if contentOffset.x <= 0 {
68 | index = nameIndexes[0]
69 | contentOffset.x += pageSize.width
70 | } else if contentOffset.x >= pageSize.width * 2 {
71 | index = nameIndexes[2]
72 | contentOffset.x -= pageSize.width
73 | }
74 | }
75 | }
76 |
77 | @Published var contentOffsetToScrollAnimated: CGPoint? = nil
78 |
79 | var nameIndexes: [Int] {
80 | if index == 0 { return [names.count - 1, 0, 1] }
81 | let nextIndex = index == names.count - 1 ? 0 : index + 1
82 | return [index - 1, index, nextIndex]
83 | }
84 |
85 | init(names: [String], pageSize: CGSize, autoDisplay: Bool) {
86 | self.names = names
87 | self.pageSize = pageSize
88 | self.autoDisplay = autoDisplay
89 | self.contentOffset = CGPoint(x: pageSize.width, y: 0)
90 | }
91 |
92 | func start() {
93 | if !autoDisplay || timer != nil { return }
94 | timer = Timer.scheduledTimer(withTimeInterval: 2, repeats: true) { [weak self] _ in
95 | guard let self = self, self.appear else { return }
96 | self.contentOffsetToScrollAnimated = CGPoint(x: self.pageSize.width * 2, y: 0)
97 | }
98 | }
99 |
100 | func stop() {
101 | timer?.invalidate()
102 | timer = nil
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/BBSwiftUIKitDemo/BBSwiftUIKitDemo/CycleViewExample.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CycleViewExample.swift
3 | // BBSwiftUIKitDemo
4 | //
5 | // Created by Kaibo Lu on 5/12/20.
6 | // Copyright © 2020 Kaibo Lu. All rights reserved.
7 | //
8 |
9 | import SwiftUI
10 | import BBSwiftUIKit
11 |
12 | struct CycleViewExample: View {
13 | let autoDisplay: Bool
14 |
15 | @State var index: Int = 0
16 |
17 | private let names: [String] = ["sun.max", "moon", "star", "cloud"]
18 |
19 | var body: some View {
20 | CycleView(names: names, pageSize: CGSize(width: UIScreen.main.bounds.width, height: 500), autoDisplay: autoDisplay)
21 | }
22 | }
23 |
24 | struct CycleViewExample_Previews: PreviewProvider {
25 | static var previews: some View {
26 | CycleViewExample(autoDisplay: false)
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/BBSwiftUIKitDemo/BBSwiftUIKitDemo/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 | UIApplicationSceneManifest
24 |
25 | UIApplicationSupportsMultipleScenes
26 |
27 | UISceneConfigurations
28 |
29 | UIWindowSceneSessionRoleApplication
30 |
31 |
32 | UISceneConfigurationName
33 | Default Configuration
34 | UISceneDelegateClassName
35 | $(PRODUCT_MODULE_NAME).SceneDelegate
36 |
37 |
38 |
39 |
40 | UILaunchStoryboardName
41 | LaunchScreen
42 | UIRequiredDeviceCapabilities
43 |
44 | armv7
45 |
46 | UISupportedInterfaceOrientations
47 |
48 | UIInterfaceOrientationPortrait
49 | UIInterfaceOrientationLandscapeLeft
50 | UIInterfaceOrientationLandscapeRight
51 |
52 | UISupportedInterfaceOrientations~ipad
53 |
54 | UIInterfaceOrientationPortrait
55 | UIInterfaceOrientationPortraitUpsideDown
56 | UIInterfaceOrientationLandscapeLeft
57 | UIInterfaceOrientationLandscapeRight
58 |
59 |
60 |
61 |
--------------------------------------------------------------------------------
/BBSwiftUIKitDemo/BBSwiftUIKitDemo/PageControlExample.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PageControlExample.swift
3 | // BBSwiftUIKitDemo
4 | //
5 | // Created by Kaibo Lu on 5/12/20.
6 | // Copyright © 2020 Kaibo Lu. All rights reserved.
7 | //
8 |
9 | import SwiftUI
10 | import BBSwiftUIKit
11 |
12 | struct PageControlExample: View {
13 | @State var currentPage: Int = 0
14 | @State var numberOfPages: Int = 5
15 | @State var hidesForSinglePage: Bool = false
16 | @State var pageIndicatorTintColor: UIColor?
17 | @State var currentPageIndicatorTintColor: UIColor?
18 |
19 | var body: some View {
20 | VStack {
21 | BBPageControl(currentPage: $currentPage, numberOfPages: numberOfPages)
22 | .bb_hidesForSinglePage(hidesForSinglePage)
23 | .bb_pageIndicatorTintColor(pageIndicatorTintColor)
24 | .bb_currentPageIndicatorTintColor(currentPageIndicatorTintColor)
25 | .background(Color.black)
26 |
27 | Button("Current page \(currentPage)") {
28 | self.currentPage = self.numberOfPages == 0 ? 0 : (self.currentPage + 1) % self.numberOfPages
29 | }
30 | .padding()
31 |
32 | Button("Number of pages \(numberOfPages)") {
33 | self.numberOfPages = (self.numberOfPages + 1) % 10
34 | }
35 | .padding()
36 |
37 | Button("Hides for single page \(hidesForSinglePage ? "true" : "false")") {
38 | self.hidesForSinglePage.toggle()
39 | }
40 | .padding()
41 |
42 | Button("Page indicator tint color \(pageIndicatorTintColor == nil ? "nil" : "blue")") {
43 | self.pageIndicatorTintColor = self.pageIndicatorTintColor == nil ? .blue : nil
44 | }
45 | .padding()
46 |
47 | Button("Current page indicator tint color \(currentPageIndicatorTintColor == nil ? "nil" : "red")") {
48 | self.currentPageIndicatorTintColor = self.currentPageIndicatorTintColor == nil ? .red : nil
49 | }
50 | .padding()
51 | }
52 | }
53 | }
54 |
55 | struct PageControlExample_Previews: PreviewProvider {
56 | static var previews: some View {
57 | PageControlExample()
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/BBSwiftUIKitDemo/BBSwiftUIKitDemo/Preview Content/Preview Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/BBSwiftUIKitDemo/BBSwiftUIKitDemo/SceneDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SceneDelegate.swift
3 | // BBSwiftUIKitDemo
4 | //
5 | // Created by Kaibo Lu on 4/19/20.
6 | // Copyright © 2020 Kaibo Lu. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import SwiftUI
11 |
12 | class SceneDelegate: UIResponder, UIWindowSceneDelegate {
13 |
14 | var window: UIWindow?
15 |
16 |
17 | func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
18 | // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
19 | // If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
20 | // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
21 |
22 | // Create the SwiftUI view that provides the window contents.
23 | let contentView = ContentView()
24 |
25 | // Use a UIHostingController as window root view controller.
26 | if let windowScene = scene as? UIWindowScene {
27 | let window = UIWindow(windowScene: windowScene)
28 | window.rootViewController = UIHostingController(rootView: contentView)
29 | self.window = window
30 | window.makeKeyAndVisible()
31 | }
32 | }
33 |
34 | func sceneDidDisconnect(_ scene: UIScene) {
35 | // Called as the scene is being released by the system.
36 | // This occurs shortly after the scene enters the background, or when its session is discarded.
37 | // Release any resources associated with this scene that can be re-created the next time the scene connects.
38 | // The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead).
39 | }
40 |
41 | func sceneDidBecomeActive(_ scene: UIScene) {
42 | // Called when the scene has moved from an inactive state to an active state.
43 | // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
44 | }
45 |
46 | func sceneWillResignActive(_ scene: UIScene) {
47 | // Called when the scene will move from an active state to an inactive state.
48 | // This may occur due to temporary interruptions (ex. an incoming phone call).
49 | }
50 |
51 | func sceneWillEnterForeground(_ scene: UIScene) {
52 | // Called as the scene transitions from the background to the foreground.
53 | // Use this method to undo the changes made on entering the background.
54 | }
55 |
56 | func sceneDidEnterBackground(_ scene: UIScene) {
57 | // Called as the scene transitions from the foreground to the background.
58 | // Use this method to save data, release shared resources, and store enough scene-specific state information
59 | // to restore the scene back to its current state.
60 | }
61 |
62 |
63 | }
64 |
65 |
--------------------------------------------------------------------------------
/BBSwiftUIKitDemo/BBSwiftUIKitDemo/ScrollViewExample.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ScrollViewExample.swift
3 | // BBSwiftUIKitDemo
4 | //
5 | // Created by Kaibo Lu on 4/19/20.
6 | // Copyright © 2020 Kaibo Lu. All rights reserved.
7 | //
8 |
9 | import SwiftUI
10 | import BBSwiftUIKit
11 |
12 | struct ScrollViewExample: View {
13 | @State var contentOffset: CGPoint = .zero
14 | @State var bounces: Bool = true
15 | @State var isPagingEnabled: Bool = false
16 | @State var showsHorizontalScrollIndicator: Bool = true
17 | @State var contentOffsetToScrollAnimated: CGPoint? = nil
18 | @State var longContent: Bool = false
19 |
20 | var body: some View {
21 | VStack {
22 | BBScrollView(.horizontal, contentOffset: $contentOffset) {
23 | HStack(spacing: 0) {
24 | Text(self.longContent ? "AAA" : "A")
25 | .frame(width: UIScreen.main.bounds.width)
26 | .background(Color.red)
27 | Text(self.longContent ? "BBB" : "B")
28 | .frame(width: UIScreen.main.bounds.width)
29 | .background(Color.blue)
30 | if self.longContent {
31 | Text("CCC")
32 | .frame(width: UIScreen.main.bounds.width)
33 | .background(Color.green)
34 | }
35 | }
36 | }
37 | .bb_bounces(bounces)
38 | .bb_isPagingEnabled(isPagingEnabled)
39 | .bb_showsHorizontalScrollIndicator(showsHorizontalScrollIndicator)
40 | .bb_contentOffsetToScrollAnimated($contentOffsetToScrollAnimated)
41 |
42 | Slider(value: $contentOffset.x, in: 0...UIScreen.main.bounds.width * (self.longContent ? 2 : 1))
43 |
44 | Button("Scroll to x = 100") {
45 | self.contentOffsetToScrollAnimated = CGPoint(x: 100, y: 0)
46 | }
47 | .padding()
48 |
49 | Button("Bounces \(bounces ? "true" : "false")") {
50 | self.bounces.toggle()
51 | }
52 | .padding()
53 |
54 | Button("Paging enabled \(isPagingEnabled ? "true" : "false")") {
55 | self.isPagingEnabled.toggle()
56 | }
57 | .padding()
58 |
59 | Button("Shows horizontal scroll indicator \(showsHorizontalScrollIndicator ? "true" : "false")") {
60 | self.showsHorizontalScrollIndicator.toggle()
61 | }
62 | .padding()
63 |
64 | Button("\(longContent ? 3 : 2) screen width") {
65 | self.longContent.toggle()
66 | }
67 | .padding()
68 | }
69 | }
70 | }
71 |
72 | struct ScrollViewExample_Previews: PreviewProvider {
73 | static var previews: some View {
74 | ScrollViewExample()
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/BBSwiftUIKitDemo/BBSwiftUIKitDemo/ScrollViewExample2.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ScrollViewExample2.swift
3 | // BBSwiftUIKitDemo
4 | //
5 | // Created by Kaibo Lu on 4/19/20.
6 | // Copyright © 2020 Kaibo Lu. All rights reserved.
7 | //
8 |
9 | import SwiftUI
10 | import BBSwiftUIKit
11 |
12 | struct ScrollViewExample2: View {
13 | @State var contentOffset: CGPoint = .zero
14 | @State var showsVerticalScrollIndicator: Bool = true
15 |
16 | var body: some View {
17 | VStack {
18 | BBScrollView(.vertical, contentOffset: $contentOffset) {
19 | VStack {
20 | Text("1")
21 | .frame(width: 50, height: UIScreen.main.bounds.height)
22 | .background(Color.red)
23 | Text("2")
24 | .frame(width: 50, height: UIScreen.main.bounds.height)
25 | .background(Color.blue)
26 | Text("3")
27 | .frame(width: 50, height: UIScreen.main.bounds.height)
28 | .background(Color.green)
29 | }
30 | }
31 | .bb_showsVerticalScrollIndicator(showsVerticalScrollIndicator)
32 | .edgesIgnoringSafeArea(.all)
33 |
34 | Slider(value: self.$contentOffset.y, in: 0...UIScreen.main.bounds.height * 2)
35 |
36 | Button("Shows vertical scroll indicator \(self.showsVerticalScrollIndicator ? "true" : "false")") {
37 | self.showsVerticalScrollIndicator.toggle()
38 | }
39 | .padding()
40 | }
41 | }
42 | }
43 |
44 | struct ScrollViewExample2_Previews: PreviewProvider {
45 | static var previews: some View {
46 | ScrollViewExample2()
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/BBSwiftUIKitDemo/BBSwiftUIKitDemo/ScrollViewExample3.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ScrollViewExample3.swift
3 | // BBSwiftUIKitDemo
4 | //
5 | // Created by Kaibo Lu on 4/19/20.
6 | // Copyright © 2020 Kaibo Lu. All rights reserved.
7 | //
8 |
9 | import SwiftUI
10 | import BBSwiftUIKit
11 |
12 | struct ScrollViewExample3: View {
13 | @State var contentOffset: CGPoint = .zero
14 |
15 | var body: some View {
16 | BBScrollView([.horizontal, .vertical], contentOffset: $contentOffset) {
17 | VStack {
18 | Text("1")
19 | .frame(width: UIScreen.main.bounds.width * 2, height: UIScreen.main.bounds.height)
20 | .background(Color.red)
21 | Text("2")
22 | .frame(width: UIScreen.main.bounds.width * 2, height: UIScreen.main.bounds.height)
23 | .background(Color.blue)
24 | Text("3")
25 | .frame(width: UIScreen.main.bounds.width * 2, height: UIScreen.main.bounds.height)
26 | .background(Color.green)
27 | }
28 | }
29 | }
30 | }
31 |
32 | struct ScrollViewExample3_Previews: PreviewProvider {
33 | static var previews: some View {
34 | ScrollViewExample3()
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/BBSwiftUIKitDemo/BBSwiftUIKitDemo/ScrollViewExample4.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ScrollViewExample4.swift
3 | // BBSwiftUIKitDemo
4 | //
5 | // Created by Kaibo Lu on 4/19/20.
6 | // Copyright © 2020 Kaibo Lu. All rights reserved.
7 | //
8 |
9 | import SwiftUI
10 | import BBSwiftUIKit
11 |
12 | struct ScrollViewExample4: View {
13 | @State var contentOffset: CGPoint = .zero
14 |
15 | var body: some View {
16 | BBScrollView([.horizontal, .vertical]) {
17 | Text("Hello, World!")
18 | Image(systemName: "heart")
19 | }
20 | }
21 | }
22 |
23 | struct ScrollViewExample4_Previews: PreviewProvider {
24 | static var previews: some View {
25 | ScrollViewExample4()
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/BBSwiftUIKitDemo/BBSwiftUIKitDemo/TableViewExample.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TableViewExample.swift
3 | // BBSwiftUIKitDemo
4 | //
5 | // Created by Kaibo Lu on 5/14/20.
6 | // Copyright © 2020 Kaibo Lu. All rights reserved.
7 | //
8 |
9 | import SwiftUI
10 | import BBSwiftUIKit
11 |
12 | struct TableViewExample: View {
13 |
14 | class Model: ObservableObject {
15 | @Published var list: Range = 0..<100
16 | @Published var updateHeight: Bool = false
17 | @Published var reloadData: Bool = false
18 | @Published var reloadRows: [Int] = []
19 | @Published var scrollToRow: Int? = nil
20 | @Published var contentOffset: CGPoint = .zero
21 | @Published var contentOffsetToScrollAnimated: CGPoint? = nil
22 | @Published var isRefreshing: Bool = false
23 | @Published var isLoadingMore: Bool = false
24 |
25 | func reloadListData() {
26 | self.list = 0..<100
27 | self.updateHeight.toggle()
28 | self.reloadData = true
29 | }
30 |
31 | func reloadListRows() {
32 | list = 0..<100
33 | updateHeight.toggle()
34 | reloadRows = (0..<10).map { $0 }
35 | }
36 | }
37 |
38 | @ObservedObject var model = Model()
39 |
40 | var body: some View {
41 | VStack {
42 | BBTableView(model.list) { i in
43 | if i % 2 == 0 {
44 | Text("\(i)\(self.model.updateHeight ? "" : "A")")
45 | .frame(height: self.model.updateHeight ? 50 : 100)
46 | .padding()
47 | .background(Color.blue)
48 | } else {
49 | Image(systemName: "heart")
50 | .resizable()
51 | .scaledToFit()
52 | .frame(height: self.model.updateHeight ? 20 : 40)
53 | .padding()
54 | .background(Color.orange)
55 | }
56 | }
57 | .bb_reloadData($model.reloadData)
58 | .bb_reloadRows($model.reloadRows, animation: .automatic)
59 | .bb_scrollToRow($model.scrollToRow, position: .none, animated: true)
60 | .bb_contentOffset($model.contentOffset)
61 | .bb_contentOffsetToScrollAnimated($model.contentOffsetToScrollAnimated)
62 | .bb_setupRefreshControl { refreshControl in
63 | refreshControl.tintColor = .blue
64 | refreshControl.attributedTitle = NSAttributedString(string: "Loading...", attributes: [.font: UIFont.systemFont(ofSize: 15), .foregroundColor: UIColor.blue])
65 | }
66 | .bb_pullDownToRefresh(isRefreshing: $model.isRefreshing) {
67 | DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
68 | self.model.reloadListData()
69 | self.model.isRefreshing = false
70 | }
71 | }
72 | .bb_pullUpToLoadMore(bottomSpace: 30) {
73 | if self.model.isLoadingMore { return }
74 | self.model.isLoadingMore = true
75 | DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
76 | self.model.list = 0..