├── .gitignore
├── .swiftpm
└── xcode
│ └── package.xcworkspace
│ └── xcshareddata
│ └── IDEWorkspaceChecks.plist
├── DemoApp
└── DemoApp
│ ├── DemoApp.xcodeproj
│ ├── project.pbxproj
│ └── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
│ ├── DemoApp
│ ├── Assets.xcassets
│ │ ├── AccentColor.colorset
│ │ │ └── Contents.json
│ │ ├── AppIcon.appiconset
│ │ │ └── Contents.json
│ │ └── Contents.json
│ ├── ContentView.swift
│ ├── DemoAppApp.swift
│ └── Preview Content
│ │ └── Preview Assets.xcassets
│ │ └── Contents.json
│ ├── DemoAppTests
│ └── DemoAppTests.swift
│ └── DemoAppUITests
│ ├── DemoAppUITests.swift
│ └── DemoAppUITestsLaunchTests.swift
├── Package.swift
├── README.md
└── Sources
└── ContributionChart
├── ContributionChartTest.swift
└── ContributionChartView.swift
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | /.build
3 | /Packages
4 | /*.xcodeproj
5 | xcuserdata/
6 | DerivedData/
7 | .swiftpm/config/registries.json
8 | .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
9 | .netrc
10 |
--------------------------------------------------------------------------------
/.swiftpm/xcode/package.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/DemoApp/DemoApp/DemoApp.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 56;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | C00592122C59D22F00FAE9E2 /* DemoAppApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = C00592112C59D22F00FAE9E2 /* DemoAppApp.swift */; };
11 | C00592142C59D22F00FAE9E2 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C00592132C59D22F00FAE9E2 /* ContentView.swift */; };
12 | C00592162C59D23000FAE9E2 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C00592152C59D23000FAE9E2 /* Assets.xcassets */; };
13 | C00592192C59D23000FAE9E2 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C00592182C59D23000FAE9E2 /* Preview Assets.xcassets */; };
14 | C00592232C59D23100FAE9E2 /* DemoAppTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C00592222C59D23100FAE9E2 /* DemoAppTests.swift */; };
15 | C005922D2C59D23100FAE9E2 /* DemoAppUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C005922C2C59D23100FAE9E2 /* DemoAppUITests.swift */; };
16 | C005922F2C59D23100FAE9E2 /* DemoAppUITestsLaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C005922E2C59D23100FAE9E2 /* DemoAppUITestsLaunchTests.swift */; };
17 | C005923E2C59D5BC00FAE9E2 /* ContributionChart in Frameworks */ = {isa = PBXBuildFile; productRef = C005923D2C59D5BC00FAE9E2 /* ContributionChart */; };
18 | /* End PBXBuildFile section */
19 |
20 | /* Begin PBXContainerItemProxy section */
21 | C005921F2C59D23000FAE9E2 /* PBXContainerItemProxy */ = {
22 | isa = PBXContainerItemProxy;
23 | containerPortal = C00592062C59D22F00FAE9E2 /* Project object */;
24 | proxyType = 1;
25 | remoteGlobalIDString = C005920D2C59D22F00FAE9E2;
26 | remoteInfo = DemoApp;
27 | };
28 | C00592292C59D23100FAE9E2 /* PBXContainerItemProxy */ = {
29 | isa = PBXContainerItemProxy;
30 | containerPortal = C00592062C59D22F00FAE9E2 /* Project object */;
31 | proxyType = 1;
32 | remoteGlobalIDString = C005920D2C59D22F00FAE9E2;
33 | remoteInfo = DemoApp;
34 | };
35 | /* End PBXContainerItemProxy section */
36 |
37 | /* Begin PBXFileReference section */
38 | C005920E2C59D22F00FAE9E2 /* DemoApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = DemoApp.app; sourceTree = BUILT_PRODUCTS_DIR; };
39 | C00592112C59D22F00FAE9E2 /* DemoAppApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DemoAppApp.swift; sourceTree = ""; };
40 | C00592132C59D22F00FAE9E2 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; };
41 | C00592152C59D23000FAE9E2 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
42 | C00592182C59D23000FAE9E2 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; };
43 | C005921E2C59D23000FAE9E2 /* DemoAppTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = DemoAppTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
44 | C00592222C59D23100FAE9E2 /* DemoAppTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DemoAppTests.swift; sourceTree = ""; };
45 | C00592282C59D23100FAE9E2 /* DemoAppUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = DemoAppUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
46 | C005922C2C59D23100FAE9E2 /* DemoAppUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DemoAppUITests.swift; sourceTree = ""; };
47 | C005922E2C59D23100FAE9E2 /* DemoAppUITestsLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DemoAppUITestsLaunchTests.swift; sourceTree = ""; };
48 | C005923C2C59D57200FAE9E2 /* SwiftUI-ContributionChart */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = "SwiftUI-ContributionChart"; path = ../..; sourceTree = ""; };
49 | /* End PBXFileReference section */
50 |
51 | /* Begin PBXFrameworksBuildPhase section */
52 | C005920B2C59D22F00FAE9E2 /* Frameworks */ = {
53 | isa = PBXFrameworksBuildPhase;
54 | buildActionMask = 2147483647;
55 | files = (
56 | C005923E2C59D5BC00FAE9E2 /* ContributionChart in Frameworks */,
57 | );
58 | runOnlyForDeploymentPostprocessing = 0;
59 | };
60 | C005921B2C59D23000FAE9E2 /* Frameworks */ = {
61 | isa = PBXFrameworksBuildPhase;
62 | buildActionMask = 2147483647;
63 | files = (
64 | );
65 | runOnlyForDeploymentPostprocessing = 0;
66 | };
67 | C00592252C59D23100FAE9E2 /* Frameworks */ = {
68 | isa = PBXFrameworksBuildPhase;
69 | buildActionMask = 2147483647;
70 | files = (
71 | );
72 | runOnlyForDeploymentPostprocessing = 0;
73 | };
74 | /* End PBXFrameworksBuildPhase section */
75 |
76 | /* Begin PBXGroup section */
77 | C00592052C59D22F00FAE9E2 = {
78 | isa = PBXGroup;
79 | children = (
80 | C00592102C59D22F00FAE9E2 /* DemoApp */,
81 | C00592212C59D23100FAE9E2 /* DemoAppTests */,
82 | C005922B2C59D23100FAE9E2 /* DemoAppUITests */,
83 | C005920F2C59D22F00FAE9E2 /* Products */,
84 | C005923B2C59D57200FAE9E2 /* Frameworks */,
85 | );
86 | sourceTree = "";
87 | };
88 | C005920F2C59D22F00FAE9E2 /* Products */ = {
89 | isa = PBXGroup;
90 | children = (
91 | C005920E2C59D22F00FAE9E2 /* DemoApp.app */,
92 | C005921E2C59D23000FAE9E2 /* DemoAppTests.xctest */,
93 | C00592282C59D23100FAE9E2 /* DemoAppUITests.xctest */,
94 | );
95 | name = Products;
96 | sourceTree = "";
97 | };
98 | C00592102C59D22F00FAE9E2 /* DemoApp */ = {
99 | isa = PBXGroup;
100 | children = (
101 | C00592112C59D22F00FAE9E2 /* DemoAppApp.swift */,
102 | C00592132C59D22F00FAE9E2 /* ContentView.swift */,
103 | C00592152C59D23000FAE9E2 /* Assets.xcassets */,
104 | C00592172C59D23000FAE9E2 /* Preview Content */,
105 | );
106 | path = DemoApp;
107 | sourceTree = "";
108 | };
109 | C00592172C59D23000FAE9E2 /* Preview Content */ = {
110 | isa = PBXGroup;
111 | children = (
112 | C00592182C59D23000FAE9E2 /* Preview Assets.xcassets */,
113 | );
114 | path = "Preview Content";
115 | sourceTree = "";
116 | };
117 | C00592212C59D23100FAE9E2 /* DemoAppTests */ = {
118 | isa = PBXGroup;
119 | children = (
120 | C00592222C59D23100FAE9E2 /* DemoAppTests.swift */,
121 | );
122 | path = DemoAppTests;
123 | sourceTree = "";
124 | };
125 | C005922B2C59D23100FAE9E2 /* DemoAppUITests */ = {
126 | isa = PBXGroup;
127 | children = (
128 | C005922C2C59D23100FAE9E2 /* DemoAppUITests.swift */,
129 | C005922E2C59D23100FAE9E2 /* DemoAppUITestsLaunchTests.swift */,
130 | );
131 | path = DemoAppUITests;
132 | sourceTree = "";
133 | };
134 | C005923B2C59D57200FAE9E2 /* Frameworks */ = {
135 | isa = PBXGroup;
136 | children = (
137 | C005923C2C59D57200FAE9E2 /* SwiftUI-ContributionChart */,
138 | );
139 | name = Frameworks;
140 | sourceTree = "";
141 | };
142 | /* End PBXGroup section */
143 |
144 | /* Begin PBXNativeTarget section */
145 | C005920D2C59D22F00FAE9E2 /* DemoApp */ = {
146 | isa = PBXNativeTarget;
147 | buildConfigurationList = C00592322C59D23100FAE9E2 /* Build configuration list for PBXNativeTarget "DemoApp" */;
148 | buildPhases = (
149 | C005920A2C59D22F00FAE9E2 /* Sources */,
150 | C005920B2C59D22F00FAE9E2 /* Frameworks */,
151 | C005920C2C59D22F00FAE9E2 /* Resources */,
152 | );
153 | buildRules = (
154 | );
155 | dependencies = (
156 | );
157 | name = DemoApp;
158 | packageProductDependencies = (
159 | C005923D2C59D5BC00FAE9E2 /* ContributionChart */,
160 | );
161 | productName = DemoApp;
162 | productReference = C005920E2C59D22F00FAE9E2 /* DemoApp.app */;
163 | productType = "com.apple.product-type.application";
164 | };
165 | C005921D2C59D23000FAE9E2 /* DemoAppTests */ = {
166 | isa = PBXNativeTarget;
167 | buildConfigurationList = C00592352C59D23100FAE9E2 /* Build configuration list for PBXNativeTarget "DemoAppTests" */;
168 | buildPhases = (
169 | C005921A2C59D23000FAE9E2 /* Sources */,
170 | C005921B2C59D23000FAE9E2 /* Frameworks */,
171 | C005921C2C59D23000FAE9E2 /* Resources */,
172 | );
173 | buildRules = (
174 | );
175 | dependencies = (
176 | C00592202C59D23000FAE9E2 /* PBXTargetDependency */,
177 | );
178 | name = DemoAppTests;
179 | productName = DemoAppTests;
180 | productReference = C005921E2C59D23000FAE9E2 /* DemoAppTests.xctest */;
181 | productType = "com.apple.product-type.bundle.unit-test";
182 | };
183 | C00592272C59D23100FAE9E2 /* DemoAppUITests */ = {
184 | isa = PBXNativeTarget;
185 | buildConfigurationList = C00592382C59D23100FAE9E2 /* Build configuration list for PBXNativeTarget "DemoAppUITests" */;
186 | buildPhases = (
187 | C00592242C59D23100FAE9E2 /* Sources */,
188 | C00592252C59D23100FAE9E2 /* Frameworks */,
189 | C00592262C59D23100FAE9E2 /* Resources */,
190 | );
191 | buildRules = (
192 | );
193 | dependencies = (
194 | C005922A2C59D23100FAE9E2 /* PBXTargetDependency */,
195 | );
196 | name = DemoAppUITests;
197 | productName = DemoAppUITests;
198 | productReference = C00592282C59D23100FAE9E2 /* DemoAppUITests.xctest */;
199 | productType = "com.apple.product-type.bundle.ui-testing";
200 | };
201 | /* End PBXNativeTarget section */
202 |
203 | /* Begin PBXProject section */
204 | C00592062C59D22F00FAE9E2 /* Project object */ = {
205 | isa = PBXProject;
206 | attributes = {
207 | BuildIndependentTargetsInParallel = 1;
208 | LastSwiftUpdateCheck = 1540;
209 | LastUpgradeCheck = 1540;
210 | TargetAttributes = {
211 | C005920D2C59D22F00FAE9E2 = {
212 | CreatedOnToolsVersion = 15.4;
213 | };
214 | C005921D2C59D23000FAE9E2 = {
215 | CreatedOnToolsVersion = 15.4;
216 | TestTargetID = C005920D2C59D22F00FAE9E2;
217 | };
218 | C00592272C59D23100FAE9E2 = {
219 | CreatedOnToolsVersion = 15.4;
220 | TestTargetID = C005920D2C59D22F00FAE9E2;
221 | };
222 | };
223 | };
224 | buildConfigurationList = C00592092C59D22F00FAE9E2 /* Build configuration list for PBXProject "DemoApp" */;
225 | compatibilityVersion = "Xcode 14.0";
226 | developmentRegion = en;
227 | hasScannedForEncodings = 0;
228 | knownRegions = (
229 | en,
230 | Base,
231 | );
232 | mainGroup = C00592052C59D22F00FAE9E2;
233 | productRefGroup = C005920F2C59D22F00FAE9E2 /* Products */;
234 | projectDirPath = "";
235 | projectRoot = "";
236 | targets = (
237 | C005920D2C59D22F00FAE9E2 /* DemoApp */,
238 | C005921D2C59D23000FAE9E2 /* DemoAppTests */,
239 | C00592272C59D23100FAE9E2 /* DemoAppUITests */,
240 | );
241 | };
242 | /* End PBXProject section */
243 |
244 | /* Begin PBXResourcesBuildPhase section */
245 | C005920C2C59D22F00FAE9E2 /* Resources */ = {
246 | isa = PBXResourcesBuildPhase;
247 | buildActionMask = 2147483647;
248 | files = (
249 | C00592192C59D23000FAE9E2 /* Preview Assets.xcassets in Resources */,
250 | C00592162C59D23000FAE9E2 /* Assets.xcassets in Resources */,
251 | );
252 | runOnlyForDeploymentPostprocessing = 0;
253 | };
254 | C005921C2C59D23000FAE9E2 /* Resources */ = {
255 | isa = PBXResourcesBuildPhase;
256 | buildActionMask = 2147483647;
257 | files = (
258 | );
259 | runOnlyForDeploymentPostprocessing = 0;
260 | };
261 | C00592262C59D23100FAE9E2 /* Resources */ = {
262 | isa = PBXResourcesBuildPhase;
263 | buildActionMask = 2147483647;
264 | files = (
265 | );
266 | runOnlyForDeploymentPostprocessing = 0;
267 | };
268 | /* End PBXResourcesBuildPhase section */
269 |
270 | /* Begin PBXSourcesBuildPhase section */
271 | C005920A2C59D22F00FAE9E2 /* Sources */ = {
272 | isa = PBXSourcesBuildPhase;
273 | buildActionMask = 2147483647;
274 | files = (
275 | C00592142C59D22F00FAE9E2 /* ContentView.swift in Sources */,
276 | C00592122C59D22F00FAE9E2 /* DemoAppApp.swift in Sources */,
277 | );
278 | runOnlyForDeploymentPostprocessing = 0;
279 | };
280 | C005921A2C59D23000FAE9E2 /* Sources */ = {
281 | isa = PBXSourcesBuildPhase;
282 | buildActionMask = 2147483647;
283 | files = (
284 | C00592232C59D23100FAE9E2 /* DemoAppTests.swift in Sources */,
285 | );
286 | runOnlyForDeploymentPostprocessing = 0;
287 | };
288 | C00592242C59D23100FAE9E2 /* Sources */ = {
289 | isa = PBXSourcesBuildPhase;
290 | buildActionMask = 2147483647;
291 | files = (
292 | C005922D2C59D23100FAE9E2 /* DemoAppUITests.swift in Sources */,
293 | C005922F2C59D23100FAE9E2 /* DemoAppUITestsLaunchTests.swift in Sources */,
294 | );
295 | runOnlyForDeploymentPostprocessing = 0;
296 | };
297 | /* End PBXSourcesBuildPhase section */
298 |
299 | /* Begin PBXTargetDependency section */
300 | C00592202C59D23000FAE9E2 /* PBXTargetDependency */ = {
301 | isa = PBXTargetDependency;
302 | target = C005920D2C59D22F00FAE9E2 /* DemoApp */;
303 | targetProxy = C005921F2C59D23000FAE9E2 /* PBXContainerItemProxy */;
304 | };
305 | C005922A2C59D23100FAE9E2 /* PBXTargetDependency */ = {
306 | isa = PBXTargetDependency;
307 | target = C005920D2C59D22F00FAE9E2 /* DemoApp */;
308 | targetProxy = C00592292C59D23100FAE9E2 /* PBXContainerItemProxy */;
309 | };
310 | /* End PBXTargetDependency section */
311 |
312 | /* Begin XCBuildConfiguration section */
313 | C00592302C59D23100FAE9E2 /* Debug */ = {
314 | isa = XCBuildConfiguration;
315 | buildSettings = {
316 | ALWAYS_SEARCH_USER_PATHS = NO;
317 | ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
318 | CLANG_ANALYZER_NONNULL = YES;
319 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
320 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
321 | CLANG_ENABLE_MODULES = YES;
322 | CLANG_ENABLE_OBJC_ARC = YES;
323 | CLANG_ENABLE_OBJC_WEAK = YES;
324 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
325 | CLANG_WARN_BOOL_CONVERSION = YES;
326 | CLANG_WARN_COMMA = YES;
327 | CLANG_WARN_CONSTANT_CONVERSION = YES;
328 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
329 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
330 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
331 | CLANG_WARN_EMPTY_BODY = YES;
332 | CLANG_WARN_ENUM_CONVERSION = YES;
333 | CLANG_WARN_INFINITE_RECURSION = YES;
334 | CLANG_WARN_INT_CONVERSION = YES;
335 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
336 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
337 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
338 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
339 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
340 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
341 | CLANG_WARN_STRICT_PROTOTYPES = YES;
342 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
343 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
344 | CLANG_WARN_UNREACHABLE_CODE = YES;
345 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
346 | COPY_PHASE_STRIP = NO;
347 | DEBUG_INFORMATION_FORMAT = dwarf;
348 | ENABLE_STRICT_OBJC_MSGSEND = YES;
349 | ENABLE_TESTABILITY = YES;
350 | ENABLE_USER_SCRIPT_SANDBOXING = YES;
351 | GCC_C_LANGUAGE_STANDARD = gnu17;
352 | GCC_DYNAMIC_NO_PIC = NO;
353 | GCC_NO_COMMON_BLOCKS = YES;
354 | GCC_OPTIMIZATION_LEVEL = 0;
355 | GCC_PREPROCESSOR_DEFINITIONS = (
356 | "DEBUG=1",
357 | "$(inherited)",
358 | );
359 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
360 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
361 | GCC_WARN_UNDECLARED_SELECTOR = YES;
362 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
363 | GCC_WARN_UNUSED_FUNCTION = YES;
364 | GCC_WARN_UNUSED_VARIABLE = YES;
365 | IPHONEOS_DEPLOYMENT_TARGET = 17.5;
366 | LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
367 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
368 | MTL_FAST_MATH = YES;
369 | ONLY_ACTIVE_ARCH = YES;
370 | SDKROOT = iphoneos;
371 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
372 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
373 | };
374 | name = Debug;
375 | };
376 | C00592312C59D23100FAE9E2 /* Release */ = {
377 | isa = XCBuildConfiguration;
378 | buildSettings = {
379 | ALWAYS_SEARCH_USER_PATHS = NO;
380 | ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
381 | CLANG_ANALYZER_NONNULL = YES;
382 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
383 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
384 | CLANG_ENABLE_MODULES = YES;
385 | CLANG_ENABLE_OBJC_ARC = YES;
386 | CLANG_ENABLE_OBJC_WEAK = YES;
387 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
388 | CLANG_WARN_BOOL_CONVERSION = YES;
389 | CLANG_WARN_COMMA = YES;
390 | CLANG_WARN_CONSTANT_CONVERSION = YES;
391 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
392 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
393 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
394 | CLANG_WARN_EMPTY_BODY = YES;
395 | CLANG_WARN_ENUM_CONVERSION = YES;
396 | CLANG_WARN_INFINITE_RECURSION = YES;
397 | CLANG_WARN_INT_CONVERSION = YES;
398 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
399 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
400 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
401 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
402 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
403 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
404 | CLANG_WARN_STRICT_PROTOTYPES = YES;
405 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
406 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
407 | CLANG_WARN_UNREACHABLE_CODE = YES;
408 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
409 | COPY_PHASE_STRIP = NO;
410 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
411 | ENABLE_NS_ASSERTIONS = NO;
412 | ENABLE_STRICT_OBJC_MSGSEND = YES;
413 | ENABLE_USER_SCRIPT_SANDBOXING = YES;
414 | GCC_C_LANGUAGE_STANDARD = gnu17;
415 | GCC_NO_COMMON_BLOCKS = YES;
416 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
417 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
418 | GCC_WARN_UNDECLARED_SELECTOR = YES;
419 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
420 | GCC_WARN_UNUSED_FUNCTION = YES;
421 | GCC_WARN_UNUSED_VARIABLE = YES;
422 | IPHONEOS_DEPLOYMENT_TARGET = 17.5;
423 | LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
424 | MTL_ENABLE_DEBUG_INFO = NO;
425 | MTL_FAST_MATH = YES;
426 | SDKROOT = iphoneos;
427 | SWIFT_COMPILATION_MODE = wholemodule;
428 | VALIDATE_PRODUCT = YES;
429 | };
430 | name = Release;
431 | };
432 | C00592332C59D23100FAE9E2 /* Debug */ = {
433 | isa = XCBuildConfiguration;
434 | buildSettings = {
435 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
436 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
437 | CODE_SIGN_STYLE = Automatic;
438 | CURRENT_PROJECT_VERSION = 1;
439 | DEVELOPMENT_ASSET_PATHS = "\"DemoApp/Preview Content\"";
440 | ENABLE_PREVIEWS = YES;
441 | GENERATE_INFOPLIST_FILE = YES;
442 | INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
443 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
444 | INFOPLIST_KEY_UILaunchScreen_Generation = YES;
445 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
446 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
447 | LD_RUNPATH_SEARCH_PATHS = (
448 | "$(inherited)",
449 | "@executable_path/Frameworks",
450 | );
451 | MARKETING_VERSION = 1.0;
452 | PRODUCT_BUNDLE_IDENTIFIER = com.qutian.DemoApp;
453 | PRODUCT_NAME = "$(TARGET_NAME)";
454 | SWIFT_EMIT_LOC_STRINGS = YES;
455 | SWIFT_VERSION = 5.0;
456 | TARGETED_DEVICE_FAMILY = "1,2";
457 | };
458 | name = Debug;
459 | };
460 | C00592342C59D23100FAE9E2 /* Release */ = {
461 | isa = XCBuildConfiguration;
462 | buildSettings = {
463 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
464 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
465 | CODE_SIGN_STYLE = Automatic;
466 | CURRENT_PROJECT_VERSION = 1;
467 | DEVELOPMENT_ASSET_PATHS = "\"DemoApp/Preview Content\"";
468 | ENABLE_PREVIEWS = YES;
469 | GENERATE_INFOPLIST_FILE = YES;
470 | INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
471 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
472 | INFOPLIST_KEY_UILaunchScreen_Generation = YES;
473 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
474 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
475 | LD_RUNPATH_SEARCH_PATHS = (
476 | "$(inherited)",
477 | "@executable_path/Frameworks",
478 | );
479 | MARKETING_VERSION = 1.0;
480 | PRODUCT_BUNDLE_IDENTIFIER = com.qutian.DemoApp;
481 | PRODUCT_NAME = "$(TARGET_NAME)";
482 | SWIFT_EMIT_LOC_STRINGS = YES;
483 | SWIFT_VERSION = 5.0;
484 | TARGETED_DEVICE_FAMILY = "1,2";
485 | };
486 | name = Release;
487 | };
488 | C00592362C59D23100FAE9E2 /* Debug */ = {
489 | isa = XCBuildConfiguration;
490 | buildSettings = {
491 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
492 | BUNDLE_LOADER = "$(TEST_HOST)";
493 | CODE_SIGN_STYLE = Automatic;
494 | CURRENT_PROJECT_VERSION = 1;
495 | GENERATE_INFOPLIST_FILE = YES;
496 | IPHONEOS_DEPLOYMENT_TARGET = 17.5;
497 | MARKETING_VERSION = 1.0;
498 | PRODUCT_BUNDLE_IDENTIFIER = com.qutian.DemoAppTests;
499 | PRODUCT_NAME = "$(TARGET_NAME)";
500 | SWIFT_EMIT_LOC_STRINGS = NO;
501 | SWIFT_VERSION = 5.0;
502 | TARGETED_DEVICE_FAMILY = "1,2";
503 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/DemoApp.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/DemoApp";
504 | };
505 | name = Debug;
506 | };
507 | C00592372C59D23100FAE9E2 /* Release */ = {
508 | isa = XCBuildConfiguration;
509 | buildSettings = {
510 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
511 | BUNDLE_LOADER = "$(TEST_HOST)";
512 | CODE_SIGN_STYLE = Automatic;
513 | CURRENT_PROJECT_VERSION = 1;
514 | GENERATE_INFOPLIST_FILE = YES;
515 | IPHONEOS_DEPLOYMENT_TARGET = 17.5;
516 | MARKETING_VERSION = 1.0;
517 | PRODUCT_BUNDLE_IDENTIFIER = com.qutian.DemoAppTests;
518 | PRODUCT_NAME = "$(TARGET_NAME)";
519 | SWIFT_EMIT_LOC_STRINGS = NO;
520 | SWIFT_VERSION = 5.0;
521 | TARGETED_DEVICE_FAMILY = "1,2";
522 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/DemoApp.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/DemoApp";
523 | };
524 | name = Release;
525 | };
526 | C00592392C59D23100FAE9E2 /* Debug */ = {
527 | isa = XCBuildConfiguration;
528 | buildSettings = {
529 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
530 | CODE_SIGN_STYLE = Automatic;
531 | CURRENT_PROJECT_VERSION = 1;
532 | GENERATE_INFOPLIST_FILE = YES;
533 | MARKETING_VERSION = 1.0;
534 | PRODUCT_BUNDLE_IDENTIFIER = com.qutian.DemoAppUITests;
535 | PRODUCT_NAME = "$(TARGET_NAME)";
536 | SWIFT_EMIT_LOC_STRINGS = NO;
537 | SWIFT_VERSION = 5.0;
538 | TARGETED_DEVICE_FAMILY = "1,2";
539 | TEST_TARGET_NAME = DemoApp;
540 | };
541 | name = Debug;
542 | };
543 | C005923A2C59D23100FAE9E2 /* Release */ = {
544 | isa = XCBuildConfiguration;
545 | buildSettings = {
546 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
547 | CODE_SIGN_STYLE = Automatic;
548 | CURRENT_PROJECT_VERSION = 1;
549 | GENERATE_INFOPLIST_FILE = YES;
550 | MARKETING_VERSION = 1.0;
551 | PRODUCT_BUNDLE_IDENTIFIER = com.qutian.DemoAppUITests;
552 | PRODUCT_NAME = "$(TARGET_NAME)";
553 | SWIFT_EMIT_LOC_STRINGS = NO;
554 | SWIFT_VERSION = 5.0;
555 | TARGETED_DEVICE_FAMILY = "1,2";
556 | TEST_TARGET_NAME = DemoApp;
557 | };
558 | name = Release;
559 | };
560 | /* End XCBuildConfiguration section */
561 |
562 | /* Begin XCConfigurationList section */
563 | C00592092C59D22F00FAE9E2 /* Build configuration list for PBXProject "DemoApp" */ = {
564 | isa = XCConfigurationList;
565 | buildConfigurations = (
566 | C00592302C59D23100FAE9E2 /* Debug */,
567 | C00592312C59D23100FAE9E2 /* Release */,
568 | );
569 | defaultConfigurationIsVisible = 0;
570 | defaultConfigurationName = Release;
571 | };
572 | C00592322C59D23100FAE9E2 /* Build configuration list for PBXNativeTarget "DemoApp" */ = {
573 | isa = XCConfigurationList;
574 | buildConfigurations = (
575 | C00592332C59D23100FAE9E2 /* Debug */,
576 | C00592342C59D23100FAE9E2 /* Release */,
577 | );
578 | defaultConfigurationIsVisible = 0;
579 | defaultConfigurationName = Release;
580 | };
581 | C00592352C59D23100FAE9E2 /* Build configuration list for PBXNativeTarget "DemoAppTests" */ = {
582 | isa = XCConfigurationList;
583 | buildConfigurations = (
584 | C00592362C59D23100FAE9E2 /* Debug */,
585 | C00592372C59D23100FAE9E2 /* Release */,
586 | );
587 | defaultConfigurationIsVisible = 0;
588 | defaultConfigurationName = Release;
589 | };
590 | C00592382C59D23100FAE9E2 /* Build configuration list for PBXNativeTarget "DemoAppUITests" */ = {
591 | isa = XCConfigurationList;
592 | buildConfigurations = (
593 | C00592392C59D23100FAE9E2 /* Debug */,
594 | C005923A2C59D23100FAE9E2 /* Release */,
595 | );
596 | defaultConfigurationIsVisible = 0;
597 | defaultConfigurationName = Release;
598 | };
599 | /* End XCConfigurationList section */
600 |
601 | /* Begin XCSwiftPackageProductDependency section */
602 | C005923D2C59D5BC00FAE9E2 /* ContributionChart */ = {
603 | isa = XCSwiftPackageProductDependency;
604 | productName = ContributionChart;
605 | };
606 | /* End XCSwiftPackageProductDependency section */
607 | };
608 | rootObject = C00592062C59D22F00FAE9E2 /* Project object */;
609 | }
610 |
--------------------------------------------------------------------------------
/DemoApp/DemoApp/DemoApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/DemoApp/DemoApp/DemoApp.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/DemoApp/DemoApp/DemoApp/Assets.xcassets/AccentColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "idiom" : "universal"
5 | }
6 | ],
7 | "info" : {
8 | "author" : "xcode",
9 | "version" : 1
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/DemoApp/DemoApp/DemoApp/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "platform" : "ios",
6 | "size" : "1024x1024"
7 | }
8 | ],
9 | "info" : {
10 | "author" : "xcode",
11 | "version" : 1
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/DemoApp/DemoApp/DemoApp/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/DemoApp/DemoApp/DemoApp/ContentView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ContentView.swift
3 | // DemoApp
4 | //
5 | // Created by Qu Tian on 2024/7/31.
6 | //
7 |
8 | import SwiftUI
9 | import ContributionChart
10 |
11 |
12 | struct ContentView: View {
13 | @State private var data = [0.87, 0.17, 0.6, 0.21, 0.88, 0.41, 0.37, 0.39, 0.34, 0.91, 0.45, 0.51, 0.1, 0.97, 0.32, 0.72, 0.15, 0.2, 0.87, 0.86, 0.35, 0.12, 0.77, 0.56]
14 |
15 | var body: some View {
16 | RecordBlock {
17 | ContributionChartView(data: data, rows: 3, columns: 8, targetValue: 1.0)
18 | }
19 | .frame(width: 210, height: 95)
20 | .padding()
21 | }
22 | }
23 |
24 | fileprivate struct RecordBlock: View {
25 | @ViewBuilder var content:Content
26 |
27 | var body: some View {
28 | ZStack {
29 | RoundedRectangle(cornerRadius: 16)
30 | .fill(Color.white)
31 | .shadow(color: Color.black.opacity(0.3), radius: 16.0, y: 16.0)
32 | content
33 | }
34 | }
35 | }
36 |
37 |
38 | #Preview {
39 | ContentView()
40 | }
41 |
--------------------------------------------------------------------------------
/DemoApp/DemoApp/DemoApp/DemoAppApp.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DemoAppApp.swift
3 | // DemoApp
4 | //
5 | // Created by Qu Tian on 2024/7/31.
6 | //
7 |
8 | import SwiftUI
9 |
10 | @main
11 | struct DemoAppApp: App {
12 | var body: some Scene {
13 | WindowGroup {
14 | ContentView()
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/DemoApp/DemoApp/DemoApp/Preview Content/Preview Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/DemoApp/DemoApp/DemoAppTests/DemoAppTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DemoAppTests.swift
3 | // DemoAppTests
4 | //
5 | // Created by Qu Tian on 2024/7/31.
6 | //
7 |
8 | import XCTest
9 | @testable import DemoApp
10 |
11 | final class DemoAppTests: XCTestCase {
12 |
13 | override func setUpWithError() throws {
14 | // Put setup code here. This method is called before the invocation of each test method in the class.
15 | }
16 |
17 | override func tearDownWithError() throws {
18 | // Put teardown code here. This method is called after the invocation of each test method in the class.
19 | }
20 |
21 | func testExample() throws {
22 | // This is an example of a functional test case.
23 | // Use XCTAssert and related functions to verify your tests produce the correct results.
24 | // Any test you write for XCTest can be annotated as throws and async.
25 | // Mark your test throws to produce an unexpected failure when your test encounters an uncaught error.
26 | // Mark your test async to allow awaiting for asynchronous code to complete. Check the results with assertions afterwards.
27 | }
28 |
29 | func testPerformanceExample() throws {
30 | // This is an example of a performance test case.
31 | self.measure {
32 | // Put the code you want to measure the time of here.
33 | }
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/DemoApp/DemoApp/DemoAppUITests/DemoAppUITests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DemoAppUITests.swift
3 | // DemoAppUITests
4 | //
5 | // Created by Qu Tian on 2024/7/31.
6 | //
7 |
8 | import XCTest
9 |
10 | final class DemoAppUITests: XCTestCase {
11 |
12 | override func setUpWithError() throws {
13 | // Put setup code here. This method is called before the invocation of each test method in the class.
14 |
15 | // In UI tests it is usually best to stop immediately when a failure occurs.
16 | continueAfterFailure = false
17 |
18 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this.
19 | }
20 |
21 | override func tearDownWithError() throws {
22 | // Put teardown code here. This method is called after the invocation of each test method in the class.
23 | }
24 |
25 | func testExample() throws {
26 | // UI tests must launch the application that they test.
27 | let app = XCUIApplication()
28 | app.launch()
29 |
30 | // Use XCTAssert and related functions to verify your tests produce the correct results.
31 | }
32 |
33 | func testLaunchPerformance() throws {
34 | if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 7.0, *) {
35 | // This measures how long it takes to launch your application.
36 | measure(metrics: [XCTApplicationLaunchMetric()]) {
37 | XCUIApplication().launch()
38 | }
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/DemoApp/DemoApp/DemoAppUITests/DemoAppUITestsLaunchTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DemoAppUITestsLaunchTests.swift
3 | // DemoAppUITests
4 | //
5 | // Created by Qu Tian on 2024/7/31.
6 | //
7 |
8 | import XCTest
9 |
10 | final class DemoAppUITestsLaunchTests: XCTestCase {
11 |
12 | override class var runsForEachTargetApplicationUIConfiguration: Bool {
13 | true
14 | }
15 |
16 | override func setUpWithError() throws {
17 | continueAfterFailure = false
18 | }
19 |
20 | func testLaunch() throws {
21 | let app = XCUIApplication()
22 | app.launch()
23 |
24 | // Insert steps here to perform after app launch but before taking a screenshot,
25 | // such as logging into a test account or navigating somewhere in the app
26 |
27 | let attachment = XCTAttachment(screenshot: app.screenshot())
28 | attachment.name = "Launch Screen"
29 | attachment.lifetime = .keepAlways
30 | add(attachment)
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version: 5.6
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: "ContributionChart",
8 | platforms: [
9 | .iOS(.v13), .macOS(.v10_15), .tvOS(.v13), .watchOS(.v6)
10 | ],
11 | products: [
12 | // Products define the executables and libraries a package produces, and make them visible to other packages.
13 | .library(
14 | name: "ContributionChart",
15 | targets: ["ContributionChart"]),
16 | ],
17 | dependencies: [
18 | // Dependencies declare other packages that this package depends on.
19 | // .package(url: /* package url */, from: "1.0.0"),
20 | ],
21 | targets: [
22 | // Targets are the basic building blocks of a package. A target can define a module or a test suite.
23 | // Targets can depend on other targets in this package, and on products in packages this package depends on.
24 | .target(
25 | name: "ContributionChart",
26 | dependencies: [])
27 | ]
28 | )
29 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ContributionChart
2 |
3 | A contribution chart (aka. heatmap, GitHub-like) library for iOS, macOS, and watchOS.
4 |
5 | 100% written in SwiftUI.
6 |
7 | 

8 |
9 | - [It Supports](#it-supports)
10 | * [Custom Block Number](#custom-block-number)
11 | * [Custom Block Color](#custom-block-color)
12 | * [Dark mode](#dark-mode)
13 | - [Installation](#installation)
14 | - [Usage](#usage)
15 | - [Demo Code](#demo-code)
16 |
17 | Updates
18 | - 7/31/24 Support input array with any length and add a demo app inside.
19 |
20 | # It Supports
21 | ## Custom Block Number
22 | Of course, you can also custom the **size** of blocks and **spacing** between blocks.
23 |
24 |
25 |
26 | ## Custom Block Color
27 | Here are examples using **system colors** as below, and you can custom any color you like.
28 |
29 |
30 |
31 | ## Dark mode
32 | Adjust to dark mode color automatically.
33 |
34 |
35 |
36 | # Installation
37 | Require iOS 13, macOS 10.15, watchOS 6 and Xcode 11 or higher.
38 | In Xcode go to `File -> Swift Packages -> Add Package Dependency`
39 | and paste in the repo's url:
40 |
41 | `https://github.com/VIkill33/SwiftUI-ContributionChart.git`
42 |
43 | Or you can download the code of this repo, then `Add Local...` in Xcode, and open the folder of the repo.
44 |
45 | # Usage
46 | - Import this package after you installed by `import ContributionChart`
47 | - Use the chart like
48 | ```swift
49 | ContributionChartView(data: yourData,
50 | rows: yourRows,
51 | columns: yourColumns,
52 | targetValue: yourTargetValue,
53 | blockColor: .green)
54 | ```
55 | yourData is (**a double array**), and the targetValue is recommanded to set to the max value of the array.
56 |
57 | The color of a block will appear as exactly the color as parameter `blockColor` when its value is equal to `targetValue`, and appears light gray when is equal to zero.
58 |
59 | The top-Leading block represents the first value in array, while the bottom-trailing represents the last. And the order follows as below:
60 |
61 |
62 |
63 | # Demo Code
64 | ```swift
65 | import SwiftUI
66 | import ContributionChart
67 |
68 | struct ContentView: View {
69 | var data: [Double]
70 | let rows = 7
71 | let columns = 14
72 |
73 | init() {
74 | data = [0.3, 0.4, 0.4, 0.4, 0.1, 0.5, 0.0, 0.1, 0.0, 0.2, 0.2, 0.2, 0.0, 0.2, 0.2, 0.5, 0.4, 0.2, 0.4, 0.5, 0.2, 0.2, 0.4, 0.3, 0.3, 0.2, 0.4, 0.0, 0.0, 0.5, 0.4, 0.3, 0.5, 0.3, 0.0, 0.0, 0.1, 0.0, 0.2, 0.3, 0.0, 0.0, 0.0, 0.5, 0.3, 0.3, 0.0, 0.3, 0.0, 0.5, 0.3, 0.3, 0.4, 0.5, 0.5, 0.3, 0.4, 0.1, 0.4, 0.2, 0.5, 0.1, 0.4, 0.2, 0.5, 0.4, 0.3, 0.5, 0.0, 0.4, 0.3, 0.2, 0.1, 0.5, 0.2, 0.0, 0.2, 0.5, 0.5, 0.3, 0.4, 0.0, 0.3, 0.3, 0.1, 0.2, 0.5, 0.2, 0.1, 0.4, 0.4, 0.0, 0.5, 0.3, 0.3, 0.5, 0.0, 0.2]
75 | }
76 |
77 | var body: some View {
78 | ContributionChartView(data: data,
79 | rows: rows,
80 | columns: columns,
81 | targetValue: 0.5,
82 | blockColor: .green)
83 | }
84 | }
85 |
86 |
87 | struct ContentView_Previews: PreviewProvider {
88 | static var previews: some View {
89 | ContentView()
90 | }
91 | }
92 |
93 | ```
94 |
--------------------------------------------------------------------------------
/Sources/ContributionChart/ContributionChartTest.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SwiftUIView.swift
3 | //
4 | //
5 | // Created by Vikill Blacks on 2023/2/2.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct SwiftUIView: View {
11 | var body: some View {
12 | ContributionChartView(data: [0,3,4,2,4,2,5,1,3], rows: 3, columns: 3, targetValue: 5, RectangleRadius: 2.0)
13 | }
14 | }
15 |
16 | struct SwiftUIView_Previews: PreviewProvider {
17 | static var previews: some View {
18 | SwiftUIView()
19 | //.preferredColorScheme(.dark)
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Sources/ContributionChart/ContributionChartView.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 |
3 | @available(iOS 13, macOS 10.15, watchOS 6, *)
4 | public struct ContributionChartView: View {
5 |
6 | var data: [Double]
7 | var rows: Int
8 | var columns: Int
9 | var targetValue: Double
10 | var blockColor: Color = Color.green
11 | var blockBackgroundColor: Color = Color.background
12 |
13 | var heatMapRectangleWidth: Double = 20.0
14 | var heatMapRectangleSpacing: Double = 2.0
15 | var heatMapRectangleRadius: Double = 5.0
16 |
17 | public init(data: [Double], rows: Int, columns: Int, targetValue: Double, blockColor: Color = Color.green, blockBackgroundColor: Color, RectangleWidth: Double = 20.0, RectangleSpacing: Double = 2.0, RectangleRadius: Double = 5.0){
18 | self.data = data
19 | self.rows = rows
20 | self.columns = columns
21 | self.targetValue = targetValue
22 | self.blockColor = blockColor
23 | self.blockBackgroundColor = blockBackgroundColor
24 | self.heatMapRectangleWidth = RectangleWidth
25 | self.heatMapRectangleSpacing = RectangleSpacing
26 | self.heatMapRectangleRadius = RectangleRadius
27 | }
28 |
29 | public init(data: [Double], rows: Int, columns: Int, targetValue: Double, blockColor: Color = Color.green, RectangleWidth: Double = 20.0, RectangleSpacing: Double = 2.0, RectangleRadius: Double = 5.0){
30 | self.data = data
31 | self.rows = rows
32 | self.columns = columns
33 | if data.count < rows * columns {
34 | let elementsToAdd = Array(repeating: 0.0, count: rows * columns - data.count)
35 | self.data.append(contentsOf: elementsToAdd)
36 | }
37 | self.targetValue = targetValue
38 | self.blockColor = blockColor
39 | self.heatMapRectangleWidth = RectangleWidth
40 | self.heatMapRectangleSpacing = RectangleSpacing
41 | self.heatMapRectangleRadius = RectangleRadius
42 | }
43 |
44 | public var body: some View {
45 | VStack {
46 | // Chart
47 | GeometryReader { geo in
48 | ZStack {
49 | HStack(spacing: heatMapRectangleSpacing) {
50 | ForEach(0.. Double {
110 | let opacityRatio: Double = Double(rowData[index]) / Double(targetValue)
111 | return opacityRatio > 1.0 ? 1.0 : opacityRatio
112 | }
113 |
114 | }
115 |
116 | extension Color {
117 | init(hexString: String) {
118 | let hex = hexString.trimmingCharacters(in: CharacterSet.alphanumerics.inverted)
119 | var int = UInt64()
120 | Scanner(string: hex).scanHexInt64(&int)
121 | let r, g, b: UInt64
122 | switch hex.count {
123 | case 3: // RGB (12-bit)
124 | (r, g, b) = ((int >> 8) * 17, (int >> 4 & 0xF) * 17, (int & 0xF) * 17)
125 | case 6: // RGB (24-bit)
126 | (r, g, b) = (int >> 16, int >> 8 & 0xFF, int & 0xFF)
127 | case 8: // ARGB (32-bit)
128 | (r, g, b) = (int >> 16 & 0xFF, int >> 8 & 0xFF, int & 0xFF)
129 | default:
130 | (r, g, b) = (0, 0, 0)
131 | }
132 | self.init(red: Double(r) / 255, green: Double(g) / 255, blue: Double(b) / 255)
133 | }
134 | #if os(macOS)
135 | static let background = Color(NSColor.windowBackgroundColor)
136 | static let secondaryBackground = Color(NSColor.underPageBackgroundColor)
137 | static let tertiaryBackground = Color(NSColor.controlBackgroundColor)
138 | #endif
139 | #if os(iOS)
140 | static let background = Color(UIColor.systemBackground)
141 | static let secondaryBackground = Color(UIColor.secondarySystemBackground)
142 | static let tertiaryBackground = Color(UIColor.tertiarySystemBackground)
143 | #endif
144 | #if os(watchOS)
145 | static let background = Color.black
146 | #endif
147 | }
148 |
--------------------------------------------------------------------------------