Repository: VIkill33/SwiftUI-ContributionChart Branch: main Commit: 1a5456abe211 Files: 18 Total size: 40.0 KB Directory structure: gitextract_at9suabs/ ├── .gitignore ├── .swiftpm/ │ └── xcode/ │ └── package.xcworkspace/ │ └── xcshareddata/ │ └── IDEWorkspaceChecks.plist ├── DemoApp/ │ └── DemoApp/ │ ├── DemoApp/ │ │ ├── Assets.xcassets/ │ │ │ ├── AccentColor.colorset/ │ │ │ │ └── Contents.json │ │ │ ├── AppIcon.appiconset/ │ │ │ │ └── Contents.json │ │ │ └── Contents.json │ │ ├── ContentView.swift │ │ ├── DemoAppApp.swift │ │ └── Preview Content/ │ │ └── Preview Assets.xcassets/ │ │ └── Contents.json │ ├── DemoApp.xcodeproj/ │ │ ├── project.pbxproj │ │ └── project.xcworkspace/ │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata/ │ │ └── IDEWorkspaceChecks.plist │ ├── DemoAppTests/ │ │ └── DemoAppTests.swift │ └── DemoAppUITests/ │ ├── DemoAppUITests.swift │ └── DemoAppUITestsLaunchTests.swift ├── Package.swift ├── README.md └── Sources/ └── ContributionChart/ ├── ContributionChartTest.swift └── ContributionChartView.swift ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ .DS_Store /.build /Packages /*.xcodeproj xcuserdata/ DerivedData/ .swiftpm/config/registries.json .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata .netrc ================================================ FILE: .swiftpm/xcode/package.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist ================================================ IDEDidComputeMac32BitWarning ================================================ FILE: DemoApp/DemoApp/DemoApp/Assets.xcassets/AccentColor.colorset/Contents.json ================================================ { "colors" : [ { "idiom" : "universal" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: DemoApp/DemoApp/DemoApp/Assets.xcassets/AppIcon.appiconset/Contents.json ================================================ { "images" : [ { "idiom" : "universal", "platform" : "ios", "size" : "1024x1024" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: DemoApp/DemoApp/DemoApp/Assets.xcassets/Contents.json ================================================ { "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: DemoApp/DemoApp/DemoApp/ContentView.swift ================================================ // // ContentView.swift // DemoApp // // Created by Qu Tian on 2024/7/31. // import SwiftUI import ContributionChart struct ContentView: View { @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] var body: some View { RecordBlock { ContributionChartView(data: data, rows: 3, columns: 8, targetValue: 1.0) } .frame(width: 210, height: 95) .padding() } } fileprivate struct RecordBlock: View { @ViewBuilder var content:Content var body: some View { ZStack { RoundedRectangle(cornerRadius: 16) .fill(Color.white) .shadow(color: Color.black.opacity(0.3), radius: 16.0, y: 16.0) content } } } #Preview { ContentView() } ================================================ FILE: DemoApp/DemoApp/DemoApp/DemoAppApp.swift ================================================ // // DemoAppApp.swift // DemoApp // // Created by Qu Tian on 2024/7/31. // import SwiftUI @main struct DemoAppApp: App { var body: some Scene { WindowGroup { ContentView() } } } ================================================ FILE: DemoApp/DemoApp/DemoApp/Preview Content/Preview Assets.xcassets/Contents.json ================================================ { "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: DemoApp/DemoApp/DemoApp.xcodeproj/project.pbxproj ================================================ // !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 56; objects = { /* Begin PBXBuildFile section */ C00592122C59D22F00FAE9E2 /* DemoAppApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = C00592112C59D22F00FAE9E2 /* DemoAppApp.swift */; }; C00592142C59D22F00FAE9E2 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C00592132C59D22F00FAE9E2 /* ContentView.swift */; }; C00592162C59D23000FAE9E2 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C00592152C59D23000FAE9E2 /* Assets.xcassets */; }; C00592192C59D23000FAE9E2 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C00592182C59D23000FAE9E2 /* Preview Assets.xcassets */; }; C00592232C59D23100FAE9E2 /* DemoAppTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C00592222C59D23100FAE9E2 /* DemoAppTests.swift */; }; C005922D2C59D23100FAE9E2 /* DemoAppUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C005922C2C59D23100FAE9E2 /* DemoAppUITests.swift */; }; C005922F2C59D23100FAE9E2 /* DemoAppUITestsLaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C005922E2C59D23100FAE9E2 /* DemoAppUITestsLaunchTests.swift */; }; C005923E2C59D5BC00FAE9E2 /* ContributionChart in Frameworks */ = {isa = PBXBuildFile; productRef = C005923D2C59D5BC00FAE9E2 /* ContributionChart */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ C005921F2C59D23000FAE9E2 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = C00592062C59D22F00FAE9E2 /* Project object */; proxyType = 1; remoteGlobalIDString = C005920D2C59D22F00FAE9E2; remoteInfo = DemoApp; }; C00592292C59D23100FAE9E2 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = C00592062C59D22F00FAE9E2 /* Project object */; proxyType = 1; remoteGlobalIDString = C005920D2C59D22F00FAE9E2; remoteInfo = DemoApp; }; /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ C005920E2C59D22F00FAE9E2 /* DemoApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = DemoApp.app; sourceTree = BUILT_PRODUCTS_DIR; }; C00592112C59D22F00FAE9E2 /* DemoAppApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DemoAppApp.swift; sourceTree = ""; }; C00592132C59D22F00FAE9E2 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; C00592152C59D23000FAE9E2 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; C00592182C59D23000FAE9E2 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; C005921E2C59D23000FAE9E2 /* DemoAppTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = DemoAppTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; C00592222C59D23100FAE9E2 /* DemoAppTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DemoAppTests.swift; sourceTree = ""; }; C00592282C59D23100FAE9E2 /* DemoAppUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = DemoAppUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; C005922C2C59D23100FAE9E2 /* DemoAppUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DemoAppUITests.swift; sourceTree = ""; }; C005922E2C59D23100FAE9E2 /* DemoAppUITestsLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DemoAppUITestsLaunchTests.swift; sourceTree = ""; }; C005923C2C59D57200FAE9E2 /* SwiftUI-ContributionChart */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = "SwiftUI-ContributionChart"; path = ../..; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ C005920B2C59D22F00FAE9E2 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( C005923E2C59D5BC00FAE9E2 /* ContributionChart in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; C005921B2C59D23000FAE9E2 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; C00592252C59D23100FAE9E2 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ C00592052C59D22F00FAE9E2 = { isa = PBXGroup; children = ( C00592102C59D22F00FAE9E2 /* DemoApp */, C00592212C59D23100FAE9E2 /* DemoAppTests */, C005922B2C59D23100FAE9E2 /* DemoAppUITests */, C005920F2C59D22F00FAE9E2 /* Products */, C005923B2C59D57200FAE9E2 /* Frameworks */, ); sourceTree = ""; }; C005920F2C59D22F00FAE9E2 /* Products */ = { isa = PBXGroup; children = ( C005920E2C59D22F00FAE9E2 /* DemoApp.app */, C005921E2C59D23000FAE9E2 /* DemoAppTests.xctest */, C00592282C59D23100FAE9E2 /* DemoAppUITests.xctest */, ); name = Products; sourceTree = ""; }; C00592102C59D22F00FAE9E2 /* DemoApp */ = { isa = PBXGroup; children = ( C00592112C59D22F00FAE9E2 /* DemoAppApp.swift */, C00592132C59D22F00FAE9E2 /* ContentView.swift */, C00592152C59D23000FAE9E2 /* Assets.xcassets */, C00592172C59D23000FAE9E2 /* Preview Content */, ); path = DemoApp; sourceTree = ""; }; C00592172C59D23000FAE9E2 /* Preview Content */ = { isa = PBXGroup; children = ( C00592182C59D23000FAE9E2 /* Preview Assets.xcassets */, ); path = "Preview Content"; sourceTree = ""; }; C00592212C59D23100FAE9E2 /* DemoAppTests */ = { isa = PBXGroup; children = ( C00592222C59D23100FAE9E2 /* DemoAppTests.swift */, ); path = DemoAppTests; sourceTree = ""; }; C005922B2C59D23100FAE9E2 /* DemoAppUITests */ = { isa = PBXGroup; children = ( C005922C2C59D23100FAE9E2 /* DemoAppUITests.swift */, C005922E2C59D23100FAE9E2 /* DemoAppUITestsLaunchTests.swift */, ); path = DemoAppUITests; sourceTree = ""; }; C005923B2C59D57200FAE9E2 /* Frameworks */ = { isa = PBXGroup; children = ( C005923C2C59D57200FAE9E2 /* SwiftUI-ContributionChart */, ); name = Frameworks; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ C005920D2C59D22F00FAE9E2 /* DemoApp */ = { isa = PBXNativeTarget; buildConfigurationList = C00592322C59D23100FAE9E2 /* Build configuration list for PBXNativeTarget "DemoApp" */; buildPhases = ( C005920A2C59D22F00FAE9E2 /* Sources */, C005920B2C59D22F00FAE9E2 /* Frameworks */, C005920C2C59D22F00FAE9E2 /* Resources */, ); buildRules = ( ); dependencies = ( ); name = DemoApp; packageProductDependencies = ( C005923D2C59D5BC00FAE9E2 /* ContributionChart */, ); productName = DemoApp; productReference = C005920E2C59D22F00FAE9E2 /* DemoApp.app */; productType = "com.apple.product-type.application"; }; C005921D2C59D23000FAE9E2 /* DemoAppTests */ = { isa = PBXNativeTarget; buildConfigurationList = C00592352C59D23100FAE9E2 /* Build configuration list for PBXNativeTarget "DemoAppTests" */; buildPhases = ( C005921A2C59D23000FAE9E2 /* Sources */, C005921B2C59D23000FAE9E2 /* Frameworks */, C005921C2C59D23000FAE9E2 /* Resources */, ); buildRules = ( ); dependencies = ( C00592202C59D23000FAE9E2 /* PBXTargetDependency */, ); name = DemoAppTests; productName = DemoAppTests; productReference = C005921E2C59D23000FAE9E2 /* DemoAppTests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; C00592272C59D23100FAE9E2 /* DemoAppUITests */ = { isa = PBXNativeTarget; buildConfigurationList = C00592382C59D23100FAE9E2 /* Build configuration list for PBXNativeTarget "DemoAppUITests" */; buildPhases = ( C00592242C59D23100FAE9E2 /* Sources */, C00592252C59D23100FAE9E2 /* Frameworks */, C00592262C59D23100FAE9E2 /* Resources */, ); buildRules = ( ); dependencies = ( C005922A2C59D23100FAE9E2 /* PBXTargetDependency */, ); name = DemoAppUITests; productName = DemoAppUITests; productReference = C00592282C59D23100FAE9E2 /* DemoAppUITests.xctest */; productType = "com.apple.product-type.bundle.ui-testing"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ C00592062C59D22F00FAE9E2 /* Project object */ = { isa = PBXProject; attributes = { BuildIndependentTargetsInParallel = 1; LastSwiftUpdateCheck = 1540; LastUpgradeCheck = 1540; TargetAttributes = { C005920D2C59D22F00FAE9E2 = { CreatedOnToolsVersion = 15.4; }; C005921D2C59D23000FAE9E2 = { CreatedOnToolsVersion = 15.4; TestTargetID = C005920D2C59D22F00FAE9E2; }; C00592272C59D23100FAE9E2 = { CreatedOnToolsVersion = 15.4; TestTargetID = C005920D2C59D22F00FAE9E2; }; }; }; buildConfigurationList = C00592092C59D22F00FAE9E2 /* Build configuration list for PBXProject "DemoApp" */; compatibilityVersion = "Xcode 14.0"; developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, Base, ); mainGroup = C00592052C59D22F00FAE9E2; productRefGroup = C005920F2C59D22F00FAE9E2 /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( C005920D2C59D22F00FAE9E2 /* DemoApp */, C005921D2C59D23000FAE9E2 /* DemoAppTests */, C00592272C59D23100FAE9E2 /* DemoAppUITests */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ C005920C2C59D22F00FAE9E2 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( C00592192C59D23000FAE9E2 /* Preview Assets.xcassets in Resources */, C00592162C59D23000FAE9E2 /* Assets.xcassets in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; C005921C2C59D23000FAE9E2 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; C00592262C59D23100FAE9E2 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ C005920A2C59D22F00FAE9E2 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( C00592142C59D22F00FAE9E2 /* ContentView.swift in Sources */, C00592122C59D22F00FAE9E2 /* DemoAppApp.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; C005921A2C59D23000FAE9E2 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( C00592232C59D23100FAE9E2 /* DemoAppTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; C00592242C59D23100FAE9E2 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( C005922D2C59D23100FAE9E2 /* DemoAppUITests.swift in Sources */, C005922F2C59D23100FAE9E2 /* DemoAppUITestsLaunchTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ C00592202C59D23000FAE9E2 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = C005920D2C59D22F00FAE9E2 /* DemoApp */; targetProxy = C005921F2C59D23000FAE9E2 /* PBXContainerItemProxy */; }; C005922A2C59D23100FAE9E2 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = C005920D2C59D22F00FAE9E2 /* DemoApp */; targetProxy = C00592292C59D23100FAE9E2 /* PBXContainerItemProxy */; }; /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ C00592302C59D23100FAE9E2 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu17; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 17.5; LOCALIZATION_PREFERS_STRING_CATALOGS = YES; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; }; name = Debug; }; C00592312C59D23100FAE9E2 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu17; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 17.5; LOCALIZATION_PREFERS_STRING_CATALOGS = YES; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; SDKROOT = iphoneos; SWIFT_COMPILATION_MODE = wholemodule; VALIDATE_PRODUCT = YES; }; name = Release; }; C00592332C59D23100FAE9E2 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"DemoApp/Preview Content\""; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.qutian.DemoApp; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; }; C00592342C59D23100FAE9E2 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"DemoApp/Preview Content\""; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.qutian.DemoApp; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Release; }; C00592362C59D23100FAE9E2 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; GENERATE_INFOPLIST_FILE = YES; IPHONEOS_DEPLOYMENT_TARGET = 17.5; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.qutian.DemoAppTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/DemoApp.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/DemoApp"; }; name = Debug; }; C00592372C59D23100FAE9E2 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; GENERATE_INFOPLIST_FILE = YES; IPHONEOS_DEPLOYMENT_TARGET = 17.5; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.qutian.DemoAppTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/DemoApp.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/DemoApp"; }; name = Release; }; C00592392C59D23100FAE9E2 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.qutian.DemoAppUITests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; TEST_TARGET_NAME = DemoApp; }; name = Debug; }; C005923A2C59D23100FAE9E2 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.qutian.DemoAppUITests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; TEST_TARGET_NAME = DemoApp; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ C00592092C59D22F00FAE9E2 /* Build configuration list for PBXProject "DemoApp" */ = { isa = XCConfigurationList; buildConfigurations = ( C00592302C59D23100FAE9E2 /* Debug */, C00592312C59D23100FAE9E2 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; C00592322C59D23100FAE9E2 /* Build configuration list for PBXNativeTarget "DemoApp" */ = { isa = XCConfigurationList; buildConfigurations = ( C00592332C59D23100FAE9E2 /* Debug */, C00592342C59D23100FAE9E2 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; C00592352C59D23100FAE9E2 /* Build configuration list for PBXNativeTarget "DemoAppTests" */ = { isa = XCConfigurationList; buildConfigurations = ( C00592362C59D23100FAE9E2 /* Debug */, C00592372C59D23100FAE9E2 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; C00592382C59D23100FAE9E2 /* Build configuration list for PBXNativeTarget "DemoAppUITests" */ = { isa = XCConfigurationList; buildConfigurations = ( C00592392C59D23100FAE9E2 /* Debug */, C005923A2C59D23100FAE9E2 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ /* Begin XCSwiftPackageProductDependency section */ C005923D2C59D5BC00FAE9E2 /* ContributionChart */ = { isa = XCSwiftPackageProductDependency; productName = ContributionChart; }; /* End XCSwiftPackageProductDependency section */ }; rootObject = C00592062C59D22F00FAE9E2 /* Project object */; } ================================================ FILE: DemoApp/DemoApp/DemoApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata ================================================ ================================================ FILE: DemoApp/DemoApp/DemoApp.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist ================================================ IDEDidComputeMac32BitWarning ================================================ FILE: DemoApp/DemoApp/DemoAppTests/DemoAppTests.swift ================================================ // // DemoAppTests.swift // DemoAppTests // // Created by Qu Tian on 2024/7/31. // import XCTest @testable import DemoApp final class DemoAppTests: XCTestCase { override func setUpWithError() throws { // Put setup code here. This method is called before the invocation of each test method in the class. } override func tearDownWithError() throws { // Put teardown code here. This method is called after the invocation of each test method in the class. } func testExample() throws { // This is an example of a functional test case. // Use XCTAssert and related functions to verify your tests produce the correct results. // Any test you write for XCTest can be annotated as throws and async. // Mark your test throws to produce an unexpected failure when your test encounters an uncaught error. // Mark your test async to allow awaiting for asynchronous code to complete. Check the results with assertions afterwards. } func testPerformanceExample() throws { // This is an example of a performance test case. self.measure { // Put the code you want to measure the time of here. } } } ================================================ FILE: DemoApp/DemoApp/DemoAppUITests/DemoAppUITests.swift ================================================ // // DemoAppUITests.swift // DemoAppUITests // // Created by Qu Tian on 2024/7/31. // import XCTest final class DemoAppUITests: XCTestCase { override func setUpWithError() throws { // Put setup code here. This method is called before the invocation of each test method in the class. // In UI tests it is usually best to stop immediately when a failure occurs. continueAfterFailure = false // 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. } override func tearDownWithError() throws { // Put teardown code here. This method is called after the invocation of each test method in the class. } func testExample() throws { // UI tests must launch the application that they test. let app = XCUIApplication() app.launch() // Use XCTAssert and related functions to verify your tests produce the correct results. } func testLaunchPerformance() throws { if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 7.0, *) { // This measures how long it takes to launch your application. measure(metrics: [XCTApplicationLaunchMetric()]) { XCUIApplication().launch() } } } } ================================================ FILE: DemoApp/DemoApp/DemoAppUITests/DemoAppUITestsLaunchTests.swift ================================================ // // DemoAppUITestsLaunchTests.swift // DemoAppUITests // // Created by Qu Tian on 2024/7/31. // import XCTest final class DemoAppUITestsLaunchTests: XCTestCase { override class var runsForEachTargetApplicationUIConfiguration: Bool { true } override func setUpWithError() throws { continueAfterFailure = false } func testLaunch() throws { let app = XCUIApplication() app.launch() // Insert steps here to perform after app launch but before taking a screenshot, // such as logging into a test account or navigating somewhere in the app let attachment = XCTAttachment(screenshot: app.screenshot()) attachment.name = "Launch Screen" attachment.lifetime = .keepAlways add(attachment) } } ================================================ FILE: Package.swift ================================================ // swift-tools-version: 5.6 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription let package = Package( name: "ContributionChart", platforms: [ .iOS(.v13), .macOS(.v10_15), .tvOS(.v13), .watchOS(.v6) ], products: [ // Products define the executables and libraries a package produces, and make them visible to other packages. .library( name: "ContributionChart", targets: ["ContributionChart"]), ], dependencies: [ // Dependencies declare other packages that this package depends on. // .package(url: /* package url */, from: "1.0.0"), ], targets: [ // Targets are the basic building blocks of a package. A target can define a module or a test suite. // Targets can depend on other targets in this package, and on products in packages this package depends on. .target( name: "ContributionChart", dependencies: []) ] ) ================================================ FILE: README.md ================================================ # ContributionChart A contribution chart (aka. heatmap, GitHub-like) library for iOS, macOS, and watchOS. 100% written in SwiftUI. - [It Supports](#it-supports) * [Custom Block Number](#custom-block-number) * [Custom Block Color](#custom-block-color) * [Dark mode](#dark-mode) - [Installation](#installation) - [Usage](#usage) - [Demo Code](#demo-code) Updates - 7/31/24 Support input array with any length and add a demo app inside. # It Supports ## Custom Block Number Of course, you can also custom the **size** of blocks and **spacing** between blocks. ## Custom Block Color Here are examples using **system colors** as below, and you can custom any color you like. ## Dark mode Adjust to dark mode color automatically. # Installation Require iOS 13, macOS 10.15, watchOS 6 and Xcode 11 or higher. In Xcode go to `File -> Swift Packages -> Add Package Dependency` and paste in the repo's url: `https://github.com/VIkill33/SwiftUI-ContributionChart.git` Or you can download the code of this repo, then `Add Local...` in Xcode, and open the folder of the repo. # Usage - Import this package after you installed by `import ContributionChart` - Use the chart like ```swift ContributionChartView(data: yourData, rows: yourRows, columns: yourColumns, targetValue: yourTargetValue, blockColor: .green) ``` yourData is (**a double array**), and the targetValue is recommanded to set to the max value of the array. 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. The top-Leading block represents the first value in array, while the bottom-trailing represents the last. And the order follows as below: # Demo Code ```swift import SwiftUI import ContributionChart struct ContentView: View { var data: [Double] let rows = 7 let columns = 14 init() { 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] } var body: some View { ContributionChartView(data: data, rows: rows, columns: columns, targetValue: 0.5, blockColor: .green) } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } } ``` ================================================ FILE: Sources/ContributionChart/ContributionChartTest.swift ================================================ // // SwiftUIView.swift // // // Created by Vikill Blacks on 2023/2/2. // import SwiftUI struct SwiftUIView: View { var body: some View { ContributionChartView(data: [0,3,4,2,4,2,5,1,3], rows: 3, columns: 3, targetValue: 5, RectangleRadius: 2.0) } } struct SwiftUIView_Previews: PreviewProvider { static var previews: some View { SwiftUIView() //.preferredColorScheme(.dark) } } ================================================ FILE: Sources/ContributionChart/ContributionChartView.swift ================================================ import SwiftUI @available(iOS 13, macOS 10.15, watchOS 6, *) public struct ContributionChartView: View { var data: [Double] var rows: Int var columns: Int var targetValue: Double var blockColor: Color = Color.green var blockBackgroundColor: Color = Color.background var heatMapRectangleWidth: Double = 20.0 var heatMapRectangleSpacing: Double = 2.0 var heatMapRectangleRadius: Double = 5.0 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){ self.data = data self.rows = rows self.columns = columns self.targetValue = targetValue self.blockColor = blockColor self.blockBackgroundColor = blockBackgroundColor self.heatMapRectangleWidth = RectangleWidth self.heatMapRectangleSpacing = RectangleSpacing self.heatMapRectangleRadius = RectangleRadius } 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){ self.data = data self.rows = rows self.columns = columns if data.count < rows * columns { let elementsToAdd = Array(repeating: 0.0, count: rows * columns - data.count) self.data.append(contentsOf: elementsToAdd) } self.targetValue = targetValue self.blockColor = blockColor self.heatMapRectangleWidth = RectangleWidth self.heatMapRectangleSpacing = RectangleSpacing self.heatMapRectangleRadius = RectangleRadius } public var body: some View { VStack { // Chart GeometryReader { geo in ZStack { HStack(spacing: heatMapRectangleSpacing) { ForEach(0.. Double { let opacityRatio: Double = Double(rowData[index]) / Double(targetValue) return opacityRatio > 1.0 ? 1.0 : opacityRatio } } extension Color { init(hexString: String) { let hex = hexString.trimmingCharacters(in: CharacterSet.alphanumerics.inverted) var int = UInt64() Scanner(string: hex).scanHexInt64(&int) let r, g, b: UInt64 switch hex.count { case 3: // RGB (12-bit) (r, g, b) = ((int >> 8) * 17, (int >> 4 & 0xF) * 17, (int & 0xF) * 17) case 6: // RGB (24-bit) (r, g, b) = (int >> 16, int >> 8 & 0xFF, int & 0xFF) case 8: // ARGB (32-bit) (r, g, b) = (int >> 16 & 0xFF, int >> 8 & 0xFF, int & 0xFF) default: (r, g, b) = (0, 0, 0) } self.init(red: Double(r) / 255, green: Double(g) / 255, blue: Double(b) / 255) } #if os(macOS) static let background = Color(NSColor.windowBackgroundColor) static let secondaryBackground = Color(NSColor.underPageBackgroundColor) static let tertiaryBackground = Color(NSColor.controlBackgroundColor) #endif #if os(iOS) static let background = Color(UIColor.systemBackground) static let secondaryBackground = Color(UIColor.secondarySystemBackground) static let tertiaryBackground = Color(UIColor.tertiarySystemBackground) #endif #if os(watchOS) static let background = Color.black #endif }